diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91ce3b0ed..c34cdbdc9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,19 @@
###Changelog
+####Version 0.10.0
+* Pebble: option to send sunrise and sunset events to timeline
+* Pebble: fix problems with unknown app keys while configuring watchfaces
+* Mi Band: BLE connection fixes
+* Fixes for enabling logging at whithout restarting Gadgetbridge
+* Re-enable device paring activity on Android 6 (BLE scanning needs the location preference)
+* Display device address in device info
+
####Version 0.9.8
* Pebble: fix more reconnnect issues
* Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health
* Pebble: option in AppManager to delete files from cache
* Pebble: enable pbw cache and watchface configuration for Firmware 2.x
* Pebble: allow enabling of Pebble Health without "untested features" being enabled
+* Pebble: fix music information being messed up
* Honour "Do Not Disturb" for phone calls and SMS
####Version 0.9.7
diff --git a/README.md b/README.md
index 9367cd440..6f888ce8c 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ need to create an account and transmit any of your data to the vendor's servers.
2. Start Gadgetbridge, tap on the device you want to connect to
3. To test, choose "Debug" from the menu and play around
-For more information read [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Getting-Started-(Pebble))
+For more information read [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble-Getting-Started)
## Features (Mi Band)
@@ -103,10 +103,14 @@ Contributions are welcome, be it feedback, bugreports, documentation, translatio
on any of the open [issues](https://github.com/Freeyourgadget/Gadgetbridge/issues?q=is%3Aopen+is%3Aissue);
just leave a comment that you're working on one to avoid duplicated work.
-Please do not use the issue tracker as a forum, do not ask for ETAs and read the issue conversation before posting.
+Translations can be contributed via https://www.transifex.com/projects/p/gadgetbridge/resource/strings/ or manually.
-Translations can be contributed via https://www.transifex.com/projects/p/gadgetbridge/resource/strings/ or
-manually.
+## Do you have further questions or feedback?
+
+Feel free to open an issue on our issue tracker, but please:
+- do not use the issue tracker as a forum, do not ask for ETAs and read the issue conversation before posting
+- use the search functionality to ensure that your questions wasn't already answered. Don't forget to check the **closed** issues as well!
+- remember that this is a community project, people are contributing in their free time because they like doing so: don't take the fun away! Be kind and constructive.
## Having problems?
diff --git a/app/build.gradle b/app/build.gradle
index c30ca034c..f6cd378ff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,6 +6,8 @@ def ABORT_ON_CHECK_FAILURE=false
tasks.withType(Test) { systemProperty 'MiFirmwareDir', System.getProperty('MiFirmwareDir', null) }
+// sourceSets.test.runtimeClasspath += File('src/main/assets')
+
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
@@ -16,8 +18,8 @@ android {
targetSdkVersion 23
// note: always bump BOTH versionCode and versionName!
- versionName "0.9.8"
- versionCode 52
+ versionName "0.10.0"
+ versionCode 53
}
buildTypes {
release {
@@ -42,6 +44,8 @@ android {
}
dependencies {
+// testCompile 'ch.qos.logback:logback-classic:1.1.3'
+// testCompile 'ch.qos.logback:logback-core:1.1.3'
testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:1.9.5"
@@ -54,6 +58,7 @@ dependencies {
compile 'com.github.PhilJay:MPAndroidChart:v2.2.4'
compile 'com.github.pfichtner:durationformatter:0.1.1'
compile 'de.cketti.library.changelog:ckchangelog:1.2.2'
+ compile 'net.e175.klaus:solarpositioning:0.0.9'
}
check.dependsOn 'findbugs', 'pmd', 'lint'
@@ -99,7 +104,6 @@ task pmd(type: Pmd) {
}
}
-
task findbugs(type: FindBugs) {
ignoreFailures = !ABORT_ON_CHECK_FAILURE
effort = "default"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 436e04230..46562de5b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
+
fileLogger;
private static Prefs prefs;
private static GBPrefs gbPrefs;
/**
@@ -72,6 +66,13 @@ public class GBApplication extends Application {
public static final String ACTION_QUIT
= "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit";
+ private static Logging logging = new Logging() {
+ @Override
+ protected String createLogDirectory() throws IOException {
+ File dir = FileUtils.getExternalFilesDir();
+ return dir.getAbsolutePath();
+ }
+ };
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -114,11 +115,6 @@ public class GBApplication extends Application {
}
setupExceptionHandler();
-// For debugging problems with the logback configuration
-// LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-// print logback's internal status
-// StatusPrinter.print(lc);
-// Logger logger = LoggerFactory.getLogger(GBApplication.class);
deviceService = createDeviceService();
GB.environment = GBEnvironment.createDeviceEnvironment();
@@ -138,6 +134,10 @@ public class GBApplication extends Application {
// db.close();
}
+ public static void setupLogging(boolean enabled) {
+ logging.setupLogging(enabled);
+ }
+
private void setupExceptionHandler() {
LoggingExceptionHandler handler = new LoggingExceptionHandler(Thread.getDefaultUncaughtExceptionHandler());
Thread.setDefaultUncaughtExceptionHandler(handler);
@@ -147,71 +147,6 @@ public class GBApplication extends Application {
return prefs.getBoolean("log_to_file", false);
}
- public static void setupLogging(boolean enable) {
- try {
- if (fileLogger == null) {
- File dir = FileUtils.getExternalFilesDir();
- // used by assets/logback.xml since the location cannot be statically determined
- System.setProperty("GB_LOGFILES_DIR", dir.getAbsolutePath());
- rememberFileLogger();
- }
- if (enable) {
- startFileLogger();
- } else {
- stopFileLogger();
- }
- getLogger().info("Gadgetbridge version: " + BuildConfig.VERSION_NAME);
- } catch (IOException ex) {
- Log.e("GBApplication", "External files dir not available, cannot log to file", ex);
- stopFileLogger();
- }
- }
-
- private static void startFileLogger() {
- if (fileLogger != null && !fileLogger.isStarted()) {
- addFileLogger(fileLogger);
- fileLogger.start();
- }
- }
-
- private static void stopFileLogger() {
- if (fileLogger != null && fileLogger.isStarted()) {
- fileLogger.stop();
- removeFileLogger(fileLogger);
- }
- }
-
- private static void rememberFileLogger() {
- ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
- fileLogger = root.getAppender("FILE");
- }
-
- private static void addFileLogger(Appender fileLogger) {
- try {
- ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
- if (!root.isAttached(fileLogger)) {
- root.addAppender(fileLogger);
- }
- } catch (Throwable ex) {
- Log.e("GBApplication", "Error adding logger FILE appender", ex);
- }
- }
-
- private static void removeFileLogger(Appender fileLogger) {
- try {
- ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
- if (root.isAttached(fileLogger)) {
- root.detachAppender(fileLogger);
- }
- } catch (Throwable ex) {
- Log.e("GBApplication", "Error removing logger FILE appender", ex);
- }
- }
-
- private static Logger getLogger() {
- return LoggerFactory.getLogger(GBApplication.class);
- }
-
public static Context getContext() {
return context;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java
new file mode 100644
index 000000000..7ea3c8e90
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java
@@ -0,0 +1,128 @@
+package nodomain.freeyourgadget.gadgetbridge;
+
+import android.util.Log;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.encoder.Encoder;
+import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
+import ch.qos.logback.core.util.StatusPrinter;
+
+public abstract class Logging {
+ public static final String PROP_LOGFILES_DIR = "GB_LOGFILES_DIR";
+
+ private FileAppender fileLogger;
+
+ public void setupLogging(boolean enable) {
+ try {
+ if (fileLogger == null) {
+ init();
+ }
+ if (enable) {
+ startFileLogger();
+ } else {
+ stopFileLogger();
+ }
+ getLogger().info("Gadgetbridge version: " + BuildConfig.VERSION_NAME);
+ } catch (IOException ex) {
+ Log.e("GBApplication", "External files dir not available, cannot log to file", ex);
+ stopFileLogger();
+ }
+ }
+
+ public void debugLoggingConfiguration() {
+ // For debugging problems with the logback configuration
+ LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+ // print logback's internal status
+ StatusPrinter.print(lc);
+// Logger logger = LoggerFactory.getLogger(Logging.class);
+ }
+
+ protected abstract String createLogDirectory() throws IOException;
+
+ protected void init() throws IOException {
+ String dir = createLogDirectory();
+ if (dir == null) {
+ throw new IllegalArgumentException("log directory must not be null");
+ }
+ // used by assets/logback.xml since the location cannot be statically determined
+ System.setProperty(PROP_LOGFILES_DIR, dir);
+ rememberFileLogger();
+ }
+
+ private Logger getLogger() {
+ return LoggerFactory.getLogger(Logging.class);
+ }
+
+ private void startFileLogger() {
+ if (fileLogger != null && !fileLogger.isStarted()) {
+ addFileLogger(fileLogger);
+ fileLogger.setLazy(false); // hack to make sure that start() actually opens the file
+ fileLogger.start();
+ }
+ }
+
+ private void stopFileLogger() {
+ if (fileLogger != null && fileLogger.isStarted()) {
+ fileLogger.stop();
+ removeFileLogger(fileLogger);
+ }
+ }
+
+ private void rememberFileLogger() {
+ ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ fileLogger = (FileAppender) root.getAppender("FILE");
+ }
+
+ private void addFileLogger(Appender fileLogger) {
+ try {
+ ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ if (!root.isAttached(fileLogger)) {
+ root.addAppender(fileLogger);
+ }
+ } catch (Throwable ex) {
+ Log.e("GBApplication", "Error adding logger FILE appender", ex);
+ }
+ }
+
+ private void removeFileLogger(Appender fileLogger) {
+ try {
+ ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ if (root.isAttached(fileLogger)) {
+ root.detachAppender(fileLogger);
+ }
+ } catch (Throwable ex) {
+ Log.e("GBApplication", "Error removing logger FILE appender", ex);
+ }
+ }
+
+ public FileAppender getFileLogger() {
+ return fileLogger;
+ }
+
+ public boolean setImmediateFlush(boolean enable) {
+ FileAppender fileLogger = getFileLogger();
+ Encoder encoder = fileLogger.getEncoder();
+ if (encoder instanceof LayoutWrappingEncoder) {
+ ((LayoutWrappingEncoder) encoder).setImmediateFlush(enable);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isImmediateFlush() {
+ FileAppender fileLogger = getFileLogger();
+ Encoder encoder = fileLogger.getEncoder();
+ if (encoder instanceof LayoutWrappingEncoder) {
+ return ((LayoutWrappingEncoder) encoder).isImmediateFlush();
+ }
+ return false;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java
index 702aa1346..37de8193f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java
@@ -353,11 +353,7 @@ public class ControlCenter extends GBActivity {
}
private void launchDiscoveryActivity() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- startActivity(new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS));
- } else {
- startActivity(new Intent(this, DiscoveryActivity.class));
- }
+ startActivity(new Intent(this, DiscoveryActivity.class));
}
@Override
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 d7d81f692..c832148f4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java
@@ -20,15 +20,20 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
+import net.e175.klaus.solarpositioning.DeltaT;
+import net.e175.klaus.solarpositioning.SPA;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
+import java.util.GregorianCalendar;
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.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
@@ -36,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class DebugActivity extends GBActivity {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
index f2ab531b6..3f00ced47 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java
@@ -1,5 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
+import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -8,10 +9,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
+import android.support.v4.app.ActivityCompat;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
@@ -48,6 +51,7 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
// continue with LE scan, if available
if (isScanning == Scanning.SCANNING_BT) {
+ checkAndRequestLocationPermission();
startDiscovery(Scanning.SCANNING_BTLE);
} else {
discoveryFinished();
@@ -320,6 +324,12 @@ public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemC
adapter.startDiscovery();
}
+ private void checkAndRequestLocationPermission() {
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 0);
+ }
+ }
+
private Message getPostMessage(Runnable runnable) {
Message m = Message.obtain(handler, runnable);
m.obj = runnable;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java
index 7dba008be..a5181dccc 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java
@@ -22,6 +22,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
+import java.util.Scanner;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -138,19 +139,36 @@ public class ExternalPebbleJSActivity extends GBActivity {
try {
JSONObject in = new JSONObject(msg);
JSONObject out = new JSONObject();
- String cur_key;
+ String inKey, outKey;
+ boolean passKey = false;
for (Iterator key = in.keys(); key.hasNext(); ) {
- cur_key = key.next();
- int pebbleAppIndex = knownKeys.optInt(cur_key);
+ passKey = false;
+ inKey = key.next();
+ outKey = null;
+ int pebbleAppIndex = knownKeys.optInt(inKey);
if (pebbleAppIndex != 0) {
- Object obj = in.get(cur_key);
+ passKey = true;
+ outKey = String.valueOf(pebbleAppIndex);
+
+ } else {
+ //do not discard integer keys (see https://developer.pebble.com/guides/communication/using-pebblekit-js/ )
+ Scanner scanner = new Scanner(inKey);
+ if (scanner.hasNextInt() && inKey.equals("" + scanner.nextInt())) {
+ passKey = true;
+ outKey = inKey;
+ }
+ }
+
+ if (passKey && outKey != null) {
+ Object obj = in.get(inKey);
if (obj instanceof Boolean) {
obj = ((Boolean) obj) ? "true" : "false";
}
- out.put(String.valueOf(pebbleAppIndex), obj);
+ out.put(outKey, obj);
} else {
- GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN);
+ GB.toast("Discarded key " + inKey + ", not found in the local configuration and is not an integer key.", Toast.LENGTH_SHORT, GB.WARN);
}
+
}
LOG.info(out.toString());
GBApplication.deviceService().onAppConfiguration(appUuid, out.toString());
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java
index 39c972ea1..dd9c2599a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java
@@ -1,14 +1,33 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
+import java.util.Locale;
+
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class GBActivity extends AppCompatActivity {
+ private void setLanguage(String language) {
+ Locale locale;
+ if (language.equals("default")) {
+ locale = Locale.getDefault();
+ } else {
+ locale = new Locale(language);
+ }
+ Configuration config = new Configuration();
+ config.locale = locale;
+
+ // FIXME: I have no idea what I am doing
+ getApplicationContext().getResources().updateConfiguration(config, getApplicationContext().getResources().getDisplayMetrics());
+ getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
if (GBApplication.isDarkThemeEnabled()) {
@@ -17,6 +36,9 @@ public class GBActivity extends AppCompatActivity {
setTheme(R.style.GadgetbridgeTheme);
}
+ Prefs prefs = GBApplication.getPrefs();
+ String language = prefs.getString("language", "default");
+ setLanguage(language);
super.onCreate(savedInstanceState);
}
}
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 c6479695e..41d6bc556 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
@@ -1,17 +1,28 @@
package nodomain.freeyourgadget.gadgetbridge.activities;
+import android.Manifest;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
import android.os.Bundle;
+import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.IOException;
import java.util.List;
+import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -19,13 +30,14 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActi
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_SLEEP_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH;
public class SettingsActivity extends AbstractSettingsActivity {
+ private static final Logger LOG = LoggerFactory.getLogger(SettingsActivity.class);
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -114,6 +126,34 @@ public class SettingsActivity extends AbstractSettingsActivity {
category.removePreference(pref);
}
+ pref = findPreference("location_aquire");
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ if (ActivityCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(SettingsActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 0);
+ }
+
+ LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ Criteria criteria = new Criteria();
+ String provider = locationManager.getBestProvider(criteria, false);
+ if (provider != null) {
+ Location location = locationManager.getLastKnownLocation(provider);
+ String latitude = String.format(Locale.US, "%.6g", location.getLatitude());
+ String longitude = String.format(Locale.US, "%.6g", location.getLongitude());
+ LOG.info("got location. Lat: " + latitude + " Lng: " + longitude);
+ EditTextPreference pref_latitude = (EditTextPreference) findPreference("location_latitude");
+ EditTextPreference pref_longitude = (EditTextPreference) findPreference("location_longitude");
+ pref_latitude.setText(latitude);
+ pref_longitude.setText(longitude);
+ pref_latitude.setSummary(latitude);
+ pref_longitude.setSummary(longitude);
+ } else {
+ LOG.warn("No location provider found, did you deny location permission?");
+ }
+ return true;
+ }
+ });
+
// Get all receivers of Media Buttons
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
@@ -146,6 +186,8 @@ public class SettingsActivity extends AbstractSettingsActivity {
"pebble_emu_addr",
"pebble_emu_port",
"pebble_reconnect_attempts",
+ "location_latitude",
+ "location_longitude",
"canned_reply_suffix",
"canned_reply_1",
"canned_reply_2",
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 a6dd21bf1..602ca5c85 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java
@@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -51,4 +52,8 @@ public interface EventHandler {
void onScreenshotReq();
void onEnableHeartRateSleepSupport(boolean enable);
+
+ void onAddCalendarEvent(CalendarEventSpec calendarEventSpec);
+
+ void onDeleteCalendarEvent(byte type, long id);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java
index 8379d75bd..ca8b9e64e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java
@@ -16,6 +16,7 @@ public final class MiBandConst {
public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer";
public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar";
public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection";
+ public static final String PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "mi_device_time_offset_hours";
public static final String ORIGIN_SMS = "sms";
@@ -27,6 +28,7 @@ public final class MiBandConst {
public static final String MI_1A = "1A";
public static final String MI_1S = "1S";
public static final String MI_AMAZFIT = "Amazfit";
+ public static final String MI_PRO = "2";
public static int getNotificationPrefIntValue(String pref, String origin, Prefs prefs, int defaultValue) {
String key = getNotificationPrefKey(pref, origin);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java
index c4e8e83c0..505d35303 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java
@@ -140,6 +140,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
return location;
}
+ public static int getDeviceTimeOffsetHours() throws IllegalArgumentException {
+ Prefs prefs = GBApplication.getPrefs();
+ return prefs.getInt(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS, 0);
+ }
+
public static boolean getHeartrateSleepSupport(String miBandAddress) throws IllegalArgumentException {
Prefs prefs = GBApplication.getPrefs();
return prefs.getBoolean(MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, false);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java
index 88a5e230c..e41cf9be8 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java
@@ -40,6 +40,10 @@ public class MiBandDateConverter {
value[offset + 4],
value[offset + 5]);
+ int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours();
+ if(offsetInHours != 0)
+ timestamp.add(Calendar.HOUR_OF_DAY,-offsetInHours);
+
return timestamp;
}
@@ -53,6 +57,18 @@ public class MiBandDateConverter {
* @return
*/
public static byte[] calendarToRawBytes(Calendar timestamp) {
+
+ // The mi-band device currently records sleep
+ // only if it happens after 10pm and before 7am.
+ // The offset is used to trick the device to record sleep
+ // in non-standard hours.
+ // If you usually sleep, say, from 6am to 2pm, set the
+ // shift to -8, so at 6am the device thinks it's still 10pm
+ // of the day before.
+ int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours();
+ if(offsetInHours != 0)
+ timestamp.add(Calendar.HOUR_OF_DAY,offsetInHours);
+
return new byte[]{
(byte) (timestamp.get(Calendar.YEAR) - 2000),
(byte) timestamp.get(Calendar.MONTH),
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java
index e49ae1abb..7fb29ce96 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java
@@ -16,6 +16,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.OR
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_PEBBLEMSG;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_SMS;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ADDRESS;
+import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DONT_ACK_TRANSFER;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL;
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR;
@@ -62,6 +63,7 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
PREF_MIBAND_ADDRESS,
PREF_MIBAND_FITNESS_GOAL,
PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR,
+ PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS,
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL),
getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_K9MAIL),
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java
new file mode 100644
index 000000000..e9470d31a
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java
@@ -0,0 +1,89 @@
+package nodomain.freeyourgadget.gadgetbridge.externalevents;
+
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import net.e175.klaus.solarpositioning.DeltaT;
+import net.e175.klaus.solarpositioning.SPA;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+public class AlarmReceiver extends BroadcastReceiver {
+ private static final Logger LOG = LoggerFactory.getLogger(AlarmReceiver.class);
+
+ public AlarmReceiver() {
+ Context context = GBApplication.getContext();
+ Intent intent = new Intent("DAILY_ALARM");
+ intent.setPackage(BuildConfig.APPLICATION_ID);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent("DAILY_ALARM"), 0);
+ AlarmManager am = (AlarmManager) (context.getSystemService(Context.ALARM_SERVICE));
+
+ am.setInexactRepeating(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 10000, AlarmManager.INTERVAL_DAY, pendingIntent);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!GBApplication.getPrefs().getBoolean("send_sunrise_sunset", false)) {
+ LOG.info("won't send sunrise and sunset events (disabled in preferences)");
+ return;
+ }
+ LOG.info("will resend sunrise and sunset events");
+
+ final GregorianCalendar dateTimeTomorrow = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ dateTimeTomorrow.set(Calendar.HOUR, 0);
+ dateTimeTomorrow.set(Calendar.MINUTE, 0);
+ dateTimeTomorrow.set(Calendar.SECOND, 0);
+ dateTimeTomorrow.set(Calendar.MILLISECOND, 0);
+ dateTimeTomorrow.add(GregorianCalendar.DAY_OF_MONTH, 1);
+
+ /*
+ * rotate ids ud reuse the id from two days ago for tomorrow, this way we will have
+ * sunrise /sunset for 3 days while sending only sunrise/sunset per day
+ */
+ byte id_tomorrow = (byte) ((dateTimeTomorrow.getTimeInMillis() / (1000L * 60L * 60L * 24L)) % 3);
+
+ GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_SUNRISE, id_tomorrow);
+ GBApplication.deviceService().onDeleteCalendarEvent(CalendarEventSpec.TYPE_SUNSET, id_tomorrow);
+
+ Prefs prefs = GBApplication.getPrefs();
+
+ float latitude = prefs.getFloat("location_latitude", 0);
+ float longitude = prefs.getFloat("location_longitude", 0);
+ LOG.info("got longitude/latitude from preferences: " + latitude + "/" + longitude);
+ GregorianCalendar[] sunriseTransitSetTomorrow = SPA.calculateSunriseTransitSet(dateTimeTomorrow, latitude, longitude, DeltaT.estimate(dateTimeTomorrow));
+
+ CalendarEventSpec calendarEventSpec = new CalendarEventSpec();
+ calendarEventSpec.durationInSeconds = 0;
+ calendarEventSpec.description = null;
+
+ calendarEventSpec.type = CalendarEventSpec.TYPE_SUNRISE;
+ calendarEventSpec.title = "Sunrise";
+ if (sunriseTransitSetTomorrow[0] != null) {
+ calendarEventSpec.id = id_tomorrow;
+ calendarEventSpec.timestamp = (int) (sunriseTransitSetTomorrow[0].getTimeInMillis() / 1000);
+ GBApplication.deviceService().onAddCalendarEvent(calendarEventSpec);
+ }
+
+ calendarEventSpec.type = CalendarEventSpec.TYPE_SUNSET;
+ calendarEventSpec.title = "Sunset";
+ if (sunriseTransitSetTomorrow[2] != null) {
+ calendarEventSpec.id = id_tomorrow;
+ calendarEventSpec.timestamp = (int) (sunriseTransitSetTomorrow[2].getTimeInMillis() / 1000);
+ GBApplication.deviceService().onAddCalendarEvent(calendarEventSpec);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java
index 5282e7a59..1fa0364ff 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java
@@ -14,7 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
public class BluetoothConnectReceiver extends BroadcastReceiver {
- private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
+ private static final Logger LOG = LoggerFactory.getLogger(BluetoothConnectReceiver.class);
final DeviceCommunicationService service;
@@ -32,7 +32,7 @@ public class BluetoothConnectReceiver extends BroadcastReceiver {
GBDevice gbDevice = service.getGBDevice();
if (gbDevice != null) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- if (device.getAddress().equals(gbDevice.getAddress())) {
+ if (device.getAddress().equals(gbDevice.getAddress()) && gbDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) {
LOG.info("will connect to " + gbDevice.getName());
GBApplication.deviceService().connect();
} else {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java
index d910e75eb..67c7e8e70 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java
@@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -18,20 +19,13 @@ public class MusicPlaybackReceiver extends BroadcastReceiver {
String artist = intent.getStringExtra("artist");
String album = intent.getStringExtra("album");
String track = intent.getStringExtra("track");
- /*
- Bundle bundle = intent.getExtras();
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
- LOG.info(String.format("%s %s (%s)", key,
- value != null ? value.toString() : "null", value != null ? value.getClass().getName() : "no class"));
- }
- */
+
LOG.info("Current track: " + artist + ", " + album + ", " + track);
MusicSpec musicSpec = new MusicSpec();
musicSpec.artist = artist;
- musicSpec.artist = album;
- musicSpec.artist = track;
+ musicSpec.album = album;
+ musicSpec.track = track;
GBApplication.deviceService().onSetMusicInfo(musicSpec);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java
index 1e46d9782..252755342 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java
@@ -42,6 +42,7 @@ public class GBDevice implements Parcelable {
public static final String EXTRA_DEVICE = "device";
private static final String DEVINFO_HW_VER = "HW: ";
private static final String DEVINFO_FW_VER = "FW: ";
+ private static final String DEVINFO_ADDR = "ADDR: ";
private final String mName;
private final String mAddress;
private final DeviceType mDeviceType;
@@ -346,6 +347,9 @@ public class GBDevice implements Parcelable {
if (mFirmwareVersion != null) {
result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion));
}
+ if (mAddress != null) {
+ result.add(new GenericItem(DEVINFO_ADDR, mAddress));
+ }
Collections.sort(result);
return result;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java
index 61af3fcb8..2e598eacf 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java
@@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
@@ -223,4 +224,24 @@ public class GBDeviceService implements DeviceService {
.putExtra(EXTRA_BOOLEAN_ENABLE, enable);
invokeService(intent);
}
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ Intent intent = createIntent().setAction(ACTION_ADD_CALENDAREVENT)
+ .putExtra(EXTRA_CALENDAREVENT_ID, calendarEventSpec.id)
+ .putExtra(EXTRA_CALENDAREVENT_TYPE, calendarEventSpec.type)
+ .putExtra(EXTRA_CALENDAREVENT_TIMESTAMP, calendarEventSpec.timestamp)
+ .putExtra(EXTRA_CALENDAREVENT_DURATION, calendarEventSpec.durationInSeconds)
+ .putExtra(EXTRA_CALENDAREVENT_TITLE, calendarEventSpec.title)
+ .putExtra(EXTRA_CALENDAREVENT_DESCRIPTION, calendarEventSpec.description);
+ invokeService(intent);
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ Intent intent = createIntent().setAction(ACTION_DELETE_CALENDAREVENT)
+ .putExtra(EXTRA_CALENDAREVENT_TYPE, type)
+ .putExtra(EXTRA_CALENDAREVENT_ID, id);
+ invokeService(intent);
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java
new file mode 100644
index 000000000..438123e49
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java
@@ -0,0 +1,14 @@
+package nodomain.freeyourgadget.gadgetbridge.model;
+
+public class CalendarEventSpec {
+ public static final byte TYPE_UNKNOWN = 0;
+ public static final byte TYPE_SUNRISE = 1;
+ public static final byte TYPE_SUNSET = 2;
+
+ public byte type;
+ public long id;
+ public int timestamp;
+ public int durationInSeconds;
+ public String title;
+ public String description;
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java
index 2cf979cda..aaf02798b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java
@@ -36,6 +36,8 @@ public interface DeviceService extends EventHandler {
String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement";
String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support";
String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement";
+ String ACTION_ADD_CALENDAREVENT = PREFIX + ".action.add_calendarevent";
+ String ACTION_DELETE_CALENDAREVENT = PREFIX + ".action.delete_calendarevent";
String EXTRA_DEVICE_ADDRESS = "device_address";
String EXTRA_NOTIFICATION_BODY = "notification_body";
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
@@ -65,6 +67,12 @@ public interface DeviceService extends EventHandler {
String EXTRA_REALTIME_STEPS = "realtime_steps";
String EXTRA_TIMESTAMP = "timestamp";
String EXTRA_HEART_RATE_VALUE = "hr_value";
+ String EXTRA_CALENDAREVENT_ID = "calendarevent_id";
+ String EXTRA_CALENDAREVENT_TYPE = "calendarevent_type";
+ String EXTRA_CALENDAREVENT_TIMESTAMP = "calendarevent_timestamp";
+ String EXTRA_CALENDAREVENT_DURATION = "calendarevent_duration";
+ String EXTRA_CALENDAREVENT_TITLE = "calendarevent_title";
+ String EXTRA_CALENDAREVENT_DESCRIPTION = "calendarevent_description";
void start();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
index 37d217f8a..fdafa5265 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java
@@ -25,6 +25,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.K9Receiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver;
@@ -34,6 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.SMSReceiver;
import nodomain.freeyourgadget.gadgetbridge.externalevents.TimeChangeReceiver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -43,10 +45,12 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ADD_CALENDAREVENT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETE_CALENDAREVENT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT;
@@ -70,6 +74,12 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOOLEAN_ENABLE;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_DESCRIPTION;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_DURATION;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_ID;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TIMESTAMP;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TITLE;
+import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TYPE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS;
@@ -108,6 +118,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
private MusicPlaybackReceiver mMusicPlaybackReceiver = null;
private TimeChangeReceiver mTimeChangeReceiver = null;
private BluetoothConnectReceiver mBlueToothConnectReceiver = null;
+ private AlarmReceiver mAlarmReceiver = null;
private Random mRandom = new Random();
@@ -268,6 +279,23 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mDeviceSupport.onNotification(notificationSpec);
break;
}
+ case ACTION_ADD_CALENDAREVENT: {
+ CalendarEventSpec calendarEventSpec = new CalendarEventSpec();
+ calendarEventSpec.id = intent.getLongExtra(EXTRA_CALENDAREVENT_ID, -1);
+ calendarEventSpec.type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1);
+ calendarEventSpec.timestamp = intent.getIntExtra(EXTRA_CALENDAREVENT_TIMESTAMP, -1);
+ calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1);
+ calendarEventSpec.title = intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE);
+ calendarEventSpec.description = intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION);
+ mDeviceSupport.onAddCalendarEvent(calendarEventSpec);
+ break;
+ }
+ case ACTION_DELETE_CALENDAREVENT: {
+ long id = intent.getLongExtra(EXTRA_CALENDAREVENT_ID, -1);
+ byte type = intent.getByteExtra(EXTRA_CALENDAREVENT_TYPE, (byte) -1);
+ mDeviceSupport.onDeleteCalendarEvent(type, id);
+ break;
+ }
case ACTION_REBOOT: {
mDeviceSupport.onReboot();
break;
@@ -469,6 +497,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
mBlueToothConnectReceiver = new BluetoothConnectReceiver(this);
registerReceiver(mBlueToothConnectReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
}
+ if (mAlarmReceiver == null) {
+ mAlarmReceiver = new AlarmReceiver();
+ registerReceiver(mAlarmReceiver, new IntentFilter("DAILY_ALARM"));
+ }
} else {
if (mPhoneCallReceiver != null) {
unregisterReceiver(mPhoneCallReceiver);
@@ -498,6 +530,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
unregisterReceiver(mBlueToothConnectReceiver);
mBlueToothConnectReceiver = null;
}
+ if (mAlarmReceiver != null) {
+ unregisterReceiver(mAlarmReceiver);
+ mAlarmReceiver = null;
+ }
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java
index 8047aad60..019722b54 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java
@@ -13,6 +13,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -268,4 +269,20 @@ public class ServiceDeviceSupport implements DeviceSupport {
}
delegate.onEnableRealtimeHeartRateMeasurement(enable);
}
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ if (checkBusy("add calendar event")) {
+ return;
+ }
+ delegate.onAddCalendarEvent(calendarEventSpec);
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ if (checkBusy("delete calendar event")) {
+ return;
+ }
+ delegate.onDeleteCalendarEvent(type, id);
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java
index 04179458c..261181128 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java
@@ -147,7 +147,6 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
}
private void gattServicesDiscovered(List discoveredGattServices) {
-
if (discoveredGattServices == null) {
return;
}
@@ -180,7 +179,7 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
@Override
public void onServicesDiscovered(BluetoothGatt gatt) {
- gattServicesDiscovered(getQueue().getSupportedGattServices());
+ gattServicesDiscovered(gatt.getServices());
initializeDevice(createTransactionBuilder("Initializing device")).queue(getQueue());
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
index f60094356..fc7358a81 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java
@@ -156,11 +156,9 @@ public final class BtLEQueue {
LOG.info("Attempting to connect to " + mGbDevice.getName());
mBluetoothAdapter.cancelDiscovery();
BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress());
-// boolean result;
synchronized (mGattMonitor) {
// connectGatt with true doesn't really work ;( too often connection problems
mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback);
-// result = mBluetoothGatt.connect();
}
boolean result = mBluetoothGatt != null;
if (result) {
@@ -223,7 +221,11 @@ public final class BtLEQueue {
private boolean maybeReconnect() {
if (mAutoReconnect && mBluetoothGatt != null) {
LOG.info("Enabling automatic ble reconnect...");
- return mBluetoothGatt.connect();
+ boolean result = mBluetoothGatt.connect();
+ if (result) {
+ setDeviceConnectionState(State.WAITING_FOR_RECONNECT);
+ }
+ return result;
}
return false;
}
@@ -308,6 +310,12 @@ public final class BtLEQueue {
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
LOG.debug("connection state change, newState: " + newState + getStatusString(status));
+ synchronized (mGattMonitor) {
+ if (mBluetoothGatt == null) {
+ mBluetoothGatt = gatt;
+ }
+ }
+
if (!checkCorrectGattInstance(gatt, "connection state event")) {
return;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java
index 3ba149df6..c1c36258b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java
@@ -83,7 +83,7 @@ public class DeviceInfo extends AbstractInfo {
}
public boolean supportsHeartrate() {
- return isMili1S() || (test1AHRMode && isMili1A());
+ return isMiliPro() || isMili1S() || (test1AHRMode && isMili1A());
}
@Override
@@ -116,6 +116,10 @@ public class DeviceInfo extends AbstractInfo {
return hwVersion == 6;
}
+ public boolean isMiliPro() {
+ return hwVersion == 8 || (feature == 8 && appearance == 0);
+ }
+
public String getHwVersion() {
if (isMili1()) {
return MiBandConst.MI_1;
@@ -129,6 +133,9 @@ public class DeviceInfo extends AbstractInfo {
if (isAmazFit()) {
return MiBandConst.MI_AMAZFIT;
}
+ if (isMiliPro()) {
+ return MiBandConst.MI_PRO;
+ }
return "?";
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java
index 2d97818d7..fd00d02ac 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java
@@ -32,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
@@ -360,10 +361,18 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
LOG.info("Attempting to set wear location...");
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT);
if (characteristic != null) {
- int location = MiBandCoordinator.getWearLocation(getDevice().getAddress());
- transaction.write(characteristic, new byte[]{
- MiBandService.COMMAND_SET_WEAR_LOCATION,
- (byte) location
+ transaction.add(new ConditionalWriteAction(characteristic) {
+ @Override
+ protected byte[] checkCondition() {
+ if (getDeviceInfo() != null && getDeviceInfo().isAmazFit()) {
+ return null;
+ }
+ int location = MiBandCoordinator.getWearLocation(getDevice().getAddress());
+ return new byte[]{
+ MiBandService.COMMAND_SET_WEAR_LOCATION,
+ (byte) location
+ };
+ }
});
} else {
LOG.info("Unable to set Wear Location");
@@ -382,6 +391,16 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
}
}
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ // not supported
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ // not supported
+ }
+
/**
* Part of device initialization process. Do not call manually.
*
@@ -834,6 +853,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
private void handleHeartrate(byte[] value) {
if (value.length == 2 && value[0] == 6) {
int hrValue = (value[1] & 0xff);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("heart rate: " + hrValue);
+ }
Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT)
.putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue)
.putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis());
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java
index 26d55f730..710178f9f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java
@@ -17,6 +17,10 @@ public class AppMessageHandler {
mPebbleProtocol = pebbleProtocol;
}
+ public boolean isEnabled() {
+ return true;
+ }
+
public UUID getUUID() {
return mUUID;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java
index daa0b020c..1a7e4b97c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java
@@ -20,6 +20,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MisfitSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class AppMessageHandlerMisfit extends AppMessageHandler {
@@ -41,6 +42,12 @@ public class AppMessageHandlerMisfit extends AppMessageHandler {
private final MisfitSampleProvider sampleProvider = new MisfitSampleProvider();
+ @Override
+ public boolean isEnabled() {
+ Prefs prefs = GBApplication.getPrefs();
+ return prefs.getBoolean("pebble_sync_misfit", true);
+ }
+
@Override
public GBDeviceEvent[] handleMessage(ArrayList> pairs) {
for (Pair pair : pairs) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java
index 3c6517d6c..62f4d39a1 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java
@@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSleepMonitorResult;
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.MorpheuzSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class AppMessageHandlerMorpheuz extends AppMessageHandler {
@@ -56,6 +57,12 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler {
return mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs);
}
+ @Override
+ public boolean isEnabled() {
+ Prefs prefs = GBApplication.getPrefs();
+ return prefs.getBoolean("pebble_sync_morpheuz", true);
+ }
+
@Override
public GBDeviceEvent[] handleMessage(ArrayList> pairs) {
int ctrl_message = 0;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java
index b7d425448..35d84be23 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java
@@ -13,7 +13,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-class DatalogSessionHealthOverlayData extends DatalogSession {
+class DatalogSessionHealthOverlayData extends DatalogSessionPebbleHealth {
private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthOverlayData.class);
@@ -26,6 +26,10 @@ class DatalogSessionHealthOverlayData extends DatalogSession {
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
+ if (!isPebbleHealthEnabled()) {
+ return false;
+ }
+
int initialPosition = datalogMessage.position();
int beginOfRecordPosition;
short recordVersion; //probably
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java
index 2162f9d50..455e64592 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java
@@ -13,7 +13,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-class DatalogSessionHealthSleep extends DatalogSession {
+class DatalogSessionHealthSleep extends DatalogSessionPebbleHealth {
private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSleep.class);
@@ -25,6 +25,11 @@ class DatalogSessionHealthSleep extends DatalogSession {
@Override
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
+
+ if (!isPebbleHealthEnabled()) {
+ return false;
+ }
+
int initialPosition = datalogMessage.position();
int beginOfRecordPosition;
short recordVersion; //probably
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java
index f89f03639..06151d7a4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java
@@ -16,7 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-public class DatalogSessionHealthSteps extends DatalogSession {
+public class DatalogSessionHealthSteps extends DatalogSessionPebbleHealth {
private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSteps.class);
@@ -29,6 +29,10 @@ public class DatalogSessionHealthSteps extends DatalogSession {
public boolean handleMessage(ByteBuffer datalogMessage, int length) {
LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length));
+ if (!isPebbleHealthEnabled()) {
+ return false;
+ }
+
int timestamp;
byte recordLength, recordNum;
short recordVersion; //probably
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java
new file mode 100644
index 000000000..6df7a7514
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java
@@ -0,0 +1,18 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble;
+
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+abstract class DatalogSessionPebbleHealth extends DatalogSession {
+
+ DatalogSessionPebbleHealth(byte id, UUID uuid, int tag, byte itemType, short itemSize) {
+ super(id, uuid, tag, itemType, itemSize);
+ }
+
+ protected boolean isPebbleHealthEnabled() {
+ Prefs prefs = GBApplication.getPrefs();
+ return prefs.getBoolean("pebble_sync_health", true);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
index a14fb4965..fcc196412 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java
@@ -161,6 +161,8 @@ public class PebbleIoThread extends GBDeviceIoThread {
@Override
protected boolean connect(String btDeviceAddress) {
GBDevice.State originalState = gbDevice.getState();
+ gbDevice.setState(GBDevice.State.CONNECTING);
+ gbDevice.sendDeviceUpdateIntent(getContext());
try {
// contains only one ":"? then it is addr:port
int firstColon = btDeviceAddress.indexOf(":");
@@ -188,6 +190,8 @@ public class PebbleIoThread extends GBDeviceIoThread {
} catch (IOException e) {
e.printStackTrace();
gbDevice.setState(originalState);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+
mInStream = null;
mOutStream = null;
mBtSocket = null;
@@ -197,12 +201,8 @@ public class PebbleIoThread extends GBDeviceIoThread {
mPebbleProtocol.setForceProtocol(prefs.getBoolean("pebble_force_protocol", false));
mIsConnected = true;
- if (originalState == GBDevice.State.WAITING_FOR_RECONNECT) {
- gbDevice.setState(GBDevice.State.INITIALIZED);
- } else {
- gbDevice.setState(GBDevice.State.CONNECTED);
- write(mPebbleProtocol.encodeFirmwareVersionReq());
- }
+ write(mPebbleProtocol.encodeFirmwareVersionReq());
+ gbDevice.setState(GBDevice.State.CONNECTED);
gbDevice.sendDeviceUpdateIntent(getContext());
return true;
@@ -210,15 +210,18 @@ public class PebbleIoThread extends GBDeviceIoThread {
@Override
public void run() {
- gbDevice.setState(GBDevice.State.CONNECTING);
- gbDevice.sendDeviceUpdateIntent(getContext());
-
mIsConnected = connect(gbDevice.getAddress());
- enablePebbleKitReceiver(mIsConnected);
- mQuit = !mIsConnected; // quit if not connected
+ if (!mIsConnected) {
+ if (GBApplication.getGBPrefs().getAutoReconnect()) {
+ gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+ }
+ return;
+ }
byte[] buffer = new byte[8192];
-
+ enablePebbleKitReceiver(true);
+ mQuit = false;
while (!mQuit) {
try {
if (mIsInstalling) {
@@ -361,8 +364,9 @@ public class PebbleIoThread extends GBDeviceIoThread {
mIsConnected = false;
int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10);
if (!mQuit && GBApplication.getGBPrefs().getAutoReconnect() && reconnectAttempts > 0) {
- gbDevice.setState(GBDevice.State.CONNECTING);
+ gbDevice.setState(GBDevice.State.WAITING_FOR_RECONNECT);
gbDevice.sendDeviceUpdateIntent(getContext());
+
int delaySeconds = 1;
while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) {
LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")");
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
index 52adb0450..d53de9b1d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java
@@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
@@ -240,6 +241,8 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final byte LENGTH_UUID = 16;
+ static final long GB_UUID_MASK = 0x4767744272646700L;
+
// base is -5
private static final String[] hwRevisions = {
// Emulator
@@ -453,7 +456,6 @@ public class PebbleProtocol extends GBDeviceProtocol {
if (isFw3x) {
// 3.x notification
- //return encodeTimelinePin(id, (int) ((ts + 600) & 0xffffffffL), (short) 90, PebbleIconID.TIMELINE_CALENDAR, title); // really, this is just for testing
return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.type, notificationSpec.cannedReplies);
} else if (mForceProtocol || notificationSpec.type != NotificationType.EMAIL) {
// 2.x notification
@@ -466,6 +468,29 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
}
+ @Override
+ public byte[] encodeAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ long id = calendarEventSpec.id != -1 ? calendarEventSpec.id : mRandom.nextLong();
+ int iconId;
+ switch (calendarEventSpec.type) {
+ case CalendarEventSpec.TYPE_SUNRISE:
+ iconId = PebbleIconID.SUNRISE;
+ break;
+ case CalendarEventSpec.TYPE_SUNSET:
+ iconId = PebbleIconID.SUNSET;
+ break;
+ default:
+ iconId = PebbleIconID.TIMELINE_CALENDAR;
+ }
+
+ return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) calendarEventSpec.durationInSeconds, iconId, calendarEventSpec.title, calendarEventSpec.description);
+ }
+
+ @Override
+ public byte[] encodeDeleteCalendarEvent(byte type, long id) {
+ return encodeBlobdb(new UUID(GB_UUID_MASK | type, id), BLOBDB_DELETE, BLOBDB_PIN, null);
+ }
+
@Override
public byte[] encodeSetTime() {
long ts = System.currentTimeMillis();
@@ -744,11 +769,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
return buf.array();
}
- private byte[] encodeTimelinePin(int id, int timestamp, short duration, int icon_id, String title, String subtitle) {
+ private byte[] encodeTimelinePin(UUID uuid, int timestamp, short duration, int icon_id, String title, String subtitle) {
final short TIMELINE_PIN_LENGTH = 46;
icon_id |= 0x80000000;
- UUID uuid = new UUID(mRandom.nextLong(), ((long) mRandom.nextInt() << 32) | id);
byte attributes_count = 2;
byte actions_count = 0;
@@ -1236,10 +1260,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.put(PHONEVERSION_APPVERSION_MAGIC);
buf.put((byte) 3); // major
- buf.put((byte) 8); // minor
- buf.put((byte) 1); // patch
+ buf.put((byte) 12); // minor
+ buf.put((byte) 0); // patch
buf.order(ByteOrder.LITTLE_ENDIAN);
- buf.putLong(0x00000000000000af); //flags
+ buf.putLong(0x00000000000001af); //flags
return buf.array();
}
@@ -1804,7 +1828,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
LOG.info(ENDPOINT_NAME + ": (cmd:" + command + ")" + uuid);
break;
}
- return null;
+ return new GBDeviceEvent[]{null};
}
private GBDeviceEvent decodeBlobDb(ByteBuffer buf) {
@@ -2094,8 +2118,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
AppMessageHandler handler = mAppMessageHandlers.get(uuid);
if (handler != null) {
- ArrayList> dict = decodeDict(buf);
- devEvts = handler.handleMessage(dict);
+ if (handler.isEnabled()) {
+ ArrayList> dict = decodeDict(buf);
+ devEvts = handler.handleMessage(dict);
+ } else {
+ devEvts = new GBDeviceEvent[]{null};
+ }
} else {
try {
devEvts = decodeDictToJSONAppMessage(uuid, buf);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java
index e396d03f9..b0bc3b47d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java
@@ -12,6 +12,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -119,4 +120,18 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
public void onSetAlarms(ArrayList extends Alarm> alarms) {
//nothing to do ATM
}
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ if (reconnect()) {
+ super.onAddCalendarEvent(calendarEventSpec);
+ }
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ if (reconnect()) {
+ super.onDeleteCalendarEvent(type, id);
+ }
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java
index 5dfa8b2a5..78e15667d 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java
@@ -8,6 +8,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -187,4 +188,16 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeHeartRateMeasurement(enable);
sendToDevice(bytes);
}
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ byte[] bytes = gbDeviceProtocol.encodeAddCalendarEvent(calendarEventSpec);
+ sendToDevice(bytes);
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+ byte[] bytes = gbDeviceProtocol.encodeDeleteCalendarEvent(type, id);
+ sendToDevice(bytes);
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java
index bdf1b1014..a226c0947 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java
@@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.serial;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
public abstract class GBDeviceProtocol {
@@ -65,6 +66,14 @@ public abstract class GBDeviceProtocol {
public byte[] encodeEnableRealtimeHeartRateMeasurement(boolean enable) { return null; }
+ public byte[] encodeAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+ return null;
+ }
+
+ public byte[] encodeDeleteCalendarEvent(byte type, long id) {
+ return null;
+ }
+
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
return null;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java
index b354edb74..470ef3929 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java
@@ -182,4 +182,31 @@ public class FileUtils {
}
return out.toByteArray();
}
+
+ public static boolean deleteRecursively(File dir) {
+ if (!dir.exists()) {
+ return true;
+ }
+ if (dir.isFile()) {
+ return dir.delete();
+ }
+ for (File sub : dir.listFiles()) {
+ if (!deleteRecursively(sub)) {
+ return false;
+ }
+ }
+ return dir.delete();
+ }
+
+ public static File createTempDir(String prefix) throws IOException {
+ File parent = new File(System.getProperty("java.io.tmpdir", "/tmp"));
+ for (int i = 1; i < 100; i++) {
+ String name = prefix + (int) (Math.random() * 100000);
+ File dir = new File(parent, name);
+ if (dir.mkdirs()) {
+ return dir;
+ }
+ }
+ throw new IOException("Cannot create temporary directory in " + parent);
+ }
}
\ 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 1588000a9..9d337f732 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -28,6 +28,7 @@
Einstellungen
Allgemeine Einstellungen
Verbinde, wenn Bluetooth eingeschaltet wird
+ Verbindungen automatisch wiederherstellen
Bevorzugter Audioplayer
Standard
Datum und Zeit
@@ -59,6 +60,12 @@
Bevorzugter Aktivitätstracker
Erlaube Zugriff von anderen Android Apps
Experimentelle Unterstützung für Android Apps, die PebbleKit benutzen
+ Sonnenauf- und -untergang
+ Sende Sonnenauf- und -untergangszeiten abhänging vom Standort auf die Pebble Timeline
+ Standort
+ Standort Bestimmen
+ Breitengrad
+ Längengrad
Benachrichtigungsprotokoll erzwingen
Diese Option erzwingt das neuste Benachrichtigungsprotokoll abhängig von der Firmwareversion. NUR EINSCHALTEN, WENN DU WEISST WAS DU TUST!
Ungetestete Features freischalten
@@ -231,5 +238,4 @@
Firmware wurde nicht gesendet
Herzfrequenz
Herzfrequenz
- Verbindungen automatisch wiederherstellen
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 3f1e31357..6ca7ee3b6 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -14,6 +14,7 @@
Gestione app
Cancella
+ Cancella e rimuovi dalla cache
Blocco notifiche
@@ -27,11 +28,15 @@
Impostazioni
Impostazioni globali
Collegati al dispositivo quando il bluetooth viene acceso
+ Riconessione automatica
Applicazione musicale preferita
Default
Data e ora
Sincronizza l\'ora
Sincronizza l\'orario al collegamento oppure quando viene cambiata l\'ora / il fuso orario in android.
+ Tema
+ Chiaro
+ Scuro
Notifiche
Ripetizioni
Chiamate telefoniche
@@ -41,6 +46,8 @@
Supporto per applicazioni che inviano le notifiche a Pebble usando Intents. Può essere usato per Conversations.
Notifiche generiche
… anche se lo schermo è acceso
+ Non disturbare
+ Non inviare notifiche nei periodi configurati come \"non disturbare\"
sempre
se lo schermo è spento
mai
@@ -53,6 +60,12 @@
Tracker delle attività preferito
Consenti accesso ad altre applicazioni
Attiva l\'accesso sperimentale ad applicazioni Android che usano PebbleKit
+ Alba e tramonto
+ Mostra gli orari calcolati per l\'alba e il tramonto sulla timeline
+ Posizione
+ Acquisisci posizione
+ Latitudin
+ Longitudin
Forza protocollo delle notifiche
Questa opzione forza l\'utilizzo della versione più recente delle notifiche in dipendenza del firmware del tuo dispositivo. ABILITALO SOLO SE SAI COSA STAI FACENDO!
Abilita funzionalità non testate
@@ -70,6 +83,9 @@
Notifica di prova creata da Gadgetbridge
Bluetooth non supportato.
Bluetooth disabilitato.
+ tocca il dispositivo connesso per gestire le App
+ tocca il dispositivo connesso per visualizzare l\'attività
+ tocca il dispositivo a cui connettersi
Impossibile connettersi. Indirizzo BT non valido?
Gadgetbridge in esecuzione
installazione del binario %1$d/%2$d
@@ -100,11 +116,13 @@
Dati dell\'utente non inseriti, vengono usati dati d\'esempio.
Quando la Mi Band vibra e lampeggia, dalle qualche leggero colpetto.
Installa
+ Imposta il tuo dispositivo perchè sia rilevabile. I dispositivi attualmente connessi non saranno probabilmente rilevati. Se non vedi il tuo dispositivo entro un paio di minuti, riprova dopo avere riavviato il dispositivo Android.
Nota:
Immagine dispositivo
Nome / Soprannome
Numero vibrazioni
Monitoraggio del sonno
+ Salva il log su file
inizializzazione in corso
Recupero dati attività
Da %1$s a %2$s
@@ -178,6 +196,8 @@
Non confermare il trasferimento dati
Se il trasferimento non viene confermato, i dati rimangono memorizzati sulla Mi Band. Utile se GB è usato insieme ad altre app.
Conserva i dati delle attività sulla Mi Band anche dopo averli sincronizzati. Utile se GB è usato insieme ad altre app.
+ Utilizza la modalità a bassa latenza per gli aggiornamenti del firmware
+ Può essere utile quando l\'aggiornamento del firmware fallisce
Storico dei passi
Passi/minuto
Passi totali
@@ -193,6 +213,7 @@
Firmware non compatibile
Questo firmware non è compatibile con il dispositivo
Sveglie da riservare per i prossimi eventi del calendario
+ Utilizza il sensore del battito cardiaco per migliorare il riconoscimento del sonno
in attesa di riconessione
Re-installazion
Informazioni sull\'utilizzatore
@@ -211,4 +232,11 @@
Impostata sveglia per %1$02d:%2$02d
HW: %1$s
FW: %1$s
+ Errore durante la creazione della directory per i file di log: %1$s
+ HR:
+ Aggiornamento firmware in corso
+ Firmware non inviato
+ Battito cardiaco
+ Battito cardiaco
+ Offset orologio del dispositivo in ore (per l\'identificazione del sonno dei lavoratori a turni)
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index f40138792..ccdfc41a4 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -28,6 +28,7 @@
設定
一般設定
Bluetoothがオンになったときにデバイスに接続
+ 自動的に再接続
お好みのオーディオプレイヤー
デフォルト
日付と時刻
@@ -59,6 +60,12 @@
お好みのアクティビティ トラッカー
第三者のアンドロイドアップにアクセス権利を与える
PebbleKitを使用してAndroidアプリ用の実験的なサポートを有効にします
+ 日の出と日の入り
+ 場所に基づいて、Pebble のタイムラインに日の出・日の入りの時間を送ります
+ 場所
+ 場所の取得
+ 緯度
+ 経度
通知プロトコルを強制する
このオプションを指定すると、ファームウェアのバージョンに応じて強制的に最新の通知プロトコルを使用します。何をしているかわかっている場合のみ有効にしてください!
未テストの機能を有効にする
@@ -231,5 +238,4 @@
ファームウェアを送信しませんでした
心拍数
心拍数
- 自動的に再接続
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 2afb4a029..b37351454 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -27,6 +27,7 @@
설정
일반 설정
블루투스가 켜지면 기기에 접속하기
+ 자동으로 재연결
선호하는 오디오 플레이어
기본값
날짜와 시간
@@ -224,5 +225,4 @@
펌웨어가 전송되지 않음
심박수
심박수
- 자동으로 재연결
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 937c29f23..8cf1b2874 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -11,6 +11,36 @@
light
dark
+
+ - System Default
+ - Deutsch
+ - English
+ - Español
+ - Français
+ - Polski
+ - Русский
+ - Tiếng Việt
+ - Türkçe
+ - Українська
+ - 한국어
+ - 日本語
+
+
+
+ - default
+ - de
+ - en
+ - es
+ - fr
+ - pl
+ - ru
+ - vi
+ - tr
+ - uk
+ - ko
+ - ja
+
+
- @string/always
- @string/when_screen_off
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 141484d16..35c8c70fe 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -35,15 +35,20 @@
General Settings
Connect to device when Bluetooth turned on
+ Reconnect automatically
Preferred Audioplayer
Default
+
Date and Time
Sync time
Sync time to device when connecting and when time or timezone changes on Android
+
Theme
Light
Dark
+ Language
+
Notifications
Repetitions
Phone Calls
@@ -69,14 +74,30 @@
Mi Band address
Pebble Settings
+
+ Activity Trackers
Preferred Activitytracker
+ Sync Pebble Health
+ Sync Misfit
+ Sync Morpheuz
+
Allow 3rd Party Android App Access
Enable experimental support for Android Apps using PebbleKit
+
+ Sunrise and Sunset
+ Send sunrise and sunset times based on the location to the pebble timeline
+
+ Location
+ Acquire Location
+ Latitude
+ Longitude
+
Force Notification Protocol
This option forces using the latest notification protocol depending on the firmware version. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING!
Enable untested features
Enable features that are untested. ENABLE ONLY IF YOU KNOW WHAT YOU ARE DOING!
Reconnection Attempts
+
not connected
connecting
connected
@@ -224,6 +245,7 @@
This firmware is not compatible with the device
Alarms to reserve for upcoming events
Use Heartrate Sensor to improve sleep detection
+ Device time offset in hours (for detecting sleep of shift workers)
waiting for reconnect
Reinstall
@@ -251,5 +273,4 @@
Firmware not sent
Heart Rate
Heart Rate
- Reconnect automatically
diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml
index abe77ebae..a49185489 100644
--- a/app/src/main/res/xml/changelog_master.xml
+++ b/app/src/main/res/xml/changelog_master.xml
@@ -1,5 +1,13 @@
+
+ Pebble: option to send sunrise and sunset events to timeline
+ Pebble: fix problems with unknown app keys while configuring watchfaces
+ Mi Band: BLE connection fixes
+ Fixes for enabling logging at whithout restarting Gadgetbridge
+ Re-enable device paring activity on Android 6 (BLE scanning needs the location preference)
+ Display device address in device info
+
Pebble: fix more reconnnect issues
Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health
@@ -7,6 +15,7 @@
Pebble: enable pbw cache and watchface configuration for Firmware 2.x
Pebble: allow enabling of Pebble Health without "untested features" being enabled
Honour "Do Not Disturb" for phone calls and SMS
+ Pebble: fix music information being messed up
Pebble: hopefully fix some reconnect issues
diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml
index 37e5d7777..548884269 100644
--- a/app/src/main/res/xml/miband_preferences.xml
+++ b/app/src/main/res/xml/miband_preferences.xml
@@ -35,6 +35,14 @@
android:defaultValue="false"
android:key="mi_hr_sleep_detection"
android:title="@string/miband_prefs_hr_sleep_detection" />
+
+
+
+
+
+
+
+ android:summary="%s"
+ android:title="@string/pref_title_pebble_activitytracker" />
+
+ >
+
+
+
+
+
+
5) {
+ File dir = new File(dirName);
+ return dir;
+ }
+ fail("Property " + Logging.PROP_LOGFILES_DIR + " has invalid value: " + dirName);
+ return null; // not reached
+ }
+
+ @Test
+ public void testToggleLogging() {
+ try {
+ File dir = getLogFilesDir();
+ } catch (AssertionFailedError ignored) {
+ // expected, as not yet set up
+ }
+
+ try {
+ logging.setupLogging(true);
+ File dir = getLogFilesDir();
+ assertEquals(1, dir.list().length);
+ assertNotNull(logging.getFileLogger());
+ assertTrue(logging.getFileLogger().isStarted());
+
+ logging.setupLogging(false);
+ assertNotNull(logging.getFileLogger());
+ assertFalse(logging.getFileLogger().isStarted());
+
+ logging.setupLogging(true);
+ assertNotNull(logging.getFileLogger());
+ assertTrue(logging.getFileLogger().isStarted());
+ } catch (AssertionFailedError ex) {
+ logging.debugLoggingConfiguration();
+ System.err.println(System.getProperty("java.class.path"));
+ throw ex;
+ }
+ }
+}