diff --git a/.travis.yml b/.travis.yml index 7b70ab33c..e259ec35d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,6 @@ +os: linux +dist: trusty + language: android jdk: diff --git a/CHANGELOG.md b/CHANGELOG.md index ce3c159f5..01b1208a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ ### Changelog +#### Version 0.35.2 +* Mi Band 1/2: Crash when updating firmware while phone is set to Spanish +* Mi Band 4: Enable music info support (displays now on the band) +* Mi Band 4: Support setting date format (for built-in watchfaces) +* Amazfit Cor 2: Try to fix empty menu on device + +#### Version 0.35.1 +* Mi Band 4: Support flashing watchfaces, res and firmware (.ft untested) + +#### Version 0.35.0 +* Mi Band 4: Initial support (WARNING: INITIAL SETUP NEEDS MI FIT WITH ACCOUNT AND ROOT, NOT A RECOMMENDED DEVICE FOR GADGETBRIDGE) + +#### Version 0.34.1 +* Mi Band 1: Fix crash when entering per-device settings +* Mi Band 3: Allow setting date format in per-device settings +* ZeTime: Fix timestmaps +* Fix a crash when flashing an non-whitelisted firmware while using Gadgetbridge in Spanish + +#### Version 0.34.0 +* Mi Band 1/2/3/Bip/Cor: Migrate many settings to per-device settings (new settings icon in device card in main activity) +* Mi Band 3: Fix setting menu items with 2.4 firmware and add support for the new timer menu +* Amazfit Bip/Cor, Casio: Add support for muting incoming calls +* ZeTime: Remove endless recursion in ZeTime settings +* Recognize FairEmail notifications as generic email notifications + +#### Version 0.33.1 +* Mi Band 3: Recognize "Xiaomi Band 3" +* Amazfit Bip: Add German, Italian, French and Turkish to language settings + +#### Version 0.33.0 +* BFH-16: Initial support +* Mi Band 2/3/Bip/Cor: Generate random per-device security keys when pairing, allow manual override to still support multiple android devices connecting to the same device +* Mi Band 3: Add Indonesian, Thai, Arabic, Vietnamese, Portuguese, Dutch, Turkish and Ukrainian to language settings +* Mi Band 3: Support flashing latest Japanese-Korean font +* Amazfit Cor 2: Initial experimental support (untested) +* Pebble: Add pebblekit extension for reopening last app +* Casio: Bugfixes and improvements +* Lookup contacts also in work profile +* Fix searching in application name when blacklisting +* Remove misleading title from database management activity when no legacy database is available + #### Version 0.32.4 * Make voip call support optional (disabled by default) * Amazfit Bip: GPX export corrections diff --git a/README.md b/README.md index 47d70173c..1d0eb502a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +**IF YOU WANT TO EDIT THE WIKI**, do so on [codeberg.org](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki) +The wiki on github.com is a read-only mirror, as is the git repo itself. Issues and PRs will move to codeberg summer 2019, if you want your issue/PR comments migrated properly, please create a codeberg acount before we will migrate. + Gadgetbridge ============ @@ -17,6 +20,7 @@ vendor's servers. [![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge) [![Code Quality: Java](https://img.shields.io/lgtm/grade/java/g/Freeyourgadget/Gadgetbridge.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Freeyourgadget/Gadgetbridge/context:java) [![Total Alerts](https://img.shields.io/lgtm/alerts/g/Freeyourgadget/Gadgetbridge.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/Freeyourgadget/Gadgetbridge/alerts) +[![Translate](https://hosted.weblate.org/widgets/freeyourgadget/-/gadgetbridge/svg-badge.svg)](https://hosted.weblate.org/projects/freeyourgadget/gadgetbridge) ## Download @@ -27,6 +31,8 @@ vendor's servers. ## Supported Devices * Amazfit Bip [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip) * Amazfit Cor [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor) +* Amazfit Cor 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor-2) +* BFH-16 * Casio GB-6900B (WIP) * HPlus Devices (e.g. ZeBand) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/HPlus) * ID115 (WIP) @@ -36,6 +42,8 @@ vendor's servers. * Mi Band, Mi Band 1A, Mi Band 1S [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band) * Mi Band 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-2) * Mi Band 3 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-3) +* Mi Band 4 (WIP, NOT RECOMMENDED, NEEDS MI FIT WITH ACCOUNT AND ROOT ONCE) [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-4) +* Mi Scale 2 (currently only displays a toast after stepping on the scale) * NO.1 F1 (WIP) * Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble) * Pebble 2 [Wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble) @@ -100,6 +108,8 @@ Please [this wiki article](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki * Vadim Kaushan (ID115) * "maxirnilian" (Lenovo Watch 9) * Andreas Böhler (Casio GB-6900B) +* Jean-François Greffier (Mi Scale 2) +* Johannes Schmitt (BFH-16) ## Contribute @@ -115,6 +125,7 @@ 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 question 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. +- Do not ask for help regarding your own projects, unless they are Gadgetbridge related ## Having problems? diff --git a/app/build.gradle b/app/build.gradle index c877660e9..7121ef0c6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { targetSdkVersion 27 // Note: always bump BOTH versionCode and versionName! - versionName "0.32.4" - versionCode 147 + versionName "0.35.2" + versionCode 154 vectorDrawables.useSupportLibrary = true } buildTypes { @@ -63,10 +63,12 @@ dependencies { implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation "junit:junit:4.12" testImplementation "org.mockito:mockito-core:1.10.19" - testImplementation "org.robolectric:robolectric:3.6.1" + testImplementation "org.robolectric:robolectric:4.2.1" + testImplementation "com.google.code.gson:gson:2.8.5" implementation fileTree(dir: "libs", include: ["*.jar"]) - implementation "androidx.appcompat:appcompat:1.0.2" + implementation "androidx.appcompat:appcompat:1.1.0-rc01" + implementation "androidx.preference:preference:1.1.0-rc01" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.recyclerview:recyclerview:1.0.0" implementation "androidx.legacy:legacy-support-v4:1.0.0" @@ -84,7 +86,7 @@ dependencies { // use pristine greendao instead of our custom version, since our custom jitpack-packaged // version contains way too much and our custom patches are in the generator only. implementation "org.greenrobot:greendao:2.2.1" - implementation "org.apache.commons:commons-lang3:3.5" + implementation "org.apache.commons:commons-lang3:3.7" implementation "org.cyanogenmod:platform.sdk:6.0" implementation 'com.jaredrummler:colorpicker:1.0.2' // implementation project(":DaoCore") @@ -152,10 +154,10 @@ task findbugs(type: FindBugs) { xml.enabled = false html.enabled = true xml { - destination file("$project.buildDir/reports/findbugs/findbugs-output.xml") + destination file ("$project.buildDir/reports/findbugs/findbugs-output.xml") } html { - destination file("$project.buildDir/reports/findbugs/findbugs-output.html") + destination file ("$project.buildDir/reports/findbugs/findbugs-output.html") } } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9597b4a21..6bfd292b9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -406,7 +406,10 @@ + android:parentActivityName=".activities.ControlCenterv2" /> + = Build.VERSION_CODES.M; } + public static boolean isRunningNougatOrLater() { return VERSION.SDK_INT >= Build.VERSION_CODES.N; } - public static boolean isRunningOreoOrLater(){ + public static boolean isRunningOreoOrLater() { return VERSION.SDK_INT >= Build.VERSION_CODES.O; } @@ -481,14 +493,14 @@ public class GBApplication extends Application { saveAppsPebbleBlackList(); } -public static String packageNameToPebbleMsgSender(String packageName) { - if ("eu.siacs.conversations".equals(packageName)){ - return("Conversations"); - } else if ("net.osmand.plus".equals(packageName)) { - return("OsmAnd"); + public static String packageNameToPebbleMsgSender(String packageName) { + if ("eu.siacs.conversations".equals(packageName)) { + return ("Conversations"); + } else if ("net.osmand.plus".equals(packageName)) { + return ("OsmAnd"); + } + return packageName; } - return packageName; -} private static HashSet calendars_blacklist = null; @@ -581,47 +593,184 @@ public static String packageNameToPebbleMsgSender(String packageName) { private void migratePrefs(int oldVersion) { SharedPreferences.Editor editor = sharedPrefs.edit(); - switch (oldVersion) { - case 0: - String legacyGender = sharedPrefs.getString("mi_user_gender", null); - String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null); - String legacyWeight = sharedPrefs.getString("mi_user_weight_kg", null); - String legacyYOB = sharedPrefs.getString("mi_user_year_of_birth", null); - if (legacyGender != null) { - int gender = "male".equals(legacyGender) ? 1 : "female".equals(legacyGender) ? 0 : 2; - editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(gender)); - editor.remove("mi_user_gender"); - } - if (legacyHeight != null) { - editor.putString(ActivityUser.PREF_USER_HEIGHT_CM, legacyHeight); - editor.remove("mi_user_height_cm"); - } - if (legacyWeight != null) { - editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeight); - editor.remove("mi_user_weight_kg"); - } - if (legacyYOB != null) { - editor.putString(ActivityUser.PREF_USER_YEAR_OF_BIRTH, legacyYOB); - editor.remove("mi_user_year_of_birth"); - } - editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); - break; - case 1: - //migrate the integer version of gender introduced in version 1 to a string value, needed for the way Android accesses the shared preferences - int legacyGender_1 = 2; - try { - legacyGender_1 = sharedPrefs.getInt(ActivityUser.PREF_USER_GENDER, 2); - } catch (Exception e) { - Log.e(TAG, "Could not access legacy activity gender", e); - } - editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(legacyGender_1)); - //also silently migrate the version to a string value - editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); - break; + if (oldVersion == 0) { + String legacyGender = sharedPrefs.getString("mi_user_gender", null); + String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null); + String legacyWeight = sharedPrefs.getString("mi_user_weight_kg", null); + String legacyYOB = sharedPrefs.getString("mi_user_year_of_birth", null); + if (legacyGender != null) { + int gender = "male".equals(legacyGender) ? 1 : "female".equals(legacyGender) ? 0 : 2; + editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(gender)); + editor.remove("mi_user_gender"); + } + if (legacyHeight != null) { + editor.putString(ActivityUser.PREF_USER_HEIGHT_CM, legacyHeight); + editor.remove("mi_user_height_cm"); + } + if (legacyWeight != null) { + editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeight); + editor.remove("mi_user_weight_kg"); + } + if (legacyYOB != null) { + editor.putString(ActivityUser.PREF_USER_YEAR_OF_BIRTH, legacyYOB); + editor.remove("mi_user_year_of_birth"); + } } + if (oldVersion < 2) { + //migrate the integer version of gender introduced in version 1 to a string value, needed for the way Android accesses the shared preferences + int legacyGender_1 = 2; + try { + legacyGender_1 = sharedPrefs.getInt(ActivityUser.PREF_USER_GENDER, 2); + } catch (Exception e) { + Log.e(TAG, "Could not access legacy activity gender", e); + } + editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(legacyGender_1)); + } + if (oldVersion < 3) { + try (DBHandler db = acquireDB()) { + DaoSession daoSession = db.getDaoSession(); + List activeDevices = DBHelper.getActiveDevices(daoSession); + for (Device dbDevice : activeDevices) { + SharedPreferences.Editor deviceSharedPrefsEdit = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier()).edit(); + if (sharedPrefs != null) { + String preferenceKey = dbDevice.getIdentifier() + "_lastSportsActivityTimeMillis"; + long lastSportsActivityTimeMillis = sharedPrefs.getLong(preferenceKey, 0); + if (lastSportsActivityTimeMillis != 0) { + deviceSharedPrefsEdit.putLong("lastSportsActivityTimeMillis", lastSportsActivityTimeMillis); + editor.remove(preferenceKey); + } + preferenceKey = dbDevice.getIdentifier() + "_lastSyncTimeMillis"; + long lastSyncTimeMillis = sharedPrefs.getLong(preferenceKey, 0); + if (lastSyncTimeMillis != 0) { + deviceSharedPrefsEdit.putLong("lastSyncTimeMillis", lastSyncTimeMillis); + editor.remove(preferenceKey); + } + + String newLanguage = null; + Set displayItems = null; + + DeviceType deviceType = fromKey(dbDevice.getType()); + + if (deviceType == AMAZFITBIP || deviceType == AMAZFITCOR || deviceType == AMAZFITCOR2) { + int oldLanguage = prefs.getInt("amazfitbip_language", -1); + newLanguage = "auto"; + String[] oldLanguageLookup = {"zh_CN", "zh_TW", "en_US", "es_ES", "ru_RU", "de_DE", "it_IT", "fr_FR", "tr_TR"}; + if (oldLanguage >= 0 && oldLanguage < oldLanguageLookup.length) { + newLanguage = oldLanguageLookup[oldLanguage]; + } + } + + if (deviceType == AMAZFITBIP || deviceType == AMAZFITCOR) { + deviceSharedPrefsEdit.putString("disconnect_notification", prefs.getString("disconnect_notification", "off")); + deviceSharedPrefsEdit.putString("disconnect_notification_start", prefs.getString("disconnect_notification_start", "8:00")); + deviceSharedPrefsEdit.putString("disconnect_notification_end", prefs.getString("disconnect_notification_end", "22:00")); + } + if (deviceType == MIBAND2 || deviceType == MIBAND3) { + deviceSharedPrefsEdit.putString("do_not_disturb", prefs.getString("mi2_do_not_disturb", "off")); + deviceSharedPrefsEdit.putString("do_not_disturb_start", prefs.getString("mi2_do_not_disturb_start", "1:00")); + deviceSharedPrefsEdit.putString("do_not_disturb_end", prefs.getString("mi2_do_not_disturb_end", "6:00")); + } + if (dbDevice.getManufacturer().equals("Huami")) { + deviceSharedPrefsEdit.putString("activate_display_on_lift_wrist", prefs.getString("activate_display_on_lift_wrist", "off")); + deviceSharedPrefsEdit.putString("display_on_lift_start", prefs.getString("display_on_lift_start", "0:00")); + deviceSharedPrefsEdit.putString("display_on_lift_end", prefs.getString("display_on_lift_end", "0:00")); + } + switch (deviceType) { + case MIBAND: + deviceSharedPrefsEdit.putBoolean("low_latency_fw_update", prefs.getBoolean("mi_low_latency_fw_update", true)); + deviceSharedPrefsEdit.putInt("device_time_offset_hours", prefs.getInt("mi_device_time_offset_hours", 0)); + break; + case AMAZFITCOR: + displayItems = prefs.getStringSet("cor_display_items", null); + break; + case AMAZFITBIP: + displayItems = prefs.getStringSet("bip_display_items", null); + break; + case MIBAND2: + displayItems = prefs.getStringSet("mi2_display_items", null); + deviceSharedPrefsEdit.putBoolean("mi2_enable_text_notifications", prefs.getBoolean("mi2_enable_text_notifications", true)); + deviceSharedPrefsEdit.putString("mi2_dateformat", prefs.getString("mi2_dateformat", "dateformat_time")); + deviceSharedPrefsEdit.putBoolean("rotate_wrist_to_cycle_info", prefs.getBoolean("mi2_rotate_wrist_to_switch_info", false)); + break; + case MIBAND3: + newLanguage = prefs.getString("miband3_language", "auto"); + displayItems = prefs.getStringSet("miband3_display_items", null); + deviceSharedPrefsEdit.putBoolean("swipe_unlock", prefs.getBoolean("mi3_band_screen_unlock", false)); + deviceSharedPrefsEdit.putString("night_mode", prefs.getString("mi3_night_mode", "off")); + deviceSharedPrefsEdit.putString("night_mode_start", prefs.getString("mi3_night_mode_start", "16:00")); + deviceSharedPrefsEdit.putString("night_mode_end", prefs.getString("mi3_night_mode_end", "7:00")); + + } + if (displayItems != null) { + deviceSharedPrefsEdit.putStringSet("display_items", displayItems); + } + if (newLanguage != null) { + deviceSharedPrefsEdit.putString("language", newLanguage); + } + } + + deviceSharedPrefsEdit.apply(); + } + editor.remove("amazfitbip_language"); + editor.remove("bip_display_items"); + editor.remove("cor_display_items"); + editor.remove("disconnect_notification"); + editor.remove("disconnect_notification_start"); + editor.remove("disconnect_notification_end"); + editor.remove("activate_display_on_lift_wrist"); + editor.remove("display_on_lift_start"); + editor.remove("display_on_lift_end"); + + editor.remove("mi_low_latency_fw_update"); + editor.remove("mi_device_time_offset_hours"); + editor.remove("mi2_do_not_disturb"); + editor.remove("mi2_do_not_disturb_start"); + editor.remove("mi2_do_not_disturb_end"); + editor.remove("mi2_dateformat"); + editor.remove("mi2_display_items"); + editor.remove("mi2_rotate_wrist_to_switch_info"); + editor.remove("mi2_enable_text_notifications"); + editor.remove("mi3_band_screen_unlock"); + editor.remove("mi3_night_mode"); + editor.remove("mi3_night_mode_start"); + editor.remove("mi3_night_mode_end"); + editor.remove("miband3_language"); + + } catch (Exception e) { + Log.w(TAG, "error acquiring DB lock"); + } + } + if (oldVersion < 4) { + try (DBHandler db = acquireDB()) { + DaoSession daoSession = db.getDaoSession(); + List activeDevices = DBHelper.getActiveDevices(daoSession); + for (Device dbDevice : activeDevices) { + SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier()); + SharedPreferences.Editor deviceSharedPrefsEdit = deviceSharedPrefs.edit(); + DeviceType deviceType = fromKey(dbDevice.getType()); + + if (deviceType == MIBAND) { + int deviceTimeOffsetHours = deviceSharedPrefs.getInt("device_time_offset_hours",0); + deviceSharedPrefsEdit.putString("device_time_offset_hours", Integer.toString(deviceTimeOffsetHours) ); + } + + deviceSharedPrefsEdit.apply(); + } + } catch (Exception e) { + Log.w(TAG, "error acquiring DB lock"); + } + } + editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); editor.apply(); } + public static SharedPreferences getDeviceSpecificSharedPrefs(String deviceIdentifier) { + if (deviceIdentifier == null || deviceIdentifier.isEmpty()) { + return null; + } + return context.getSharedPreferences("devicesettings_" + deviceIdentifier, Context.MODE_PRIVATE); + } + public static void setLanguage(String lang) { if (lang.equals("default")) { language = Resources.getSystem().getConfiguration().locale; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java index 56b97a9c0..acb260aac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java @@ -36,6 +36,10 @@ import android.widget.DatePicker; import android.widget.ListView; import android.widget.Toast; +import androidx.core.content.FileProvider; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import java.io.File; @@ -45,9 +49,6 @@ import java.util.Calendar; import java.util.List; import java.util.Objects; -import androidx.core.content.FileProvider; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter; @@ -240,10 +241,10 @@ public class ActivitySummariesActivity extends AbstractListActivity activeDevices = DBHelper.getActiveDevices(lockHandler.getDaoSession()); + for (Device dbDevice : activeDevices) { + SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier()); + if (sharedPrefs != null) { + File myPath = FileUtils.getExternalFilesDir(); + File myFile = new File(myPath, "Export_preference_" + dbDevice.getIdentifier()); + try { + ImportExportSharedPreferences.exportToFile(deviceSharedPrefs, myFile, null); + } catch (Exception ignore) { + // some devices no not have device specific preferences + } + } + } + } catch (Exception e) { + GB.toast("Error exporting device specific preferences", Toast.LENGTH_SHORT, GB.ERROR); + } } private void importShared() { - // BEGIN EXAMPLE - File myPath = null; try { - myPath = FileUtils.getExternalFilesDir(); + File myPath = FileUtils.getExternalFilesDir(); File myFile = new File(myPath, "Export_preference"); - shared_file.importFromFile(sharedPrefs,myFile ); + ImportExportSharedPreferences.importFromFile(sharedPrefs, myFile); } catch (Exception ex) { GB.toast(DbManagementActivity.this, getString(R.string.dbmanagementactivity_error_importing_db, ex.getMessage()), Toast.LENGTH_LONG, GB.ERROR, ex); } + try (DBHandler lockHandler = GBApplication.acquireDB()) { + List activeDevices = DBHelper.getActiveDevices(lockHandler.getDaoSession()); + for (Device dbDevice : activeDevices) { + SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier()); + if (sharedPrefs != null) { + File myPath = FileUtils.getExternalFilesDir(); + File myFile = new File(myPath, "Export_preference_" + dbDevice.getIdentifier()); + try { + ImportExportSharedPreferences.importFromFile(deviceSharedPrefs, myFile); + } catch (Exception ignore) { + // some devices no not have device specific preferences + } + } + } + } catch (Exception e) { + GB.toast("Error importing device specific preferences", Toast.LENGTH_SHORT, GB.ERROR); + } } private void exportDB() { @@ -159,7 +188,6 @@ public class DbManagementActivity extends AbstractGBActivity { @Override public void onClick(DialogInterface dialog, int which) { try (DBHandler dbHandler = GBApplication.acquireDB()) { - importShared(); DBHelper helper = new DBHelper(DbManagementActivity.this); File dir = FileUtils.getExternalFilesDir(); SQLiteOpenHelper sqLiteOpenHelper = dbHandler.getHelper(); @@ -170,6 +198,7 @@ public class DbManagementActivity extends AbstractGBActivity { } catch (Exception ex) { GB.toast(DbManagementActivity.this, getString(R.string.dbmanagementactivity_error_importing_db, ex.getMessage()), Toast.LENGTH_LONG, GB.ERROR, ex); } + importShared(); } }) .setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() { @@ -227,10 +256,9 @@ public class DbManagementActivity extends AbstractGBActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(this); - return true; + if (item.getItemId() == android.R.id.home) { + NavUtils.navigateUpFromSameTask(this); + return true; } return super.onOptionsItemSelected(item); } 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 6fe43a40d..d29bacc70 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -49,6 +49,9 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,9 +59,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import androidx.core.app.ActivityCompat; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity; import nodomain.freeyourgadget.gadgetbridge.adapter.DeviceCandidateAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -69,12 +72,15 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener { +public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class); private static final long SCAN_DURATION = 60000; // 60s private ScanCallback newLeScanCallback = null; + // Disabled for testing, it seems worse for a few people + private final boolean disableNewBLEScanning = true; + private final Handler handler = new Handler(); private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() { @@ -93,7 +99,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView // continue with LE scan, if available if (isScanning == Scanning.SCANNING_BT) { checkAndRequestLocationPermission(); - if (GBApplication.isRunningLollipopOrLater()) { + if (GBApplication.isRunningLollipopOrLater() && !disableNewBLEScanning) { startDiscovery(Scanning.SCANNING_NEW_BTLE); } else { startDiscovery(Scanning.SCANNING_BTLE); @@ -279,6 +285,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView cadidateListAdapter = new DeviceCandidateAdapter(this, deviceCandidates); deviceCandidatesView.setAdapter(cadidateListAdapter); deviceCandidatesView.setOnItemClickListener(this); + deviceCandidatesView.setOnItemLongClickListener(this); IntentFilter bluetoothIntents = new IntentFilter(); bluetoothIntents.addAction(BluetoothDevice.ACTION_FOUND); @@ -294,7 +301,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView } @Override - protected void onSaveInstanceState(Bundle outState) { + protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelableArrayList("deviceCandidates", deviceCandidates); } @@ -577,6 +584,27 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView return m; } + @Override + public boolean onItemLongClick(AdapterView adapterView, View view, int position, long id) { + GBDeviceCandidate deviceCandidate = deviceCandidates.get(position); + if (deviceCandidate == null) { + LOG.error("Device candidate clicked, but item not found"); + return true; + } + + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate); + GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate); + if (coordinator.getSupportedDeviceSpecificSettings(device) == null) { + return true; + } + + Intent startIntent; + startIntent = new Intent(this, DeviceSettingsActivity.class); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, device); + startActivity(startIntent); + return true; + } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { GBDeviceCandidate deviceCandidate = deviceCandidates.get(position); @@ -628,7 +656,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView super.onPause(); stopBTDiscovery(); stopBTLEDiscovery(); - if (GBApplication.isRunningLollipopOrLater()) { + if (GBApplication.isRunningLollipopOrLater() && !disableNewBLEScanning) { stopNewBTLEDiscovery(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationFilterActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationFilterActivity.java index 8df741f62..edd0fc26f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationFilterActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/NotificationFilterActivity.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 abettenburg, AndrewBedscastle, Daniele Gobbetti +/* Copyright (C) 2018-2019 abettenburg, AndrewBedscastle, Carsten Pfeiffer, + Daniele Gobbetti This file is part of Gadgetbridge. 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 37f6eb12a..90cbcbfed 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2019 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Felix Konstantin Maurer, José Rebelo, Martin, Normano64, - Pavel Elagin + Pavel Elagin, Sebastian Kranz This file is part of Gadgetbridge. @@ -38,6 +38,9 @@ import android.preference.PreferenceManager; import android.provider.DocumentsContract; import android.widget.Toast; +import androidx.core.app.ActivityCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,14 +49,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import androidx.core.app.ActivityCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.BuildConfig; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimePreferenceActivity; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; @@ -63,23 +63,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION_END; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION_START; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ON_LIFT_END; -import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ON_LIFT_START; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DISPLAY_ITEMS; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_OFF; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_SCHEDULED; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_BAND_SCREEN_UNLOCK; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_END; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_OFF; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_SCHEDULED; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_START; 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_STEPS_GOAL; @@ -374,199 +357,6 @@ public class SettingsActivity extends AbstractSettingsActivity { autoFetchInterval); pref.setSummary(summary); - - - final Preference displayPages = findPreference("bip_display_items"); - displayPages.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS); - } - }); - return true; - } - }); - - final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT); - setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT); - } - }); - return true; - } - }); - - final Preference miBand2DisplayItems = findPreference(PREF_MI2_DISPLAY_ITEMS); - miBand2DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS); - } - }); - return true; - } - }); - - final Preference miBand3ScreenUnlock = findPreference(PREF_MI3_BAND_SCREEN_UNLOCK); - miBand3ScreenUnlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI3_BAND_SCREEN_UNLOCK); - } - }); - return true; - } - }); - - final Preference miBand3DisplayItems = findPreference("miband3_display_items"); - miBand3DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS); - } - }); - return true; - } - }); - - String nightModeState = prefs.getString(MiBandConst.PREF_MI3_NIGHT_MODE, PREF_MI3_NIGHT_MODE_OFF); - boolean nightModeScheduled = nightModeState.equals(PREF_MI3_NIGHT_MODE_SCHEDULED); - - final Preference nightModeStart = findPreference(PREF_MI3_NIGHT_MODE_START); - nightModeStart.setEnabled(nightModeScheduled); - nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE_START); - } - }); - return true; - } - }); - - - final Preference nightModeEnd = findPreference(PREF_MI3_NIGHT_MODE_END); - nightModeEnd.setEnabled(nightModeScheduled); - nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE_END); - } - }); - return true; - } - }); - - - final Preference nightMode = findPreference(PREF_MI3_NIGHT_MODE); - nightMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - final boolean scheduled = PREF_MI3_NIGHT_MODE_SCHEDULED.equals(newVal.toString()); - - nightModeStart.setEnabled(scheduled); - nightModeEnd.setEnabled(scheduled); - - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE); - } - }); - return true; - } - }); - - final Preference corDisplayItems = findPreference("cor_display_items"); - corDisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS); - } - }); - return true; - } - }); - - String disconnectNotificationState = prefs.getString(PREF_DISCONNECT_NOTIFICATION, PREF_MI2_DO_NOT_DISTURB_OFF); - boolean disconnectNotificationScheduled = disconnectNotificationState.equals(PREF_MI2_DO_NOT_DISTURB_SCHEDULED); - - final Preference disconnectNotificationStart = findPreference(PREF_DISCONNECT_NOTIFICATION_START); - disconnectNotificationStart.setEnabled(disconnectNotificationScheduled); - disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_START); - } - }); - return true; - } - }); - - - final Preference disconnectNotificationEnd = findPreference(PREF_DISCONNECT_NOTIFICATION_END); - disconnectNotificationStart.setEnabled(disconnectNotificationScheduled); - disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_END); - } - }); - return true; - } - }); - - - final Preference disconnectNotification = findPreference(PREF_DISCONNECT_NOTIFICATION); - disconnectNotification.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - final boolean scheduled = PREF_MI2_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); - - disconnectNotificationStart.setEnabled(scheduled); - disconnectNotificationEnd.setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION); - } - }); - return true; - } - }); - // Get all receivers of Media Buttons Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); @@ -695,7 +485,6 @@ public class SettingsActivity extends AbstractSettingsActivity { PREF_USER_WEIGHT_KG, PREF_USER_SLEEP_DURATION, PREF_USER_STEPS_GOAL, - PREF_MI2_ENABLE_TEXT_NOTIFICATIONS, "weather_city", }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index fa6d97da4..d8bb0ad8a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -429,7 +429,7 @@ public abstract class AbstractAppManagerFragment extends Fragment { startActivity(startIntent); return true; case R.id.appmanager_app_openinstore: - String url = "https://pebble-appstore.romanport.com/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/0/?query=" + selectedApp.getName(); + String url = "https://apps.rebble.io/en_US/search/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/1/?native=true&?query=" + Uri.encode(selectedApp.getName()); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); startActivity(intent); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java index 34bebe755..0af2e32f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -48,6 +48,7 @@ import java.util.Calendar; import java.util.List; import java.util.Locale; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -58,7 +59,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; public abstract class AbstractWeekChartFragment extends AbstractChartFragment { protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class); - protected final int TOTAL_DAYS = 7; + protected final int TOTAL_DAYS = getRangeDays(); private Locale mLocale; private int mTargetValue = 0; @@ -102,6 +103,17 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { // mBalanceView.setText(getBalanceMessage(balance)); } + private String getWeeksChartsLabel(Calendar day){ + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + //month, show day date + return String.valueOf(day.get(Calendar.DAY_OF_MONTH)); + } + else{ + //week, show short day name + return day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale); + } + } + private WeekChartsData refreshWeekBeforeData(DBHandler db, BarChart barChart, Calendar day, GBDevice device) { day = (Calendar) day.clone(); // do not modify the caller's argument day.add(Calendar.DATE, -TOTAL_DAYS); @@ -114,7 +126,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { balance += calculateBalance(amounts); entries.add(new BarEntry(counter, getTotalsForActivityAmounts(amounts))); - labels.add(day.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, mLocale)); + labels.add(getWeeksChartsLabel(day)); day.add(Calendar.DATE, 1); } @@ -130,7 +142,28 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { barChart.getAxisLeft().removeAllLimitLines(); barChart.getAxisLeft().addLimitLine(target); - return new WeekChartsData(barData, new PreformattedXIndexLabelFormatter(labels), getBalanceMessage(balance, mTargetValue)); + float average = 0; + if (TOTAL_DAYS > 0) { + average = Math.abs(balance / TOTAL_DAYS); + } + LimitLine average_line = new LimitLine(average); + average_line.setLabel(getString(R.string.average, getAverage(average))); + + if (average > (mTargetValue)) { + average_line.setLineColor(Color.GREEN); + average_line.setTextColor(Color.GREEN); + } + else { + average_line.setLineColor(Color.RED); + average_line.setTextColor(Color.RED); + } + if (average > 0) { + if (GBApplication.getPrefs().getBoolean("charts_show_average", true)) { + barChart.getAxisLeft().addLimitLine(average_line); + } + } + + return new WeekChartsData(barData, new PreformattedXIndexLabelFormatter(labels), getBalanceMessage(balance, mTargetValue)); } private DayData refreshDayPie(DBHandler db, Calendar day, GBDevice device) { @@ -315,6 +348,16 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment { return amounts; } + private int getRangeDays(){ + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + return 30;} + else{ + return 7; + } + } + + abstract String getAverage(float value); + abstract int getGoal(); abstract int getOffsetHours(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java index a85bced23..2f9a6c881 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java @@ -338,6 +338,24 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts return 5; } + private String getSleepTitle() { + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + return getString(R.string.weeksleepchart_sleep_a_month); + } + else{ + return getString(R.string.weeksleepchart_sleep_a_week); + } + } + + public String getStepsTitle() { + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + return getString(R.string.weekstepschart_steps_a_month); + } + else{ + return getString(R.string.weekstepschart_steps_a_week); + } + } + @Override public CharSequence getPageTitle(int position) { switch (position) { @@ -346,9 +364,9 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts case 1: return getString(R.string.sleepchart_your_sleep); case 2: - return getString(R.string.weeksleepchart_sleep_a_week); + return getSleepTitle(); case 3: - return getString(R.string.weekstepschart_steps_a_week); + return getStepsTitle(); case 4: return getString(R.string.stats_title); case 5: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java index 936bcc27a..04ed240a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -40,7 +40,12 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; public class WeekSleepChartFragment extends AbstractWeekChartFragment { @Override public String getTitle() { - return getString(R.string.weeksleepchart_sleep_a_week); + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + return getString(R.string.weeksleepchart_sleep_a_month); + } + else{ + return getString(R.string.weeksleepchart_sleep_a_week); + } } @Override @@ -167,4 +172,10 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment { private String getHM(long value) { return DateTimeUtils.formatDurationHoursMinutes(value, TimeUnit.MINUTES); } + + @Override + String getAverage(float value) { + return getHM((long)value); + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java index 35ef4ee40..24f02940d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java @@ -30,7 +30,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; public class WeekStepsChartFragment extends AbstractWeekChartFragment { @Override public String getTitle() { - return getString(R.string.weekstepschart_steps_a_week); + if (GBApplication.getPrefs().getBoolean("charts_range", true)) { + return getString(R.string.weekstepschart_steps_a_month); + } + else{ + return getString(R.string.weekstepschart_steps_a_week); + } } @Override @@ -113,4 +118,9 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment { } else return getString(R.string.no_data); } + + @Override + String getAverage(float value) { + return String.format("%.0f", value); + } } 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 new file mode 100644 index 000000000..8b2e2ca93 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsActivity.java @@ -0,0 +1,79 @@ +/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti + + 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.activities.devicesettings; + +import android.os.Bundle; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; + + +public class DeviceSettingsActivity extends AbstractGBActivity implements + PreferenceFragmentCompat.OnPreferenceStartScreenCallback { + private static final Logger LOG = LoggerFactory.getLogger(DeviceSettingsActivity.class); + + GBDevice device; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_device_settings); + if (savedInstanceState == null) { + Fragment fragment = getSupportFragmentManager().findFragmentByTag(DeviceSpecificSettingsFragment.FRAGMENT_TAG); + if (fragment == null) { + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); + fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), coordinator.getSupportedDeviceSpecificSettings(device)); + } + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.settings_container, fragment, DeviceSpecificSettingsFragment.FRAGMENT_TAG) + .commit(); + + } + } + + @Override + public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen preferenceScreen) { + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); + + PreferenceFragmentCompat fragment = DeviceSpecificSettingsFragment.newInstance(device.getAddress(), coordinator.getSupportedDeviceSpecificSettings(device)); + Bundle args = fragment.getArguments(); + args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey()); + fragment.setArguments(args); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.settings_container, fragment, preferenceScreen.getKey()) + .addToBackStack(preferenceScreen.getKey()) + .commit(); + return true; + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..83aa6ab2f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -0,0 +1,418 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings; + +import android.os.Bundle; +import android.text.InputType; +import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; +import androidx.preference.EditTextPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; +import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference; +import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment; + +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DATEFORMAT; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ITEMS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ON_LIFT_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ON_LIFT_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_LANGUAGE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_OFF; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_SCHEDULED; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_OFF; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_SCHEDULED; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_NIGHT_MODE_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_SWIPE_UNLOCK; + +public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat { + + private static final Logger LOG = LoggerFactory.getLogger(DeviceSpecificSettingsFragment.class); + + static final String FRAGMENT_TAG = "DEVICE_SPECIFIC_SETTINGS_FRAGMENT"; + + private void setSettingsFileSuffix(String settingsFileSuffix, @NonNull int[] supportedSettings) { + Bundle args = new Bundle(); + args.putString("settingsFileSuffix", settingsFileSuffix); + args.putIntArray("supportedSettings", supportedSettings); + setArguments(args); + } + + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + Bundle arguments = getArguments(); + if (arguments == null) { + return; + } + String settingsFileSuffix = arguments.getString("settingsFileSuffix", null); + int[] supportedSettings = arguments.getIntArray("supportedSettings"); + if (settingsFileSuffix == null || supportedSettings == null) { + return; + } + + getPreferenceManager().setSharedPreferencesName("devicesettings_" + settingsFileSuffix); + + if (rootKey == null) { + // we are the main preference screen + boolean first = true; + for (int setting : supportedSettings) { + if (first) { + setPreferencesFromResource(setting, null); + first = false; + } else { + addPreferencesFromResource(setting); + } + } + } else { + // Now, this is ugly: search all the xml files for the rootKey + for (int setting : supportedSettings) { + try { + setPreferencesFromResource(setting, rootKey); + } catch (Exception ignore) { + continue; + } + break; + } + } + setChangeListener(); + } + + /* + * delayed execution so that the preferences are applied first + */ + private void invokeLater(Runnable runnable) { + getListView().post(runnable); + } + + private void setChangeListener() { + Prefs prefs = new Prefs(getPreferenceManager().getSharedPreferences()); + String disconnectNotificationState = prefs.getString(PREF_DISCONNECT_NOTIFICATION, PREF_DO_NOT_DISTURB_OFF); + boolean disconnectNotificationScheduled = disconnectNotificationState.equals(PREF_DO_NOT_DISTURB_SCHEDULED); + + final Preference disconnectNotificationStart = findPreference(PREF_DISCONNECT_NOTIFICATION_START); + if (disconnectNotificationStart != null) { + disconnectNotificationStart.setEnabled(disconnectNotificationScheduled); + disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_START); + } + }); + return true; + } + }); + } + + final Preference disconnectNotificationEnd = findPreference(PREF_DISCONNECT_NOTIFICATION_END); + if (disconnectNotificationEnd != null) { + disconnectNotificationEnd.setEnabled(disconnectNotificationScheduled); + disconnectNotificationEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_END); + } + }); + return true; + } + }); + } + + final Preference disconnectNotification = findPreference(PREF_DISCONNECT_NOTIFICATION); + if (disconnectNotification != null) { + disconnectNotification.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + final boolean scheduled = PREF_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); + + Objects.requireNonNull(disconnectNotificationStart).setEnabled(scheduled); + Objects.requireNonNull(disconnectNotificationEnd).setEnabled(scheduled); + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION); + } + }); + return true; + } + }); + + } + + String nightModeState = prefs.getString(MiBandConst.PREF_NIGHT_MODE, PREF_NIGHT_MODE_OFF); + boolean nightModeScheduled = nightModeState.equals(PREF_NIGHT_MODE_SCHEDULED); + + final Preference nightModeStart = findPreference(PREF_NIGHT_MODE_START); + if (nightModeStart != null) { + nightModeStart.setEnabled(nightModeScheduled); + nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_START); + } + }); + return true; + } + }); + } + + final Preference nightModeEnd = findPreference(PREF_NIGHT_MODE_END); + if (nightModeEnd != null) { + nightModeEnd.setEnabled(nightModeScheduled); + nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_END); + } + }); + return true; + } + }); + } + + final Preference nightMode = findPreference(PREF_NIGHT_MODE); + if (nightMode != null) { + + nightMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + final boolean scheduled = PREF_NIGHT_MODE_SCHEDULED.equals(newVal.toString()); + + Objects.requireNonNull(nightModeStart).setEnabled(scheduled); + Objects.requireNonNull(nightModeEnd).setEnabled(scheduled); + + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE); + } + }); + return true; + } + }); + } + + + String doNotDisturbState = prefs.getString(MiBandConst.PREF_DO_NOT_DISTURB, PREF_DO_NOT_DISTURB_OFF); + boolean doNotDisturbScheduled = doNotDisturbState.equals(PREF_DO_NOT_DISTURB_SCHEDULED); + + final Preference doNotDisturbStart = findPreference(PREF_DO_NOT_DISTURB_START); + if (doNotDisturbStart != null) { + doNotDisturbStart.setEnabled(doNotDisturbScheduled); + doNotDisturbStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_START); + } + }); + return true; + } + }); + } + + final Preference doNotDisturbEnd = findPreference(PREF_DO_NOT_DISTURB_END); + if (doNotDisturbEnd != null) { + doNotDisturbEnd.setEnabled(doNotDisturbScheduled); + doNotDisturbEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_END); + } + }); + return true; + } + }); + } + + final Preference doNotDisturb = findPreference(PREF_DO_NOT_DISTURB); + if (doNotDisturb != null) { + doNotDisturb.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + final boolean scheduled = PREF_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); + + Objects.requireNonNull(doNotDisturbStart).setEnabled(scheduled); + Objects.requireNonNull(doNotDisturbEnd).setEnabled(scheduled); + + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB); + } + }); + return true; + } + }); + } + + addPreferenceHandlerFor(PREF_SWIPE_UNLOCK); + addPreferenceHandlerFor(PREF_MI2_DATEFORMAT); + addPreferenceHandlerFor(PREF_DATEFORMAT); + addPreferenceHandlerFor(PREF_DISPLAY_ITEMS); + addPreferenceHandlerFor(PREF_LANGUAGE); + + String displayOnLiftState = prefs.getString(PREF_ACTIVATE_DISPLAY_ON_LIFT, PREF_DO_NOT_DISTURB_OFF); + boolean displayOnLiftScheduled = displayOnLiftState.equals(PREF_DO_NOT_DISTURB_SCHEDULED); + + final Preference rotateWristCycleInfo = findPreference(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); + if (rotateWristCycleInfo != null) { + rotateWristCycleInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(displayOnLiftState)); + rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); + } + }); + return true; + } + }); + } + + final Preference displayOnLiftStart = findPreference(PREF_DISPLAY_ON_LIFT_START); + if (displayOnLiftStart != null) { + displayOnLiftStart.setEnabled(displayOnLiftScheduled); + displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START); + } + }); + return true; + } + }); + } + + final Preference displayOnLiftEnd = findPreference(PREF_DISPLAY_ON_LIFT_END); + if (displayOnLiftEnd != null) { + displayOnLiftEnd.setEnabled(displayOnLiftScheduled); + displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END); + } + }); + return true; + } + }); + } + + final Preference displayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT); + if (displayOnLift != null) { + displayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + final boolean scheduled = PREF_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); + Objects.requireNonNull(displayOnLiftStart).setEnabled(scheduled); + Objects.requireNonNull(displayOnLiftEnd).setEnabled(scheduled); + if (rotateWristCycleInfo != null) { + rotateWristCycleInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString())); + } + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); + } + }); + return true; + } + }); + } + + EditTextPreference pref = findPreference(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); + if (pref != null) { + pref.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() { + @Override + public void onBindEditText(@NonNull EditText editText) { + editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED); + } + }); + } + } + + static DeviceSpecificSettingsFragment newInstance(String settingsFileSuffix, @NonNull int[] supportedSettings) { + DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment(); + fragment.setSettingsFileSuffix(settingsFileSuffix, supportedSettings); + + return fragment; + } + + @Override + public void onDisplayPreferenceDialog(Preference preference) { + DialogFragment dialogFragment; + if (preference instanceof XTimePreference) { + dialogFragment = new XTimePreferenceFragment(); + Bundle bundle = new Bundle(1); + bundle.putString("key", preference.getKey()); + dialogFragment.setArguments(bundle); + dialogFragment.setTargetFragment(this, 0); + if (getFragmentManager() != null) { + dialogFragment.show(getFragmentManager(), "androidx.preference.PreferenceFragment.DIALOG"); + } + } else { + super.onDisplayPreferenceDialog(preference); + } + } + + private void addPreferenceHandlerFor(final String preferenceKey) { + Preference pref = findPreference(preferenceKey); + if (pref != null) { + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(preferenceKey); + } + }); + return true; + } + }); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java index 46284b2c9..33678d51e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java @@ -223,7 +223,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter getSampleProvider(GBDevice device, DaoSession session) { return new MiBand2SampleProvider(device, session); } - public static DateTimeDisplay getDateDisplay(Context context) throws IllegalArgumentException { - Prefs prefs = GBApplication.getPrefs(); + public static DateTimeDisplay getDateDisplay(Context context, String deviceAddress) throws IllegalArgumentException { + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); String dateFormatTime = context.getString(R.string.p_dateformat_time); - if (dateFormatTime.equals(prefs.getString(MiBandConst.PREF_MI2_DATEFORMAT, dateFormatTime))) { + if (dateFormatTime.equals(sharedPrefs.getString(MiBandConst.PREF_MI2_DATEFORMAT, dateFormatTime))) { return DateTimeDisplay.TIME; } return DateTimeDisplay.DATE_TIME; } - public static ActivateDisplayOnLift getActivateDisplayOnLiftWrist(Context context) { - Prefs prefs = GBApplication.getPrefs(); + public static ActivateDisplayOnLift getActivateDisplayOnLiftWrist(Context context, String deviceAddress) { + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); String liftOff = context.getString(R.string.p_off); String liftOn = context.getString(R.string.p_on); @@ -151,16 +158,16 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return ActivateDisplayOnLift.OFF; } - public static Date getDisplayOnLiftStart() { - return getTimePreference(HuamiConst.PREF_DISPLAY_ON_LIFT_START, "00:00"); + public static Date getDisplayOnLiftStart(String deviceAddress) { + return getTimePreference(HuamiConst.PREF_DISPLAY_ON_LIFT_START, "00:00", deviceAddress); } - public static Date getDisplayOnLiftEnd() { - return getTimePreference(HuamiConst.PREF_DISPLAY_ON_LIFT_END, "00:00"); + public static Date getDisplayOnLiftEnd(String deviceAddress) { + return getTimePreference(HuamiConst.PREF_DISPLAY_ON_LIFT_END, "00:00", deviceAddress); } - public static DisconnectNotificationSetting getDisconnectNotificationSetting(Context context) { - Prefs prefs = GBApplication.getPrefs(); + public static DisconnectNotificationSetting getDisconnectNotificationSetting(Context context, String deviceAddress) { + Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); String liftOff = context.getString(R.string.p_off); String liftOn = context.getString(R.string.p_on); @@ -177,17 +184,17 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return DisconnectNotificationSetting.OFF; } - public static Date getDisconnectNotificationStart() { - return getTimePreference(HuamiConst.PREF_DISCONNECT_NOTIFICATION_START, "00:00"); + public static Date getDisconnectNotificationStart(String deviceAddress) { + return getTimePreference(HuamiConst.PREF_DISCONNECT_NOTIFICATION_START, "00:00", deviceAddress); } - public static Date getDisconnectNotificationEnd() { - return getTimePreference(HuamiConst.PREF_DISCONNECT_NOTIFICATION_END, "00:00"); + public static Date getDisconnectNotificationEnd(String deviceAddress) { + return getTimePreference(HuamiConst.PREF_DISCONNECT_NOTIFICATION_END, "00:00", deviceAddress); } - public static Set getDisplayItems() { - Prefs prefs = GBApplication.getPrefs(); - return prefs.getStringSet(MiBandConst.PREF_MI2_DISPLAY_ITEMS, null); + public static Set getDisplayItems(String deviceAddress) { + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); + return prefs.getStringSet(HuamiConst.PREF_DISPLAY_ITEMS, null); } public static boolean getGoalNotification() { @@ -195,8 +202,8 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return prefs.getBoolean(MiBandConst.PREF_MI2_GOAL_NOTIFICATION, false); } - public static boolean getRotateWristToSwitchInfo() { - Prefs prefs = GBApplication.getPrefs(); + public static boolean getRotateWristToSwitchInfo(String deviceAddress) { + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); return prefs.getBoolean(MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO, false); } @@ -231,28 +238,43 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return getTimePreference(MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND_END, "14:00"); } - public static Date getDoNotDisturbStart() { - return getTimePreference(MiBandConst.PREF_MI2_DO_NOT_DISTURB_START, "01:00"); + public static Date getDoNotDisturbStart(String deviceAddress) { + return getTimePreference(MiBandConst.PREF_DO_NOT_DISTURB_START, "01:00", deviceAddress); } - public static Date getDoNotDisturbEnd() { - return getTimePreference(MiBandConst.PREF_MI2_DO_NOT_DISTURB_END, "06:00"); + public static Date getDoNotDisturbEnd(String deviceAddress) { + return getTimePreference(MiBandConst.PREF_DO_NOT_DISTURB_END, "06:00", deviceAddress); } - public static Date getTimePreference(String key, String defaultValue) { - Prefs prefs = GBApplication.getPrefs(); + public static boolean getBandScreenUnlock(String deviceAddress) { + Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); + return prefs.getBoolean(MiBandConst.PREF_SWIPE_UNLOCK, false); + } + + protected static Date getTimePreference(String key, String defaultValue, String deviceAddress) { + Prefs prefs; + + if (deviceAddress == null) { + prefs = GBApplication.getPrefs(); + } else { + prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); + } String time = prefs.getString(key, defaultValue); DateFormat df = new SimpleDateFormat("HH:mm"); try { return df.parse(time); - } catch(Exception e) { + } catch (Exception e) { LOG.error("Unexpected exception in MiBand2Coordinator.getTime: " + e.getMessage()); } return new Date(); } + protected static Date getTimePreference(String key, String defaultValue) { + return getTimePreference(key, defaultValue, null); + } + public static MiBandConst.DistanceUnit getDistanceUnit() { Prefs prefs = GBApplication.getPrefs(); String unit = prefs.getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, GBApplication.getContext().getString(R.string.p_unit_metric)); @@ -263,18 +285,14 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { } } - public static DoNotDisturb getDoNotDisturb(Context context) { - Prefs prefs = GBApplication.getPrefs(); + public static DoNotDisturb getDoNotDisturb(String deviceAddress) { + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress); - String dndOff = context.getString(R.string.p_off); - String dndAutomatic = context.getString(R.string.p_automatic); - String dndScheduled = context.getString(R.string.p_scheduled); + String pref = prefs.getString(MiBandConst.PREF_DO_NOT_DISTURB, MiBandConst.PREF_DO_NOT_DISTURB_OFF); - String pref = prefs.getString(MiBandConst.PREF_MI2_DO_NOT_DISTURB, dndOff); - - if (dndAutomatic.equals(pref)) { + if (MiBandConst.PREF_DO_NOT_DISTURB_AUTOMATIC.equals(pref)) { return DoNotDisturb.AUTOMATIC; - } else if (dndScheduled.equals(pref)) { + } else if (MiBandConst.PREF_DO_NOT_DISTURB_SCHEDULED.equals(pref)) { return DoNotDisturb.SCHEDULED; } 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 2a5c6d7cc..86fd89ff2 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 @@ -49,6 +49,8 @@ public class HuamiService { // service uuid fee1 public static final UUID UUID_CHARACTERISTIC_AUTH = UUID.fromString("00000009-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_CHARACTERISTIC_CHUNKEDTRANSFER = UUID.fromString("00000020-0000-3512-2118-0009af100700"); @@ -136,6 +138,7 @@ public class HuamiService { public static final byte[] DATEFORMAT_TIME = new byte[] {ENDPOINT_DISPLAY, 0x0a, 0x0, 0x0 }; public static final byte[] DATEFORMAT_TIME_12_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x0 }; public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x1 }; + public static final byte[] DATEFORMAT_DATE_MM_DD_YYYY = new byte[]{ENDPOINT_DISPLAY, 30, 0x00, 'M', 'M', '/', 'd', 'd', '/', 'y', 'y', 'y', 'y'}; public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00}; public static final byte[] COMMAND_SCHEDULE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java index 527f658c6..547a9be75 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java @@ -21,10 +21,12 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; +import androidx.annotation.NonNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import androidx.annotation.NonNull; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -74,4 +76,14 @@ public class AmazfitBipCoordinator extends HuamiCoordinator { public boolean supportsWeather() { return true; } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_amazfitbip, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_disconnectnotification, + R.xml.devicesettings_pairingkey + }; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java index 3163fdaa7..0fa0fdbda 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java @@ -21,10 +21,12 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; +import androidx.annotation.NonNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import androidx.annotation.NonNull; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -77,4 +79,13 @@ public class AmazfitCorCoordinator extends HuamiCoordinator { @Override public boolean supportsUnicodeEmojis() { return true; } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_amazfitcor, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_disconnectnotification, + R.xml.devicesettings_pairingkey}; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java new file mode 100644 index 000000000..963e997fd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2Coordinator.java @@ -0,0 +1,93 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Daniele Gobbetti, João + Paulo Barraca, Matthieu Baerts + + 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.devices.huami.amazfitcor2; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class AmazfitCor2Coordinator extends HuamiCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(AmazfitCor2Coordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.AMAZFITCOR2; + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && (name.equalsIgnoreCase("Amazfit Band 2") || name.equalsIgnoreCase("Amazfit Cor 2"))) { + return DeviceType.AMAZFITCOR2; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + AmazfitCor2FWInstallHandler handler = new AmazfitCor2FWInstallHandler(uri, context); + return handler.isValid() ? handler : null; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public boolean supportsWeather() { + return true; + } + + @Override + public boolean supportsMusicInfo() { + return true; + } + + @Override + public boolean supportsUnicodeEmojis() { + return true; + } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_amazfitcor, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_disconnectnotification, + R.xml.devicesettings_pairingkey}; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWHelper.java new file mode 100644 index 000000000..ef6449c92 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWHelper.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.devices.huami.amazfitcor2; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor2.AmazfitCor2FirmwareInfo; + +public class AmazfitCor2FWHelper extends HuamiFWHelper { + + public AmazfitCor2FWHelper(Uri uri, Context context) throws IOException { + super(uri, context); + } + + @Override + protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { + firmwareInfo = new AmazfitCor2FirmwareInfo(wholeFirmwareBytes); + if (!firmwareInfo.isHeaderValid()) { + throw new IllegalArgumentException("Not a an Amazfit Cor 2 firmware"); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWInstallHandler.java new file mode 100644 index 000000000..0537b9837 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor2/AmazfitCor2FWInstallHandler.java @@ -0,0 +1,49 @@ +/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.devices.huami.amazfitcor2; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +class AmazfitCor2FWInstallHandler extends AbstractMiBandFWInstallHandler { + AmazfitCor2FWInstallHandler(Uri uri, Context context) { + super(uri, context); + } + + @Override + protected String getFwUpgradeNotice() { + return mContext.getString(R.string.fw_upgrade_notice_amazfitcor2, helper.getHumanFirmwareVersion()); + } + + @Override + protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException { + return new AmazfitCor2FWHelper(uri, context); + } + + @Override + protected boolean isSupportedDeviceType(GBDevice device) { + return device.getType() == DeviceType.AMAZFITCOR2; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java index 42cb30f00..add706c68 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java @@ -21,10 +21,12 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; +import androidx.annotation.NonNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import androidx.annotation.NonNull; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; @@ -77,4 +79,15 @@ public class MiBand2Coordinator extends HuamiCoordinator { public boolean supportsWeather() { return false; } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_miband2, + R.xml.devicesettings_donotdisturb_withauto, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_rotatewrist_cycleinfo, + R.xml.devicesettings_pairingkey + }; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java index 6e49bdce5..4c2404159 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java @@ -21,13 +21,15 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.net.Uri; +import androidx.annotation.NonNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import androidx.annotation.NonNull; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; @@ -51,7 +53,7 @@ public class MiBand3Coordinator extends HuamiCoordinator { try { BluetoothDevice device = candidate.getDevice(); String name = device.getName(); - if (name != null && name.equalsIgnoreCase(HuamiConst.MI_BAND3_NAME)) { + if (name != null && (name.equalsIgnoreCase(HuamiConst.MI_BAND3_NAME) || name.equalsIgnoreCase(HuamiConst.MI_BAND3_NAME_2))) { return DeviceType.MIBAND3; } } catch (Exception ex) { @@ -82,22 +84,31 @@ public class MiBand3Coordinator extends HuamiCoordinator { return true; } - public static boolean getBandScreenUnlock() { - Prefs prefs = GBApplication.getPrefs(); - return prefs.getBoolean(MiBandConst.PREF_MI3_BAND_SCREEN_UNLOCK, false); + + public static String getNightMode(String deviceAddress) { + Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); + + return prefs.getString(MiBandConst.PREF_NIGHT_MODE, MiBandConst.PREF_NIGHT_MODE_OFF); } - public static String getNightMode() { - Prefs prefs = GBApplication.getPrefs(); - - return prefs.getString(MiBandConst.PREF_MI3_NIGHT_MODE, MiBandConst.PREF_MI3_NIGHT_MODE_OFF); + public static Date getNightModeStart(String deviceAddress) { + return getTimePreference(MiBandConst.PREF_NIGHT_MODE_START, "16:00", deviceAddress); } - public static Date getNightModeStart() { - return getTimePreference( MiBandConst.PREF_MI3_NIGHT_MODE_START, "16:00"); + public static Date getNightModeEnd(String deviceAddress) { + return getTimePreference(MiBandConst.PREF_NIGHT_MODE_END, "07:00", deviceAddress); } - public static Date getNightModeEnd() { - return getTimePreference(MiBandConst.PREF_MI3_NIGHT_MODE_END, "07:00"); + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_miband3, + R.xml.devicesettings_dateformat, + R.xml.devicesettings_nightmode, + R.xml.devicesettings_donotdisturb_withauto, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_swipeunlock, + R.xml.devicesettings_pairingkey + }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Service.java index 61a841c1d..f4e6a5381 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Service.java @@ -22,7 +22,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.EN import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY_ITEMS; public class MiBand3Service { - public static final byte[] COMMAND_CHANGE_SCREENS = new byte[]{ENDPOINT_DISPLAY_ITEMS, DISPLAY_ITEM_BIT_CLOCK, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00}; + public static final byte[] COMMAND_CHANGE_SCREENS = new byte[]{ENDPOINT_DISPLAY_ITEMS, DISPLAY_ITEM_BIT_CLOCK, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}; public static final byte[] COMMAND_ENABLE_BAND_SCREEN_UNLOCK = new byte[]{ENDPOINT_DISPLAY, 0x16, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_BAND_SCREEN_UNLOCK = new byte[]{ENDPOINT_DISPLAY, 0x16, 0x00, 0x00}; public static final byte[] COMMAND_NIGHT_MODE_OFF = new byte[]{0x1a, 0x00}; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java new file mode 100644 index 000000000..13468b851 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4Coordinator.java @@ -0,0 +1,99 @@ +/* Copyright (C) 2016-2019 Andreas Shimokawa + + 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.devices.huami.miband4; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class MiBand4Coordinator extends HuamiCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(MiBand4Coordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.MIBAND4; + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && name.equalsIgnoreCase(HuamiConst.MI_BAND4_NAME)) { + return DeviceType.MIBAND4; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + + } + + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + MiBand4FWInstallHandler handler = new MiBand4FWInstallHandler(uri, context); + return handler.isValid() ? handler : null; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public boolean supportsWeather() { + return true; + } + + @Override + public boolean supportsActivityTracks() { + return true; + } + + @Override + public boolean supportsMusicInfo() { + return true; + } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_miband3, + R.xml.devicesettings_dateformat, + R.xml.devicesettings_nightmode, + R.xml.devicesettings_donotdisturb_withauto, + R.xml.devicesettings_liftwrist_display, + R.xml.devicesettings_swipeunlock, + R.xml.devicesettings_pairingkey + }; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWHelper.java new file mode 100644 index 000000000..ed2b17821 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWHelper.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.devices.huami.miband4; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4FirmwareInfo; + +public class MiBand4FWHelper extends HuamiFWHelper { + + public MiBand4FWHelper(Uri uri, Context context) throws IOException { + super(uri, context); + } + + @Override + protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { + firmwareInfo = new MiBand4FirmwareInfo(wholeFirmwareBytes); + if (!firmwareInfo.isHeaderValid()) { + throw new IllegalArgumentException("Not a Mi Band 4 firmware"); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWInstallHandler.java new file mode 100644 index 000000000..f8f09a8cc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband4/MiBand4FWInstallHandler.java @@ -0,0 +1,50 @@ +/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.devices.huami.miband4; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3FWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +class MiBand4FWInstallHandler extends AbstractMiBandFWInstallHandler { + MiBand4FWInstallHandler(Uri uri, Context context) { + super(uri, context); + } + + @Override + protected String getFwUpgradeNotice() { + return mContext.getString(R.string.fw_upgrade_notice_miband4, helper.getHumanFirmwareVersion()); + } + + @Override + protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException { + return new MiBand4FWHelper(uri, context); + } + + @Override + protected boolean isSupportedDeviceType(GBDevice device) { + return device.getType() == DeviceType.MIBAND4; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16Constants.java new file mode 100644 index 000000000..589344d09 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16Constants.java @@ -0,0 +1,127 @@ +/* Copyright (C) 2019 Sophanimus + + 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.devices.jyou; + +import java.util.UUID; + +import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID; + +public final class BFH16Constants { + + //Known Services + public static final UUID BFH16_GENERIC_ACCESS_SERVICE = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_GENERIC_ARTTRIBUTE_SERVICE = UUID.fromString("00001801-0000-1000-8000-00805f9b34fb"); + + public static final UUID BFH16_IDENTIFICATION_SERVICE1 = UUID.fromString("0000fef5-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_IDENTIFICATION_SERVICE2 = UUID.fromString("0000fee7-0000-1000-8000-00805f9b34fb"); + + + public static final UUID BFH16_SERVICE1 = UUID.fromString("000056ff-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_SERVICE1_WRITE = UUID.fromString("000033f3-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_SERVICE1_NOTIFY = UUID.fromString("000033f4-0000-1000-8000-00805f9b34fb"); + + public static final UUID BFH16_SERVICE2 = UUID.fromString("0000fee7-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_SERVICE2_WRITE = UUID.fromString("0000fec7-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_SERVICE2_INDICATE= UUID.fromString("0000fec8-0000-1000-8000-00805f9b34fb"); + public static final UUID BFH16_SERVICE2_READ = UUID.fromString("0000fec9-0000-1000-8000-00805f9b34fb"); + + + //Verified command bytes + public static final byte CMD_SET_ALARM_1 = (byte)0x09; + public static final byte CMD_SET_ALARM_2 = (byte)0x22; + public static final byte CMD_SET_ALARM_3 = (byte)0x23; + + public static final byte CMD_SET_DATE_AND_TIME = 0x08; + + + public static final byte CMD_MEASURE_HEART = (byte)0x0D; //param1: 0, param2: 0 -> STOP | 1 -> START + public static final byte CMD_VIBRATE = (byte)0x07; //param1: 0, param2: 1 + + + public static final byte CMD_SWITCH_PHOTO_MODE = (byte)0x25; //param1: 0, param2: 0 -> OFF | 1 -> ON + public static final byte CMD_SWITCH_12HOUR_MODE = (byte)0x3E; //byte1: 1 -> 12HourMode | 0 -> 24HourMode + public static final byte CMD_SWITCH_METRIC_IMPERIAL = (byte)0x3A; //param1: 0, param2: 0 -> METRIC | 1 -> IMPERIAL //Also requests walked steps + + + //Verified receive bytes + public static final byte RECEIVE_DEVICE_INFO = (byte)0xF6; + public static final byte RECEIVE_BATTERY_LEVEL = (byte)0xF7; + public static final byte RECEIVE_STEPS_DATA = (byte)0xF9; + public static final byte RECEIVE_HEART_DATA = (byte)0xE8; + public static final byte RECEIVE_PHOTO_TRIGGER = (byte)0xF3; + + //Verified icon bytes + public static final byte ICON_CALL = (byte)0x00; + public static final byte ICON_SMS = (byte)0x01; + public static final byte ICON_WECHAT = (byte)0x02; + public static final byte ICON_QQ = (byte)0x03; + public static final byte ICON_FACEBOOK = (byte)0x04; + public static final byte ICON_SKYPE = (byte)0x05; + public static final byte ICON_TWITTER = (byte)0x06; + public static final byte ICON_WHATSAPP = (byte)0x07; + public static final byte ICON_LINE = (byte)0x08; + public static final byte ICON_TALK = (byte)0x09; + public static final byte ICON_RUNNER = (byte)0x0A; + + + + //Most probably correct command bytes + public static final byte CMD_SET_STEPLENGTH = (byte)0x3F; //param1: 0, param2: STEPLENGTH + + + //Probably correct command bytes + public static final byte CMD_SET_INACTIVITY_WARNING_TIME = (byte)0x24; //param1: 0, param2: time + + public static final byte CMD_SET_HEART_TARGET = (byte)0x01; //param1: 0, param2: HEART TARGET + public static final byte CMD_SET_STEP_TARGET = (byte)0x03; //param1: 0, param2: STEP TARGET + + public static final byte CMD_FIND_DEVICE = (byte)0x36; //param1: 0, param2: 1 + public static final byte CMD_SET_DISCONNECT_REMIND = (byte)0x37; //param1: 0, param2: 0 -> ??? | 1 -> ??? + public static final byte CMD_SET_AUTODETECT_HEART = (byte)0x38; //param1: 0, param2: 0 -> ??? | 1 -> ??? + + public static final byte CMD_READ_HISTORY_SLEEP_COUNT = (byte)0x32; //param1: 0, param2: 0 + + public static final byte CMD_SET_NOON_TIME = (byte)0x26; //param1: start time, param2: end time + public static final byte CMD_SET_SLEEP_TIME = (byte)0x27; //param1: start time, param2: end time + + + //Could be correct command bytes + //Send PhoneName 0x17 and 0x18 + //Send PhoneNumber 0x19 and 0x20 + //Weather 0x3B + //Power Management 0x39 + //User Id 0x35 + // + + + + //______________________________________________________________________________________________ + //It may be that BFH16 uses the same communication protocol as JYOU + //copied the following JYOU vars: + + public static final byte CMD_SET_HEARTRATE_AUTO = 0x38; + public static final byte CMD_SET_HEARTRATE_WARNING_VALUE = 0x01; + public static final byte CMD_SET_TARGET_STEPS = 0x03; + //public static final byte CMD_GET_STEP_COUNT = 0x1D; + public static final byte CMD_GET_SLEEP_TIME = 0x32; + public static final byte CMD_SET_DND_SETTINGS = 0x39; + + public static final byte CMD_ACTION_HEARTRATE_SWITCH = 0x0D; + public static final byte CMD_ACTION_SHOW_NOTIFICATION = 0x2C; + public static final byte CMD_ACTION_REBOOT_DEVICE = 0x0E; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java new file mode 100644 index 000000000..020a38485 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/BFH16DeviceCoordinator.java @@ -0,0 +1,213 @@ +/* Copyright (C) 2019 Sophanimus + + 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.devices.jyou; + +import android.app.Activity; +import android.bluetooth.le.ScanFilter; +import android.content.Context; +import android.net.Uri; +import android.os.ParcelUuid; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; + +import androidx.annotation.NonNull; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class BFH16DeviceCoordinator extends AbstractDeviceCoordinator +{ + protected static final Logger LOG = LoggerFactory.getLogger(BFH16DeviceCoordinator.class); + + + @Override + public DeviceType getDeviceType() { + return DeviceType.BFH16; + } + + @Override + public String getManufacturer() { + return "Denver"; + } + + @Override + public Collection createBLEScanFilters() { + + ParcelUuid bfhService1 = new ParcelUuid(BFH16Constants.BFH16_IDENTIFICATION_SERVICE1); + ParcelUuid bfhService2 = new ParcelUuid(BFH16Constants.BFH16_IDENTIFICATION_SERVICE2); + + ScanFilter filter = new ScanFilter.Builder() + .setServiceUuid(bfhService1) + .setServiceUuid(bfhService2) + .build(); + + return Collections.singletonList(filter); + } + + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + + String name = candidate.getDevice().getName(); + if (name != null) { + if (name.startsWith("BFH-16")) { + return DeviceType.BFH16; + } + } + + return DeviceType.UNKNOWN; + + } + + @Override + public int getBondingStyle(GBDevice deviceCandidate){ + return BONDING_STYLE_NONE; + } + + @Override + public Class getPairingActivity(){ + return null; + } + + //Additional required functions ______________________________________ + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public Class getAppsManagementActivity() + { + return null; + } + + + //Supported ________________________________________________________ + + @Override + public int getAlarmSlotCount() + { + return 3; + } + + @Override + public boolean supportsFindDevice() + { + return true; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) + { + return true; + } + + @Override + public boolean supportsRealtimeData() + { + return true; + } + + + //NOT Supported ________________________________________________________ + + @Override + public boolean supportsActivityDataFetching(){ + return false; + } + + @Override + public boolean supportsActivityTracking() + { + return false; + } + + @Override + public boolean supportsAppsManagement() + { + return false; + } + + @Override + public boolean supportsCalendarEvents() + { + return false; + } + + @Override + public boolean supportsLedColor() + { + return false; + } + + @Override + public boolean supportsMusicInfo() + { + return false; + } + + @Override + public boolean supportsRgbLedColor() + { + return false; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) + { + return false; + } + + @Override + public boolean supportsWeather() + { + return false; + } + + + + + + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java index 6f215bfeb..735c4eaed 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java @@ -51,7 +51,7 @@ public abstract class AbstractMiBandFWHelper { } try (InputStream in = new BufferedInputStream(uriHelper.openInputStream())) { - this.fw = FileUtils.readAll(in, 1024 * 1536); // 1.5 MB + this.fw = FileUtils.readAll(in, 1024 * 2048); // 2.0 MB determineFirmwareInfo(fw); } catch (IOException ex) { throw ex; // pass through 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 29608fc45..ff410a9f9 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 @@ -39,24 +39,23 @@ public final class MiBandConst { public static final String PREF_MIBAND_BUTTON_ACTION_DELAY = "mi_button_press_count_match_delay"; public static final String PREF_MIBAND_BUTTON_PRESS_BROADCAST = "mi_button_press_broadcast"; 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 PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "device_time_offset_hours"; public static final String PREF_MI2_DATEFORMAT = "mi2_dateformat"; public static final String PREF_MI2_GOAL_NOTIFICATION = "mi2_goal_notification"; - public static final String PREF_MI2_DISPLAY_ITEMS = "mi2_display_items"; public static final String PREF_MI2_DISPLAY_ITEM_CLOCK = "clock"; public static final String PREF_MI2_DISPLAY_ITEM_STEPS = "steps"; public static final String PREF_MI2_DISPLAY_ITEM_DISTANCE = "distance"; public static final String PREF_MI2_DISPLAY_ITEM_CALORIES = "calories"; public static final String PREF_MI2_DISPLAY_ITEM_HEART_RATE = "heart_rate"; public static final String PREF_MI2_DISPLAY_ITEM_BATTERY = "battery"; - public static final String PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO = "mi2_rotate_wrist_to_switch_info"; + public static final String PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO = "rotate_wrist_to_cycle_info"; public static final String PREF_MI2_ENABLE_TEXT_NOTIFICATIONS = "mi2_enable_text_notifications"; - public static final String PREF_MI2_DO_NOT_DISTURB = "mi2_do_not_disturb"; - public static final String PREF_MI2_DO_NOT_DISTURB_OFF = "off"; - public static final String PREF_MI2_DO_NOT_DISTURB_AUTOMATIC = "automatic"; - public static final String PREF_MI2_DO_NOT_DISTURB_SCHEDULED = "scheduled"; - public static final String PREF_MI2_DO_NOT_DISTURB_START = "mi2_do_not_disturb_start"; - public static final String PREF_MI2_DO_NOT_DISTURB_END = "mi2_do_not_disturb_end"; + public static final String PREF_DO_NOT_DISTURB = "do_not_disturb"; + public static final String PREF_DO_NOT_DISTURB_OFF = "off"; + 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_DO_NOT_DISTURB_START = "do_not_disturb_start"; + public static final String PREF_DO_NOT_DISTURB_END = "do_not_disturb_end"; public static final String PREF_MI2_INACTIVITY_WARNINGS = "mi2_inactivity_warnings"; public static final String PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD = "mi2_inactivity_warnings_threshold"; public static final String PREF_MI2_INACTIVITY_WARNINGS_START = "mi2_inactivity_warnings_start"; @@ -66,13 +65,13 @@ public final class MiBandConst { public static final String PREF_MI2_INACTIVITY_WARNINGS_DND_END = "mi2_inactivity_warnings_dnd_end"; public static final String PREF_MIBAND_SETUP_BT_PAIRING = "mi_setup_bt_pairing"; - public static final String PREF_MI3_BAND_SCREEN_UNLOCK = "mi3_band_screen_unlock"; - public static final String PREF_MI3_NIGHT_MODE = "mi3_night_mode"; - public static final String PREF_MI3_NIGHT_MODE_START = "mi3_night_mode_start"; - public static final String PREF_MI3_NIGHT_MODE_END = "mi3_night_mode_end"; - public static final String PREF_MI3_NIGHT_MODE_OFF = "off"; - public static final String PREF_MI3_NIGHT_MODE_SUNSET = "sunset"; - public static final String PREF_MI3_NIGHT_MODE_SCHEDULED = "scheduled"; + public static final String PREF_SWIPE_UNLOCK = "swipe_unlock"; + public static final String PREF_NIGHT_MODE = "night_mode"; + public static final String PREF_NIGHT_MODE_START = "night_mode_start"; + public static final String PREF_NIGHT_MODE_END = "night_mode_end"; + public static final String PREF_NIGHT_MODE_OFF = "off"; + public static final String PREF_NIGHT_MODE_SUNSET = "sunset"; + public static final String PREF_NIGHT_MODE_SCHEDULED = "scheduled"; public static final String ORIGIN_INCOMING_CALL = "incoming_call"; public static final String ORIGIN_ALARM_CLOCK = "alarm_clock"; 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 68806ea59..fd8154294 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 @@ -22,6 +22,7 @@ import android.app.Activity; import android.bluetooth.BluetoothDevice; import android.bluetooth.le.ScanFilter; import android.content.Context; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.ParcelUuid; @@ -36,6 +37,7 @@ import androidx.annotation.NonNull; import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; @@ -235,8 +237,8 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return location; } - public static int getDeviceTimeOffsetHours() throws IllegalArgumentException { - Prefs prefs = GBApplication.getPrefs(); + public static int getDeviceTimeOffsetHours(String deviceAddress) throws IllegalArgumentException { + Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); return prefs.getInt(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS, 0); } @@ -256,6 +258,14 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return isMi1S(hwVersion) || isMiPro(hwVersion); } + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_lowlatency_fwupdate, + R.xml.devicesettings_fake_timeoffset + }; + } + private boolean isMi1S(String hardwareVersion) { return MiBandConst.MI_1S.equals(hardwareVersion); } 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 9b9802ccc..3283c6531 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 @@ -34,9 +34,9 @@ public class MiBandDateConverter { * @param value * @return */ - public static GregorianCalendar rawBytesToCalendar(byte[] value) { + public static GregorianCalendar rawBytesToCalendar(byte[] value, String deviceAddress) { if (value.length == 6) { - return rawBytesToCalendar(value, 0); + return rawBytesToCalendar(value, 0, deviceAddress); } return createCalendar(); } @@ -47,7 +47,7 @@ public class MiBandDateConverter { * @param value * @return */ - public static GregorianCalendar rawBytesToCalendar(byte[] value, int offset) { + public static GregorianCalendar rawBytesToCalendar(byte[] value, int offset, String deviceAddress) { if (value.length - offset >= 6) { GregorianCalendar timestamp = new GregorianCalendar( value[offset] + 2000, @@ -57,7 +57,7 @@ public class MiBandDateConverter { value[offset + 4], value[offset + 5]); - int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); + int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(deviceAddress); if(offsetInHours != 0) timestamp.add(Calendar.HOUR_OF_DAY,-offsetInHours); @@ -73,7 +73,7 @@ public class MiBandDateConverter { * @param timestamp * @return */ - public static byte[] calendarToRawBytes(Calendar timestamp) { + public static byte[] calendarToRawBytes(Calendar timestamp, String deviceAddress) { // The mi-band device currently records sleep // only if it happens after 10pm and before 7am. @@ -82,7 +82,7 @@ public class MiBandDateConverter { // 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(); + int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(deviceAddress); if(offsetInHours != 0) timestamp.add(Calendar.HOUR_OF_DAY,offsetInHours); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 7d99e1c02..4671d5e62 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -23,16 +23,19 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.widget.TextView; import android.widget.Toast; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; @@ -116,7 +119,7 @@ public class MiBandPairingActivity extends AbstractGBActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mi_band_pairing); - message = (TextView) findViewById(R.id.miband_pair_message); + message = findViewById(R.id.miband_pair_message); Intent intent = getIntent(); deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE); if (deviceCandidate == null && savedInstanceState != null) { @@ -129,6 +132,21 @@ public class MiBandPairingActivity extends AbstractGBActivity { return; } + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(deviceCandidate); + GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate); + + if (coordinator.getSupportedDeviceSpecificSettings(device) != null) { // FIXME: this will no longer be sane in the future + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()); + String authKey = sharedPrefs.getString("authkey", null); + if (authKey == null || authKey.isEmpty()) { + SharedPreferences.Editor editor = sharedPrefs.edit(); + + String randomAuthkey = RandomStringUtils.random(16, true, true); + editor.putString("authkey", randomAuthkey); + editor.apply(); + } + } + if (!MiBandCoordinator.hasValidUserInfo()) { Intent userSettingsIntent = new Intent(this, MiBandPreferencesActivity.class); startActivityForResult(userSettingsIntent, REQ_CODE_USER_SETTINGS, null); 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 e75ff4d44..ad5993c31 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 @@ -41,11 +41,8 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISPLAY_ON_LIFT_START; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_ALARM_CLOCK; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_INCOMING_CALL; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_END; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_OFF; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_SCHEDULED; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB_START; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_OFF; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DO_NOT_DISTURB_SCHEDULED; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_GOAL_NOTIFICATION; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_DND; @@ -56,7 +53,6 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO; 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_RESERVE_ALARM_FOR_CALENDAR; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS; @@ -106,37 +102,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { } }); - final Preference activateDisplayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT); - activateDisplayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); - } - }); - return true; - } - }); - - String displayOnLiftState = prefs.getString(PREF_ACTIVATE_DISPLAY_ON_LIFT, PREF_MI2_DO_NOT_DISTURB_OFF); - boolean displayOnLiftScheduled = displayOnLiftState.equals(PREF_MI2_DO_NOT_DISTURB_SCHEDULED); - - final Preference rotateWristCycleInfo = findPreference(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); - rotateWristCycleInfo.setEnabled(!PREF_MI2_DO_NOT_DISTURB_OFF.equals(displayOnLiftState)); - rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); - } - }); - return true; - } - }); final Preference inactivityWarnings = findPreference(PREF_MI2_INACTIVITY_WARNINGS); inactivityWarnings.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -236,108 +201,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { } }); - String doNotDisturbState = prefs.getString(MiBandConst.PREF_MI2_DO_NOT_DISTURB, PREF_MI2_DO_NOT_DISTURB_OFF); - boolean doNotDisturbScheduled = doNotDisturbState.equals(PREF_MI2_DO_NOT_DISTURB_SCHEDULED); - - final Preference doNotDisturbStart = findPreference(PREF_MI2_DO_NOT_DISTURB_START); - doNotDisturbStart.setEnabled(doNotDisturbScheduled); - doNotDisturbStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DO_NOT_DISTURB_START); - } - }); - return true; - } - }); - - final Preference doNotDisturbEnd = findPreference(PREF_MI2_DO_NOT_DISTURB_END); - doNotDisturbEnd.setEnabled(doNotDisturbScheduled); - doNotDisturbEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DO_NOT_DISTURB_END); - } - }); - return true; - } - }); - - final Preference doNotDisturb = findPreference(PREF_MI2_DO_NOT_DISTURB); - doNotDisturb.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - final boolean scheduled = PREF_MI2_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); - - doNotDisturbStart.setEnabled(scheduled); - doNotDisturbEnd.setEnabled(scheduled); - - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_DO_NOT_DISTURB); - } - }); - return true; - } - }); - - final Preference displayOnLiftStart = findPreference(PREF_DISPLAY_ON_LIFT_START); - displayOnLiftStart.setEnabled(displayOnLiftScheduled); - displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START); - } - }); - return true; - } - }); - - - final Preference displayOnLiftEnd = findPreference(PREF_DISPLAY_ON_LIFT_END); - displayOnLiftEnd.setEnabled(displayOnLiftScheduled); - displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END); - } - }); - return true; - } - }); - - - final Preference displayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT); - displayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newVal) { - final boolean scheduled = PREF_MI2_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); - - displayOnLiftStart.setEnabled(scheduled); - displayOnLiftEnd.setEnabled(scheduled); - rotateWristCycleInfo.setEnabled(!PREF_MI2_DO_NOT_DISTURB_OFF.equals(newVal.toString())); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); - } - }); - return true; - } - }); final Preference fitnessGoal = findPreference(ActivityUser.PREF_USER_STEPS_GOAL); fitnessGoal.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -408,7 +271,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { prefKeys.add(PREF_MIBAND_ADDRESS); prefKeys.add(ActivityUser.PREF_USER_STEPS_GOAL); prefKeys.add(PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR); - prefKeys.add(PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS); prefKeys.add(PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_ALARM_CLOCK)); prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java new file mode 100644 index 000000000..d0bcdee58 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/mijia_lywsd02/MijiaLywsd02Coordinator.java @@ -0,0 +1,140 @@ +/* Copyright (C) 2019 Andreas Shimokawa + + 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.devices.mijia_lywsd02; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class MijiaLywsd02Coordinator extends AbstractDeviceCoordinator { + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if (name != null && name.equals("LYWSD02")) { + return DeviceType.MIJIA_LYWSD02; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.MIJIA_LYWSD02; + } + + @Override + public int getBondingStyle(GBDevice deviceCandidate) { + return BONDING_STYLE_NONE; + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public int getAlarmSlotCount() { + return 0; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public String getManufacturer() { + return "Xiaomi"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } + + @Override + public boolean supportsFindDevice() { + return false; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) { + // nothing to delete, yet + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java index b64560e16..c2308026f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miscale2/MiScale2DeviceCoordinator.java @@ -1,3 +1,20 @@ +/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Jean-François Greffier, Vadim Kaushan + + 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.devices.miscale2; import android.annotation.TargetApi; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java index c19833900..70cdbe1e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -22,7 +22,7 @@ import android.content.Context; import android.net.Uri; import androidx.annotation.NonNull; -import nodomain.freeyourgadget.gadgetbridge.GBException; + import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; @@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class VibratissimoCoordinator extends AbstractDeviceCoordinator { + @NonNull @Override public DeviceType getSupportedType(GBDeviceCandidate candidate) { String name = candidate.getDevice().getName(); @@ -129,7 +130,7 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator { } @Override - protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) { // nothing to delete, yet } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimePreferenceActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimePreferenceActivity.java index 398fc7a1c..da9d9f57b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimePreferenceActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/zetime/ZeTimePreferenceActivity.java @@ -1,16 +1,27 @@ +/* Copyright (C) 2018-2019 Sebastian Kranz + + 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.devices.zetime; import android.os.Bundle; import android.preference.Preference; -import android.widget.Toast; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractSettingsActivity; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; -import nodomain.freeyourgadget.gadgetbridge.util.GB; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class ZeTimePreferenceActivity extends AbstractSettingsActivity { @Override @@ -18,14 +29,9 @@ public class ZeTimePreferenceActivity extends AbstractSettingsActivity { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.zetime_preferences); - addPreferencesFromResource(R.xml.preferences); GBApplication.deviceService().onReadConfiguration("do_it"); - //addTryListeners(); - - Prefs prefs = GBApplication.getPrefs(); - final Preference heartrateMeasurementInterval = findPreference(ZeTimeConstants.PREF_ZETIME_HEARTRATE_INTERVAL); heartrateMeasurementInterval.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java index 24389f0f7..9eae06913 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java @@ -1,5 +1,5 @@ /* Copyright (C) 2017-2019 AndrewH, Carsten Pfeiffer, Daniele Gobbetti, - Dikay900 + Dikay900, Nick Spacek This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java index dfedbbf16..e1d098bf0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -23,6 +23,7 @@ import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.media.AudioManager; import android.telephony.TelephonyManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -34,12 +35,23 @@ public class PhoneCallReceiver extends BroadcastReceiver { private static int mLastState = TelephonyManager.CALL_STATE_IDLE; private static String mSavedNumber; + private boolean mRestoreMutedCall = false; + private int mLastRingerMode; @Override public void onReceive(Context context, Intent intent) { TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE); if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) { mSavedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"); + } else if(intent.getAction().equals("nodomain.freeyourgadget.gadgetbridge.MUTE_CALL")) { + // Handle the mute request only if the phone is currently ringing + if(mLastState != TelephonyManager.CALL_STATE_RINGING) + return; + + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mLastRingerMode = audioManager.getRingerMode(); + audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT); + mRestoreMutedCall = true; } else { if (intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)) { String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); @@ -75,6 +87,11 @@ public class PhoneCallReceiver extends BroadcastReceiver { } else { callCommand = CallSpec.CALL_END; } + if(mRestoreMutedCall) { + mRestoreMutedCall = false; + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + audioManager.setRingerMode(mLastRingerMode); + } break; } if (callCommand != CallSpec.CALL_UNDEFINED) { 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 911683ddb..35a8fa86e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2019 Alberto, Andreas Shimokawa, Carsten Pfeiffer, criogenic, dakhnod, Daniele Gobbetti, Frank Slezak, ivanovlev, José Rebelo, - Julien Pivotto, Kasha, Roi Greenberg, Steffen Liebergeld + Julien Pivotto, Kasha, Roi Greenberg, Sebastian Kranz, Steffen Liebergeld This file is part of Gadgetbridge. @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.provider.ContactsContract; import java.util.ArrayList; @@ -406,7 +407,12 @@ public class GBDeviceService implements DeviceService { * @return contact DisplayName, if found it */ private String getContactDisplayNameByNumber(String number) { - Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); + Uri uri; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(number)); + } else { + uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)); + } String name = number; if (number == null || number.equals("")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java index a3319aefa..4138fe32d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java @@ -116,9 +116,9 @@ public class ActivityKind { case TYPE_NOT_MEASURED: return R.drawable.ic_activity_not_measured; case TYPE_LIGHT_SLEEP: - return R.drawable.ic_activity_light_sleep; + return R.drawable.ic_activity_sleep; case TYPE_DEEP_SLEEP: - return R.drawable.ic_activity_deep_sleep; + return R.drawable.ic_activity_sleep; case TYPE_RUNNING: return R.drawable.ic_activity_running; case TYPE_WALKING: @@ -128,7 +128,9 @@ public class ActivityKind { case TYPE_TREADMILL: return R.drawable.ic_activity_walking; case TYPE_EXERCISE: // fall through + return R.drawable.ic_activity_exercise; case TYPE_SWIMMING: // fall through + return R.drawable.ic_activity_swimming; case TYPE_NOT_WORN: // fall through case TYPE_ACTIVITY: // fall through case TYPE_UNKNOWN: // fall through diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java index a2dacabcc..d1ecb3013 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -1,5 +1,5 @@ /* Copyright (C) 2016-2019 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Daniele Gobbetti + Daniele Gobbetti, Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java index b6fe8db04..2aff0bb14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java @@ -40,6 +40,7 @@ public class AppNotificationType extends HashMap { put("com.android.email", NotificationType.GENERIC_EMAIL); put("ch.protonmail.android", NotificationType.GENERIC_EMAIL); put("security.pEp", NotificationType.GENERIC_EMAIL); + put("eu.faircode.email", NotificationType.GENERIC_EMAIL); // Generic SMS put("com.moez.QKSMS", NotificationType.GENERIC_SMS); 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 45372c59f..384ec39f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, dakhnod, Daniele Gobbetti, Frank Slezak, ivanovlev, JohnnySun, José Rebelo, Julien - Pivotto, Kasha, Steffen Liebergeld + Pivotto, Kasha, Sebastian Kranz, Steffen Liebergeld This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 0a631c4bb..8c6e68c64 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -1,7 +1,7 @@ /* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten - Pfeiffer, Daniele Gobbetti, João Paulo Barraca, José Rebelo, Kranz, - ladbsoft, maxirnilian, protomors, Quallenauge, Sami Alaoui, tiparega, - Vadim Kaushan + Pfeiffer, Daniele Gobbetti, Jean-François Greffier, João Paulo Barraca, + José Rebelo, Kranz, ladbsoft, maxirnilian, protomors, Quallenauge, Sami + Alaoui, Sebastian Kranz, Sophanimus, tiparega, Vadim Kaushan This file is part of Gadgetbridge. @@ -37,6 +37,8 @@ public enum DeviceType { AMAZFITBIP(12, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_amazfit_bip), AMAZFITCOR(13, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_amazfit_cor), MIBAND3(14, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband3), + AMAZFITCOR2(15, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_amazfit_cor2), + MIBAND4(16, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband4), VIBRATISSIMO(20, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo), LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview), HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus), @@ -55,6 +57,8 @@ public enum DeviceType { ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3), CASIOGB6900(120, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_casiogb6900), MISCALE2(131, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_miscale2), + BFH16(140, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_bfh16), + MIJIA_LYWSD02(200, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_mijia_lywsd02), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index 69d6ed0f7..859fb8271 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -198,6 +198,13 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { private void handleGBDeviceEvent(GBDeviceEventCallControl callEvent) { Context context = getContext(); LOG.info("Got event for CALL_CONTROL"); + if(callEvent.event == GBDeviceEventCallControl.Event.IGNORE) { + LOG.info("Sending intent for mute"); + Intent broadcastIntent = new Intent("nodomain.freeyourgadget.gadgetbridge.MUTE_CALL"); + broadcastIntent.setPackage(context.getPackageName()); + context.sendBroadcast(broadcastIntent); + return; + } Intent callIntent = new Intent(GBCallControlReceiver.ACTION_CALLCONTROL); callIntent.putExtra("event", callEvent.event.ordinal()); callIntent.setPackage(context.getPackageName()); 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 2ab72ff69..1a7876332 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -1,7 +1,8 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, Avamander, Carsten Pfeiffer, dakhnod, Daniele Gobbetti, Daniel Hauck, Dikay900, Frank Slezak, ivanovlev, João Paulo Barraca, José Rebelo, Julien Pivotto, Kasha, Martin, Matthieu - Baerts, Sergey Trofimov, Steffen Liebergeld, Taavi Eomäe, Uwe Hermann + Baerts, Sebastian Kranz, Sergey Trofimov, Steffen Liebergeld, Taavi Eomäe, + Uwe Hermann This file is part of Gadgetbridge. @@ -685,6 +686,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.PHONE_STATE"); filter.addAction("android.intent.action.NEW_OUTGOING_CALL"); + filter.addAction("nodomain.freeyourgadget.gadgetbridge.MUTE_CALL"); registerReceiver(mPhoneCallReceiver, filter); } if (mSMSReceiver == null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 0d01858c9..dfa4e83ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -1,7 +1,7 @@ -/* Copyright (C) 2015-2019 0nse, Andreas Böhler, Andreas Shimokawa, - Carsten Pfeiffer, Daniele Gobbetti, João Paulo Barraca, José Rebelo, Kranz, - ladbsoft, maxirnilian, protomors, Quallenauge, Sami Alaoui, Sergey Trofimov, - tiparega, Vadim Kaushan +/* Copyright (C) 2015-2019 0nse, Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, criogenic, Daniele Gobbetti, Jean-François Greffier, João Paulo + Barraca, José Rebelo, Kranz, ladbsoft, maxirnilian, protomors, Quallenauge, + Sami Alaoui, Sergey Trofimov, Sophanimus, tiparega, Vadim Kaushan This file is part of Gadgetbridge. @@ -35,11 +35,15 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor2.AmazfitCor2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.id115.ID115Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.BFH16DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.mijia_lywsd02.MijiaLywsd02Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.miscale2.MiScale2DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; @@ -54,13 +58,13 @@ public class DeviceSupportFactory { private final BluetoothAdapter mBtAdapter; private final Context mContext; - public DeviceSupportFactory(Context context) { + DeviceSupportFactory(Context context) { mContext = context; mBtAdapter = BluetoothAdapter.getDefaultAdapter(); } public synchronized DeviceSupport createDeviceSupport(GBDevice device) throws GBException { - DeviceSupport deviceSupport = null; + DeviceSupport deviceSupport; String deviceAddress = device.getAddress(); int indexFirstColon = deviceAddress.indexOf(":"); if (indexFirstColon > 0) { @@ -126,12 +130,18 @@ public class DeviceSupportFactory { case MIBAND3: deviceSupport = new ServiceDeviceSupport(new MiBand3Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case MIBAND4: + deviceSupport = new ServiceDeviceSupport(new MiBand4Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case AMAZFITBIP: deviceSupport = new ServiceDeviceSupport(new AmazfitBipSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; case AMAZFITCOR: deviceSupport = new ServiceDeviceSupport(new AmazfitCorSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case AMAZFITCOR2: + deviceSupport = new ServiceDeviceSupport(new AmazfitCor2Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case VIBRATISSIMO: deviceSupport = new ServiceDeviceSupport(new VibratissimoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; @@ -177,9 +187,14 @@ public class DeviceSupportFactory { case CASIOGB6900: deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; - case MISCALE2: + case MISCALE2: deviceSupport = new ServiceDeviceSupport(new MiScale2DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case BFH16: + deviceSupport = new ServiceDeviceSupport(new BFH16DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case MIJIA_LYWSD02: + deviceSupport = new ServiceDeviceSupport(new MijiaLywsd02Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); 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 d023c42a7..b1f48866a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -1,5 +1,6 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, José Rebelo, Julien Pivotto, Kasha, Steffen Liebergeld + Gobbetti, José Rebelo, Julien Pivotto, Kasha, Sebastian Kranz, Steffen + Liebergeld This file is part of Gadgetbridge. 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 bdad45b77..04f8d1cd1 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 @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, JohnnySun, José Rebelo +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti, JohnnySun, José Rebelo This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractTransaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractTransaction.java index 0ab0d611c..ce22a57ba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractTransaction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractTransaction.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Andreas Boehler +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index fdc77d61f..4c8bb8d77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -40,22 +40,7 @@ public class BLETypeConversions { * @return * @see GattCharacteristic#UUID_CHARACTERISTIC_CURRENT_TIME */ - public static byte[] calendarToRawBytes(Calendar timestamp, boolean honorDeviceTimeOffset) { - - // 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. - if (honorDeviceTimeOffset) { - int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); - if (offsetInHours != 0) { - timestamp.add(Calendar.HOUR_OF_DAY, offsetInHours); - } - } - + public static byte[] calendarToRawBytes(Calendar timestamp) { // MiBand2: // year,year,month,dayofmonth,hour,minute,second,dayofweek,0,0,tz @@ -78,25 +63,9 @@ public class BLETypeConversions { /** * Similar to calendarToRawBytes, but only up to (and including) the MINUTES. * @param timestamp - * @param honorDeviceTimeOffset * @return */ - public static byte[] shortCalendarToRawBytes(Calendar timestamp, boolean honorDeviceTimeOffset) { - - // 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. - if (honorDeviceTimeOffset) { - int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); - if (offsetInHours != 0) { - timestamp.add(Calendar.HOUR_OF_DAY, offsetInHours); - } - } - + public static byte[] shortCalendarToRawBytes(Calendar timestamp) { // MiBand2: // year,year,month,dayofmonth,hour,minute @@ -136,7 +105,7 @@ public class BLETypeConversions { * @param value * @return */ - public static GregorianCalendar rawBytesToCalendar(byte[] value, boolean honorDeviceTimeOffset) { + public static GregorianCalendar rawBytesToCalendar(byte[] value) { if (value.length >= 7) { int year = toUint16(value[0], value[1]); GregorianCalendar timestamp = new GregorianCalendar( @@ -153,14 +122,6 @@ public class BLETypeConversions { timeZone.setRawOffset(value[7] * 15 * 60 * 1000); timestamp.setTimeZone(timeZone); } - - if (honorDeviceTimeOffset) { - int offsetInHours = MiBandCoordinator.getDeviceTimeOffsetHours(); - if (offsetInHours != 0) { - timestamp.add(Calendar.HOUR_OF_DAY,-offsetInHours); - } - } - return timestamp; } 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 e0d6941f4..52b6e96d3 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 @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Sergey Trofimov, Uwe Hermann +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti, Sergey Trofimov, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEServerAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEServerAction.java index f5aab7286..3ac0d4965 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEServerAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEServerAction.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattServerCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattServerCallback.java index 03db77b44..588a7dd3b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattServerCallback.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattServerCallback.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2019 Andreas Böhler + + 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.service.btle; import android.bluetooth.BluetoothDevice; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransaction.java index bf4cad8df..70c27bd6d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransaction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransaction.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransactionBuilder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransactionBuilder.java index 5a8e7693d..43b926233 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransactionBuilder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/ServerTransactionBuilder.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2019 Andreas Böhler This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java index eef19df95..11de807bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti +/* Copyright (C) 2015-2019 Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ServerResponseAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ServerResponseAction.java index 817c25f32..302fd9fe0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ServerResponseAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ServerResponseAction.java @@ -1,5 +1,4 @@ -/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Uwe Hermann +/* Copyright (C) 2019 Andreas Böhler This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTServer.java deleted file mode 100644 index 3820ab398..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTServer.java +++ /dev/null @@ -1,228 +0,0 @@ -/* Copyright (C) 2018-2019 Andreas Böhler, Daniele Gobbetti - based on code from BlueWatcher, https://github.com/masterjc/bluewatcher - - 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.service.devices.casiogb6900; - -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattDescriptor; -import android.bluetooth.BluetoothGattServer; -import android.bluetooth.BluetoothGattServerCallback; -import android.bluetooth.BluetoothGattService; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.content.Intent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; -import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; -import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; - -class CasioGATTServer extends BluetoothGattServerCallback { - private static final Logger LOG = LoggerFactory.getLogger(CasioGATTServer.class); - - private Context mContext; - private BluetoothGattServer mBluetoothGattServer; - private CasioGB6900DeviceSupport mDeviceSupport = null; - private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl(); - - CasioGATTServer(Context context, CasioGB6900DeviceSupport deviceSupport) { - mContext = context; - mDeviceSupport = deviceSupport; - } - - public void setContext(Context ctx) { - mContext = ctx; - } - - boolean initialize() { - if(mContext == null) { - return false; - } - - BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - if (bluetoothManager == null) { - return false; - } - mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this); - if (mBluetoothGattServer == null) { - return false; - } - - BluetoothGattService casioGATTService = new BluetoothGattService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY); - BluetoothGattCharacteristic bluetoothgGATTCharacteristic = new BluetoothGattCharacteristic(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_WRITE); - bluetoothgGATTCharacteristic.setValue(new byte[0]); - - BluetoothGattCharacteristic bluetoothgGATTCharacteristic2 = new BluetoothGattCharacteristic(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED); - bluetoothgGATTCharacteristic2.setValue(CasioGB6900Constants.MUSIC_MESSAGE.getBytes()); - - BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(CasioGB6900Constants.CCC_DESCRIPTOR_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE); - bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - - bluetoothgGATTCharacteristic2.addDescriptor(bluetoothGattDescriptor); - - casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic); - casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic2); - mBluetoothGattServer.addService(casioGATTService); - - return true; - } - - @Override - public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { - - if (!characteristic.getUuid().equals(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID)) { - LOG.warn("unexpected read request"); - return; - } - - LOG.info("will send response to read request from device: " + device.getAddress()); - - if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, CasioGB6900Constants.MUSIC_MESSAGE.getBytes())) { - LOG.warn("error sending response"); - } - } - private GBDeviceEventMusicControl.Event parse3Button(int button) { - GBDeviceEventMusicControl.Event event; - switch(button) { - case 3: - event = GBDeviceEventMusicControl.Event.NEXT; - break; - case 2: - event = GBDeviceEventMusicControl.Event.PREVIOUS; - break; - case 1: - event = GBDeviceEventMusicControl.Event.PLAYPAUSE; - break; - default: - LOG.warn("Unhandled button received: " + button); - event = GBDeviceEventMusicControl.Event.UNKNOWN; - } - return event; - } - - private GBDeviceEventMusicControl.Event parse2Button(int button) { - GBDeviceEventMusicControl.Event event; - switch(button) { - case 2: - event = GBDeviceEventMusicControl.Event.PLAYPAUSE; - break; - case 1: - event = GBDeviceEventMusicControl.Event.NEXT; - break; - default: - LOG.warn("Unhandled button received: " + button); - event = GBDeviceEventMusicControl.Event.UNKNOWN; - } - return event; - } - - @Override - public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { - - if (!characteristic.getUuid().equals(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID)) { - LOG.warn("unexpected write request"); - return; - } - - if(mDeviceSupport == null) { - LOG.warn("mDeviceSupport is null, did initialization complete?"); - return; - } - - if((value[0] & 0x03) == 0) { - int button = value[1] & 0x0f; - LOG.info("Button pressed: " + button); - switch(mDeviceSupport.getModel()) - { - case MODEL_CASIO_5600B: - musicCmd.event = parse2Button(button); - break; - case MODEL_CASIO_6900B: - musicCmd.event = parse3Button(button); - break; - case MODEL_CASIO_GENERIC: - musicCmd.event = parse3Button(button); - break; - default: - LOG.warn("Unhandled device"); - return; - } - mDeviceSupport.evaluateGBDeviceEvent(musicCmd); - mDeviceSupport.evaluateGBDeviceEvent(musicCmd); - } - else { - LOG.info("received from device: " + value.toString()); - } - } - - @Override - public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { - - LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState); - if (newState == BluetoothGattServer.STATE_DISCONNECTED) { - LOG.info("CASIO GATT server noticed disconnect."); - } - if (newState == BluetoothGattServer.STATE_CONNECTED) { - GBDevice.State devState = mDeviceSupport.getDevice().getState(); - Intent deviceCommunicationServiceIntent = new Intent(mContext, DeviceCommunicationService.class); - if (devState.equals(GBDevice.State.WAITING_FOR_RECONNECT) || devState.equals(GBDevice.State.NOT_CONNECTED)) { - LOG.info("Forcing re-connect because GATT server has been reconnected."); - deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_CONNECT); - deviceCommunicationServiceIntent.putExtra(GBDevice.EXTRA_DEVICE, device); - LocalBroadcastManager.getInstance(mContext).sendBroadcast(deviceCommunicationServiceIntent); - //PendingIntent reconnectPendingIntent = PendingIntent.getService(mContext, 2, deviceCommunicationServiceIntent, PendingIntent.FLAG_UPDATE_CURRENT); - //builder.addAction(R.drawable.ic_notification, context.getString(R.string.controlcenter_connect), reconnectPendingIntent); - } - } - } - - @Override - public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { - - LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1)); - if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) { - LOG.warn("onDescriptorWriteRequest() error sending response!"); - } - } - - @Override - public void onServiceAdded(int status, BluetoothGattService service) { - LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid()); - } - - @Override - public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) { - LOG.info("onNotificationSent() status = " + status + " to device " + bluetoothDevice.getAddress()); - } - - void close() { - if (mBluetoothGattServer != null) { - mBluetoothGattServer.clearServices(); - mBluetoothGattServer.close(); - mBluetoothGattServer = null; - } - } - -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java deleted file mode 100644 index 97f3c4401..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGATTThread.java +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (C) 2018-2019 Andreas Böhler - based on code from BlueWatcher, https://github.com/masterjc/bluewatcher - - 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.service.devices.casiogb6900; -import android.content.Context; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CasioGATTThread extends Thread { - CasioGATTServer mServer = null; - private static final Logger LOG = LoggerFactory.getLogger(CasioGATTThread.class); - private boolean mStopFlag = false; - private final Object waitObject = new Object(); - - public CasioGATTThread(Context context, CasioGB6900DeviceSupport deviceSupport) - { - mServer = new CasioGATTServer(context, deviceSupport); - } - - public void setContext(Context ctx) { - mServer.setContext(ctx); - } - - @Override - public void run() { - if (!mServer.initialize()) { - LOG.error("Error initializing CasioGATTServer. Has the context been set?"); - return; - } - - long waitTime = 60 * 1000; - - while (!mStopFlag) { - synchronized (waitObject) { - try { - waitObject.wait(waitTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - if (mStopFlag) { - break; - } - } - mServer.close(); - } - - public void quit() { - mStopFlag = true; - synchronized (waitObject) { - waitObject.notify(); - } - } - - -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java index 5e3a687b7..d42233a0f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casiogb6900/CasioGB6900DeviceSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2019 Andreas Böhler +/* Copyright (C) 2018-2019 Andreas Böhler, Sebastian Kranz based on code from BlueWatcher, https://github.com/masterjc/bluewatcher This file is part of Gadgetbridge. @@ -17,12 +17,13 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900; -import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; -import android.content.Context; import android.net.Uri; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +35,9 @@ import java.util.Calendar; import java.util.UUID; import java.util.concurrent.TimeUnit; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; @@ -48,22 +51,24 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.ServerTransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.operations.InitOperation; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { private static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceSupport.class); private ArrayList mCasioCharacteristics = new ArrayList(); - private CasioGATTThread mThread; private CasioHandlerThread mHandlerThread = null; private MusicSpec mBufferMusicSpec = null; private MusicStateSpec mBufferMusicStateSpec = null; private BluetoothGatt mBtGatt = null; private CasioGB6900Constants.Model mModel = CasioGB6900Constants.Model.MODEL_CASIO_GENERIC; - private byte[] mBleSettings = null; + private boolean mFirstConnect = false; - private static final int mCasioSleepTime = 80; + private static final int mCasioSleepTime = 50; public CasioGB6900DeviceSupport() { super(LOG); @@ -79,14 +84,30 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { addSupportedService(CasioGB6900Constants.TX_POWER_SERVICE_UUID); addSupportedService(CasioGB6900Constants.LINK_LOSS_SERVICE); addSupportedService(CasioGB6900Constants.IMMEDIATE_ALERT_SERVICE_UUID); - mThread = new CasioGATTThread(getContext(), this); + + BluetoothGattService casioGATTService = new BluetoothGattService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY); + BluetoothGattCharacteristic bluetoothGATTCharacteristic = new BluetoothGattCharacteristic(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_WRITE); + bluetoothGATTCharacteristic.setValue(new byte[0]); + + BluetoothGattCharacteristic bluetoothGATTCharacteristic2 = new BluetoothGattCharacteristic(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED); + bluetoothGATTCharacteristic2.setValue(CasioGB6900Constants.MUSIC_MESSAGE.getBytes()); + + BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(CasioGB6900Constants.CCC_DESCRIPTOR_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE); + bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + + bluetoothGATTCharacteristic2.addDescriptor(bluetoothGattDescriptor); + + casioGATTService.addCharacteristic(bluetoothGATTCharacteristic); + casioGATTService.addCharacteristic(bluetoothGATTCharacteristic2); + + addSupportedServerService(casioGATTService); } @Override - public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) { - super.setContext(gbDevice, btAdapter, context); - mThread.setContext(context); - mThread.start(); + public boolean connectFirstTime() { + GB.toast(getContext(), "After first connect, disable and enable bluetooth on your Casio watch to really connect", Toast.LENGTH_SHORT, GB.INFO); + mFirstConnect = true; + return super.connect(); } @Override @@ -103,12 +124,6 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { mHandlerThread.interrupt(); mHandlerThread = null; } - - if(mThread != null) { - mThread.quit(); - mThread.interrupt(); - mThread = null; - } } @Override @@ -121,6 +136,14 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { protected TransactionBuilder initializeDevice(TransactionBuilder builder) { LOG.info("Initializing"); + if(mFirstConnect) { + gbDevice.setState(GBDevice.State.INITIALIZED); + gbDevice.sendDeviceUpdateIntent(getContext()); + getDevice().setFirmwareVersion("N/A"); + getDevice().setFirmwareVersion2("N/A"); + return builder; + } + String name = gbDevice.getName(); if(name.contains("5600B")) { @@ -131,17 +154,27 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { mModel = CasioGB6900Constants.Model.MODEL_CASIO_GENERIC; } + try { + new InitOperation(this, builder).perform(); + } catch (IOException e) { + GB.toast(getContext(), "Initializing Casio watch failed", Toast.LENGTH_SHORT, GB.ERROR, e); + } + /* gbDevice.setState(GBDevice.State.INITIALIZING); gbDevice.sendDeviceUpdateIntent(getContext()); + */ + + getDevice().setFirmwareVersion("N/A"); + getDevice().setFirmwareVersion2("N/A"); - addCharacteristics(); builder.setGattCallback(this); - enableNotifications(builder, true); - configureWatch(builder); + addCharacteristics(); + enableNotifications(builder, true); + LOG.info("Initialization Done"); return builder; @@ -251,52 +284,22 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { } } - private void readBleSettings() { - try { - TransactionBuilder builder = performInitialized("readBleSettings"); - builder.read(getCharacteristic(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID)); - builder.queue(getQueue()); - } catch(IOException e) { - LOG.error("Error reading BLE settings: " + e.getMessage()); - } - } - - private void configureBleSettings() { - // These values seem to improve connection stability _on my phone_ - // Maybe they should be configurable? - int slaveLatency = 2; - int connInterval = 300; - - mBleSettings[5] = (byte)(connInterval & 0xff); - mBleSettings[6] = (byte)((connInterval >> 8) & 0xff); - mBleSettings[7] = (byte)(slaveLatency & 0xff); - mBleSettings[8] = (byte)((slaveLatency >> 8) & 0xff); - - mBleSettings[9] = 0; // Setting for Disconnect!? - } - - private void writeBleSettings() { - try { - TransactionBuilder builder = performInitialized("writeBleSettings"); - builder.write(getCharacteristic(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID), mBleSettings); - builder.queue(getQueue()); - } catch(IOException e) { - LOG.error("Error writing BLE settings: " + e.getMessage()); - } - } private boolean handleInitResponse(byte data) { boolean handled = false; switch(data) { case (byte) 1: LOG.info("Initialization done, setting state to INITIALIZED"); - if(mHandlerThread == null) { - mHandlerThread = new CasioHandlerThread(getDevice(), getContext(), this); + if(mHandlerThread != null) { + if(mHandlerThread.isAlive()) { + mHandlerThread.quit(); + mHandlerThread.interrupt(); + } } + mHandlerThread = new CasioHandlerThread(getDevice(), getContext(), this); mHandlerThread.start(); gbDevice.setState(GBDevice.State.INITIALIZED); gbDevice.sendDeviceUpdateIntent(getContext()); - readBleSettings(); handled = true; break; default: @@ -351,7 +354,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { return true; } - private boolean handleCasioCom(byte[] data) { + private boolean handleCasioCom(byte[] data, boolean handleTime) { boolean handled = false; if(data.length < 3) { @@ -365,7 +368,11 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { handled = handleInitResponse(data[2]); break; case 2: - handled = handleTimeRequests(data[2]); + if(handleTime) { + handled = handleTimeRequests(data[2]); + } else { + handled = true; + } break; case 7: handled = handleServerFeatureRequests(data[2]); @@ -376,7 +383,7 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { @Override public boolean onCharacteristicRead(BluetoothGatt gatt, - BluetoothGattCharacteristic characteristic, int status) { + BluetoothGattCharacteristic characteristic, int status) { UUID characteristicUUID = characteristic.getUuid(); byte[] data = characteristic.getValue(); @@ -391,63 +398,6 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { } LOG.info(str); } - else if(characteristicUUID.equals(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID)) { - mBleSettings = data; - String str = "Read Casio Setting for BLE: "; - for(int i=0; i> 6) & 0x03; - //LOG.info("Call Alert: " + callAlert); - //int mailAlert = (data[0] >> 2) & 0x03; - //LOG.info("Mail Alert: " + mailAlert); - //int snsAlert = (data[2] >> 4) & 0x03; - //LOG.info("SNS Alert: " + snsAlert); - //int calAlert = (data[1] >> 6) & 0x03; - //LOG.info("Calendart Alert: " + calAlert); - //int otherAlert = (data[0] & 0x03); - //LOG.info("Other Alert: " + otherAlert); - //int vibrationValue = (data[3] & 0x0f); - //LOG.info("Vibration Value: " + vibrationValue); - //int alarmValue = (data[3] >> 4) & 0x0f; - // Vibration pattern; A = 0, B = 1, C = 2 - //LOG.info("Alarm Value: " + alarmValue); - //int animationValue = data[4] & 0x40; - // Length of Alarm, only 2, 5 and 10 possible - //LOG.info("Animation Value: " + animationValue); - // 0 = on - // 64 = off - //int useDisableMtuReqBit = data[4] & 0x08; - // 8 = on - // 0 = off!? - //LOG.info("useDisableMtuReqBit: " + useDisableMtuReqBit); - - //int slaveLatency = ((data[7] & 0xff) | ((data[8] & 0xff) << 8)); - //int connInterval = ((data[5] & 0xff) | ((data[6] & 0xff) << 8)); - //LOG.info("Slave Latency: " + slaveLatency); - //LOG.info("Connection Interval: " + connInterval); - //LOG.info(str); - - configureBleSettings(); - writeBleSettings(); - } else { return super.onCharacteristicRead(gatt, characteristic, status); } @@ -466,11 +416,11 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { return true; if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT)) { - handled = handleCasioCom(data); + handled = handleCasioCom(data, true); } if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT)) { - handled = handleCasioCom(data); + handled = handleCasioCom(data, false); } if(characteristicUUID.equals(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID)) { @@ -481,14 +431,16 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { else { findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP; } - evaluateGBDeviceEvent(findPhoneEvent); + evaluateGBDeviceEvent(findPhoneEvent); handled = true; } if(characteristicUUID.equals(CasioGB6900Constants.RINGER_CONTROL_POINT)) { if(data[0] == 0x02) { - LOG.info("Mute/ignore call event not yet supported by GB"); + GBDeviceEventCallControl callControlEvent = new GBDeviceEventCallControl(); + callControlEvent.event = GBDeviceEventCallControl.Event.IGNORE; + evaluateGBDeviceEvent(callControlEvent); } handled = true; } @@ -501,6 +453,8 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { } private void showNotification(byte icon, String title, String message) { + if(!isConnected()) + return; try { TransactionBuilder builder = performInitialized("showNotification"); int len; @@ -558,6 +512,10 @@ public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport { public void onSetAlarms(ArrayList alarms) { int alarmOffset = 4; byte[] data = new byte[20]; + + if(!isConnected()) + return; + for(int i=0; i. */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.operations; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.CasioGB6900DeviceSupport; + +public class InitOperation extends AbstractBTLEOperation { + private static final Logger LOG = LoggerFactory.getLogger(InitOperation.class); + + private final TransactionBuilder builder; + private byte[] mBleSettings = null; + + + public InitOperation(CasioGB6900DeviceSupport support, TransactionBuilder builder) { + super(support); + this.builder = builder; + builder.setGattCallback(this); + } + + @Override + protected void doPerform() throws IOException { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + TransactionBuilder builder = getSupport().createTransactionBuilder("readBleSettings"); + builder.setGattCallback(this); + builder.read(getCharacteristic(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID)); + getSupport().performImmediately(builder); + } + + @Override + public TransactionBuilder performInitialized(String taskName) throws IOException { + throw new UnsupportedOperationException("This IS the initialization class, you cannot call this method"); + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + UUID characteristicUUID = characteristic.getUuid(); + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + return super.onCharacteristicChanged(gatt, characteristic); + } + + private void configureBleSettings() { + // These values seem to improve connection stability _on my phone_ + // Maybe they should be configurable? + int slaveLatency = 2; + int connInterval = 300; + + mBleSettings[5] = (byte)(connInterval & 0xff); + mBleSettings[6] = (byte)((connInterval >> 8) & 0xff); + mBleSettings[7] = (byte)(slaveLatency & 0xff); + mBleSettings[8] = (byte)((slaveLatency >> 8) & 0xff); + + mBleSettings[9] = 0; // Setting for Disconnect!? + } + + private void writeBleSettings() { + try { + TransactionBuilder builder = getSupport().createTransactionBuilder("writeBleInit"); + builder.setGattCallback(this); + builder.write(getCharacteristic(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID), mBleSettings); + getSupport().performImmediately(builder); + } catch(IOException e) { + LOG.error("Error writing BLE settings: " + e.getMessage()); + } + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + + UUID characteristicUUID = characteristic.getUuid(); + byte[] data = characteristic.getValue(); + + if(data.length == 0) + return true; + + if(characteristicUUID.equals(CasioGB6900Constants.CASIO_SETTING_FOR_BLE_CHARACTERISTIC_UUID)) { + mBleSettings = data; + String str = "Read Casio Setting for BLE: "; + for(int i=0; i> 6) & 0x03; + //LOG.info("Call Alert: " + callAlert); + //int mailAlert = (data[0] >> 2) & 0x03; + //LOG.info("Mail Alert: " + mailAlert); + //int snsAlert = (data[2] >> 4) & 0x03; + //LOG.info("SNS Alert: " + snsAlert); + //int calAlert = (data[1] >> 6) & 0x03; + //LOG.info("Calendart Alert: " + calAlert); + //int otherAlert = (data[0] & 0x03); + //LOG.info("Other Alert: " + otherAlert); + //int vibrationValue = (data[3] & 0x0f); + //LOG.info("Vibration Value: " + vibrationValue); + //int alarmValue = (data[3] >> 4) & 0x0f; + // Vibration pattern; A = 0, B = 1, C = 2 + //LOG.info("Alarm Value: " + alarmValue); + //int animationValue = data[4] & 0x40; + // Length of Alarm, only 2, 5 and 10 possible + //LOG.info("Animation Value: " + animationValue); + // 0 = on + // 64 = off + //int useDisableMtuReqBit = data[4] & 0x08; + // 8 = on + // 0 = off!? + //LOG.info("useDisableMtuReqBit: " + useDisableMtuReqBit); + + //int slaveLatency = ((data[7] & 0xff) | ((data[8] & 0xff) << 8)); + //int connInterval = ((data[5] & 0xff) | ((data[6] & 0xff) << 8)); + //LOG.info("Slave Latency: " + slaveLatency); + //LOG.info("Connection Interval: " + connInterval); + //LOG.info(str); + + configureBleSettings(); + writeBleSettings(); + } + else { + return super.onCharacteristicRead(gatt, characteristic, status); + } + + return true; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index be8daaf5c..2c0f43101 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -1,5 +1,5 @@ /* Copyright (C) 2016-2019 Alberto, Andreas Shimokawa, Carsten Pfeiffer, - ivanovlev, João Paulo Barraca, Pavel Motyrev, Quallenauge + ivanovlev, João Paulo Barraca, Pavel Motyrev, Quallenauge, Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiActivityDetailsParser.java similarity index 87% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiActivityDetailsParser.java index 65b27d2aa..aa2d978be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiActivityDetailsParser.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,43 +33,35 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint; import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class ActivityDetailsParser { - private static final Logger LOG = LoggerFactory.getLogger(ActivityDetailsParser.class); +public class HuamiActivityDetailsParser { + private static final Logger LOG = LoggerFactory.getLogger(HuamiActivityDetailsParser.class); private static final byte TYPE_GPS = 0; private static final byte TYPE_HR = 1; - private static final byte TYPE_UNKNOWN2 = 2; - private static final byte TYPE_PAUSE = 3; + private static final byte TYPE_PAUSE = 2; + private static final byte TYPE_RESUME = 3; private static final byte TYPE_SPEED4 = 4; private static final byte TYPE_SPEED5 = 5; - private static final byte TYPE_GPS_SPEED6 = 6; + private static final byte TYPE_SPEED6 = 6; + private static final byte TYPE_SWIMMING = 8; - public static final BigDecimal HUAMI_TO_DECIMAL_DEGREES_DIVISOR = new BigDecimal(3000000.0); - private final BaseActivitySummary summary; + private static final BigDecimal HUAMI_TO_DECIMAL_DEGREES_DIVISOR = new BigDecimal(3000000.0); private final ActivityTrack activityTrack; - // private final int version; private final Date baseDate; private long baseLongitude; private long baseLatitude; private int baseAltitude; private ActivityPoint lastActivityPoint; - public boolean getSkipCounterByte() { - return skipCounterByte; - } - public void setSkipCounterByte(boolean skipCounterByte) { this.skipCounterByte = skipCounterByte; } private boolean skipCounterByte; - public ActivityDetailsParser(BaseActivitySummary summary) { - this.summary = summary; -// this.version = version; -// this.baseDate = baseDate; -// + public HuamiActivityDetailsParser(BaseActivitySummary summary) { this.baseLongitude = summary.getBaseLongitude(); this.baseLatitude = summary.getBaseLatitude(); this.baseAltitude = summary.getBaseAltitude(); @@ -109,21 +101,24 @@ public class ActivityDetailsParser { case TYPE_HR: i += consumeHeartRate(bytes, i, totalTimeOffset); break; - case TYPE_UNKNOWN2: - i += consumeUnknown2(bytes, i); - break; case TYPE_PAUSE: i += consumePause(bytes, i); break; + case TYPE_RESUME: + i += consumeResume(bytes, i); + break; case TYPE_SPEED4: i += consumeSpeed4(bytes, i); break; case TYPE_SPEED5: i += consumeSpeed5(bytes, i); break; - case TYPE_GPS_SPEED6: + case TYPE_SPEED6: i += consumeSpeed6(bytes, i); break; + case TYPE_SWIMMING: + i += consumeSwimming(bytes, i); + break; default: LOG.warn("unknown packet type" + type); i+=6; @@ -213,7 +208,6 @@ public class ActivityDetailsParser { if (v2 == 0 && v3 == 0 && v4 == 0 && v5 == 0 && v6 == 0) { // new version -// LOG.info("detected heart rate in 'new' version, where version is: " + summary.getVersion()); LOG.info("detected heart rate in 'new' version format"); ActivityPoint ap = getActivityPointFor(timeOffsetSeconds); ap.setHeartRate(v1); @@ -270,23 +264,33 @@ public class ActivityDetailsParser { } } - private int consumeUnknown2(byte[] bytes, int offset) { - return 6; // just guessing... + private int consumePause(byte[] bytes, int offset) { + LOG.debug("got pause packet: " + GB.hexdump(bytes, offset, 6)); + return 6; } - private int consumePause(byte[] bytes, int i) { - return 6; // just guessing... + private int consumeResume(byte[] bytes, int offset) { + LOG.debug("got resume package: " + GB.hexdump(bytes, offset, 6)); + return 6; } private int consumeSpeed4(byte[] bytes, int offset) { + LOG.debug("got packet type 4 (speed): " + GB.hexdump(bytes, offset, 6)); return 6; } private int consumeSpeed5(byte[] bytes, int offset) { + LOG.debug("got packet type 5 (speed): " + GB.hexdump(bytes, offset, 6)); return 6; } private int consumeSpeed6(byte[] bytes, int offset) { + LOG.debug("got packet type 6 (speed): " + GB.hexdump(bytes, offset, 6)); + return 6; + } + + private int consumeSwimming(byte[] bytes, int offset) { + LOG.debug("got packet type 8 (swimming?): " + GB.hexdump(bytes, offset, 6)); return 6; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java index 9bd15396d..21a8f0e42 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java @@ -95,7 +95,7 @@ public class HuamiBatteryInfo extends AbstractInfo { if (mData.length >= 18) { lastCharge = BLETypeConversions.rawBytesToCalendar(new byte[]{ mData[10], mData[11], mData[12], mData[13], mData[14], mData[15], mData[16], mData[17] - }, true); + }); } return lastCharge; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java index d0e3ec70c..681c4bd93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java @@ -49,8 +49,10 @@ public abstract class HuamiFirmwareInfo { }; protected static final int FONT_TYPE_OFFSET = 0x9; + protected static final int COMPRESSED_RES_HEADER_OFFSET = 0x9; + protected static final int COMPRESSED_RES_HEADER_OFFSET_NEW = 0xd; - private HuamiFirmwareType firmwareType = HuamiFirmwareType.FIRMWARE; + private HuamiFirmwareType firmwareType; public String toVersion(int crc16) { String version = getCrcMap().get(crc16); @@ -60,16 +62,16 @@ public abstract class HuamiFirmwareInfo { version = searchFirmwareVersion(bytes); break; case RES: - version = "RES " + Integer.toString(bytes[5]); + version = "RES " + bytes[5]; break; case RES_COMPRESSED: - version = "RES " + Integer.toString(bytes[14]); + version = "RES " + bytes[14]; break; case FONT: - version = "FONT " + Integer.toString(bytes[4]); + version = "FONT " + bytes[4]; break; case FONT_LATIN: - version = "FONT LATIN " + Integer.toString(bytes[4]); + version = "FONT LATIN " + bytes[4]; break; } } @@ -104,12 +106,14 @@ public abstract class HuamiFirmwareInfo { } private final int crc16; + private final int crc32; private byte[] bytes; public HuamiFirmwareInfo(byte[] bytes) { this.bytes = bytes; crc16 = CheckSums.getCRC16(bytes); + crc32 = CheckSums.getCRC32(bytes); firmwareType = determineFirmwareType(bytes); } @@ -138,6 +142,9 @@ public abstract class HuamiFirmwareInfo { public int getCrc16() { return crc16; } + public int getCrc32() { + return crc32; + } public int getFirmwareVersion() { return getCrc16(); // HACK until we know how to determine the version from the fw bytes @@ -163,7 +170,7 @@ public abstract class HuamiFirmwareInfo { if (word == 0x642e2564) { word = buf.getInt(); if (word == 0x00000000) { - byte version[] = new byte[8]; + byte[] version = new byte[8]; buf.get(version); return new String(version); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSportsActivityType.java similarity index 77% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSportsActivityType.java index f3ade44bb..bd08fd488 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSportsActivityType.java @@ -14,20 +14,20 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -public enum BipActivityType { +public enum HuamiSportsActivityType { Outdoor(1), Treadmill(2), Walking(3), Cycling(4), - Exercise(5); - + Exercise(5), + Swimming(6); private final int code; - BipActivityType(final int code) { + HuamiSportsActivityType(final int code) { this.code = code; } @@ -43,20 +43,22 @@ public enum BipActivityType { return ActivityKind.TYPE_WALKING; case Exercise: return ActivityKind.TYPE_EXERCISE; + case Swimming: + return ActivityKind.TYPE_SWIMMING; } throw new RuntimeException("Not mapped activity kind for: " + this); } - public static BipActivityType fromCode(int bipCode) { - for (BipActivityType type : values()) { - if (type.code == bipCode) { + public static HuamiSportsActivityType fromCode(int huamiCode) { + for (HuamiSportsActivityType type : values()) { + if (type.code == huamiCode) { return type; } } - throw new RuntimeException("No matching BipActivityType for code: " + bipCode); + throw new RuntimeException("No matching HuamiSportsActivityType for code: " + huamiCode); } - public static BipActivityType fromActivityKind(int activityKind) { + public static HuamiSportsActivityType fromActivityKind(int activityKind) { switch (activityKind) { case ActivityKind.TYPE_RUNNING: return Outdoor; @@ -68,6 +70,8 @@ public enum BipActivityType { return Walking; case ActivityKind.TYPE_EXERCISE: return Exercise; + case ActivityKind.TYPE_SWIMMING: + return Swimming; } throw new RuntimeException("No matching activity activityKind: " + activityKind); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index c9ca6d1f8..9d0c5b14d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Christian Fischer, Daniele Gobbetti, JohnnySun, José Rebelo, Julien Pivotto, Kasha, - Michal Novotny, Sergey Trofimov, Steffen Liebergeld + Michal Novotny, Sebastian Kranz, Sergey Trofimov, Steffen Liebergeld This file is part of Gadgetbridge. @@ -26,6 +26,8 @@ import android.net.Uri; import android.text.format.DateFormat; import android.widget.Toast; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,13 +40,13 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.TimeUnit; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.R; @@ -66,6 +68,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2FWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; import nodomain.freeyourgadget.gadgetbridge.devices.miband.DoNotDisturb; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider; @@ -86,6 +90,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; @@ -98,6 +103,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; @@ -107,7 +113,6 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions.StopNo import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchSportsSummaryOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.InitOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; @@ -203,15 +208,16 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { try { - heartRateNotifyEnabled = false; - boolean authenticate = needsAuth; - needsAuth = false; byte authFlags = getAuthFlags(); - new InitOperation(authenticate, authFlags, this, builder).perform(); + byte cryptFlags = getCryptFlags(); + heartRateNotifyEnabled = false; + boolean authenticate = needsAuth && (cryptFlags == 0x00); + needsAuth = false; + new InitOperation(authenticate, authFlags, cryptFlags, this, builder).perform(); characteristicHRControlPoint = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); characteristicChunked = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER); } catch (IOException e) { - GB.toast(getContext(), "Initializing Mi Band 2 failed", Toast.LENGTH_SHORT, GB.ERROR, e); + GB.toast(getContext(), "Initializing Huami device failed", Toast.LENGTH_SHORT, GB.ERROR, e); } return builder; } @@ -220,6 +226,10 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { return HuamiService.AUTH_BYTE; } + public byte getCryptFlags() { + return 0x00; + } + /** * Returns the given date/time (calendar) as a byte sequence, suitable for sending to the * Mi Band 2 (or derivative). The band appears to not handle DST offsets, so we simply add this @@ -231,9 +241,9 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { public byte[] getTimeBytes(Calendar calendar, TimeUnit precision) { byte[] bytes; if (precision == TimeUnit.MINUTES) { - bytes = BLETypeConversions.shortCalendarToRawBytes(calendar, true); + bytes = BLETypeConversions.shortCalendarToRawBytes(calendar); } else if (precision == TimeUnit.SECONDS) { - bytes = BLETypeConversions.calendarToRawBytes(calendar, true); + bytes = BLETypeConversions.calendarToRawBytes(calendar); } else { throw new IllegalArgumentException("Unsupported precision, only MINUTES and SECONDS are supported till now"); } @@ -245,7 +255,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } public Calendar fromTimeBytes(byte[] bytes) { - GregorianCalendar timestamp = BLETypeConversions.rawBytesToCalendar(bytes, true); + GregorianCalendar timestamp = BLETypeConversions.rawBytesToCalendar(bytes); return timestamp; } @@ -274,7 +284,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { * @param builder */ public void setInitialized(TransactionBuilder builder) { - builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZED, getContext())); + builder.add(new SetDeviceStateAction(gbDevice, State.INITIALIZED, getContext())); } // MB2: AVL @@ -291,6 +301,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), enable); builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable); builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_DEVICEEVENT), enable); + builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUDIO), enable); + builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUDIODATA), enable); return this; } @@ -333,14 +345,14 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } public NotificationStrategy getNotificationStrategy() { - String firmwareVersion = getDevice().getFirmwareVersion(); + String firmwareVersion = gbDevice.getFirmwareVersion(); if (firmwareVersion != null) { Version ver = new Version(firmwareVersion); if (MiBandConst.MI2_FW_VERSION_MIN_TEXT_NOTIFICATIONS.compareTo(ver) > 0) { return new Mi2NotificationStrategy(this); } } - if (GBApplication.getPrefs().getBoolean(MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS, true)) { + if (GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean(MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS, true)) { return new Mi2TextNotificationStrategy(this); } return new Mi2NotificationStrategy(this); @@ -461,7 +473,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS); if (characteristic != null) { builder.notify(characteristic, true); - int location = MiBandCoordinator.getWearLocation(getDevice().getAddress()); + int location = MiBandCoordinator.getWearLocation(gbDevice.getAddress()); switch (location) { case 0: // left hand builder.write(characteristic, HuamiService.WEAR_LOCATION_LEFT_WRIST); @@ -516,7 +528,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { * @param builder */ private HuamiSupport setHeartrateSleepSupport(TransactionBuilder builder) { - final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress()); + final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(gbDevice.getAddress()); if (characteristicHRControlPoint != null) { builder.notify(characteristicHRControlPoint, true); if (enableHrSleepSupport) { @@ -672,7 +684,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { sendCalendarEvents(builder); builder.queue(getQueue()); } catch (IOException ex) { - LOG.error("Unable to set time on MI device", ex); + LOG.error("Unable to set time on Huami device", ex); } } @@ -868,7 +880,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { builder.write(characteristicHRControlPoint, startHeartMeasurementManual); builder.queue(getQueue()); } catch (IOException ex) { - LOG.error("Unable to read heart rate with MI2", ex); + LOG.error("Unable to read heart rate from Huami device", ex); } } @@ -929,7 +941,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { try { new FetchActivityOperation(this).perform(); } catch (IOException ex) { - LOG.error("Unable to fetch MI activity data", ex); + LOG.error("Unable to fetch activity data", ex); } } @@ -989,7 +1001,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { @Override public void onInstallApp(Uri uri) { try { - new UpdateFirmwareOperation(uri, this).perform(); + createUpdateFirmwareOperation(uri).perform(); } catch (IOException ex) { GB.toast(getContext(), "Firmware cannot be installed: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); } @@ -1050,7 +1062,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { currentButtonPressTime = System.currentTimeMillis(); } - public void handleDeviceEvent(byte[] value) { + private void handleDeviceEvent(byte[] value) { if (value == null || value.length == 0) { return; } @@ -1058,13 +1070,14 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { switch (value[0]) { case HuamiDeviceEvent.CALL_REJECT: + LOG.info("call rejected"); callCmd.event = GBDeviceEventCallControl.Event.REJECT; evaluateGBDeviceEvent(callCmd); break; case HuamiDeviceEvent.CALL_IGNORE: - LOG.info("ignore call (not yet supported)"); - //callCmd.event = GBDeviceEventCallControl.Event.IGNORE; - //evaluateGBDeviceEvent(callCmd); + LOG.info("call ignored"); + callCmd.event = GBDeviceEventCallControl.Event.IGNORE; + evaluateGBDeviceEvent(callCmd); break; case HuamiDeviceEvent.BUTTON_PRESSED: LOG.info("button pressed"); @@ -1352,7 +1365,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { try (DBHandler handler = GBApplication.acquireDB()) { DaoSession session = handler.getDaoSession(); - Device device = DBHelper.getDevice(getDevice(), session); + Device device = DBHelper.getDevice(gbDevice, session); User user = DBHelper.getUser(session); int ts = (int) (System.currentTimeMillis() / 1000); MiBand2SampleProvider provider = new MiBand2SampleProvider(gbDevice, session); @@ -1517,7 +1530,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { case HuamiConst.PREF_DISCONNECT_NOTIFICATION_END: setDisconnectNotification(builder); break; - case MiBandConst.PREF_MI2_DISPLAY_ITEMS: + case HuamiConst.PREF_DISPLAY_ITEMS: setDisplayItems(builder); break; case MiBandConst.PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO: @@ -1526,9 +1539,9 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { case ActivityUser.PREF_USER_STEPS_GOAL: setFitnessGoal(builder); break; - case MiBandConst.PREF_MI2_DO_NOT_DISTURB: - case MiBandConst.PREF_MI2_DO_NOT_DISTURB_START: - case MiBandConst.PREF_MI2_DO_NOT_DISTURB_END: + case MiBandConst.PREF_DO_NOT_DISTURB: + case MiBandConst.PREF_DO_NOT_DISTURB_START: + case MiBandConst.PREF_DO_NOT_DISTURB_END: setDoNotDisturb(builder); break; case MiBandConst.PREF_MI2_INACTIVITY_WARNINGS: @@ -1543,6 +1556,15 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { case SettingsActivity.PREF_MEASUREMENT_SYSTEM: setDistanceUnit(builder); break; + case MiBandConst.PREF_SWIPE_UNLOCK: + setBandScreenUnlock(builder); + break; + case HuamiConst.PREF_DATEFORMAT: + setDateFormat(builder); + break; + case HuamiConst.PREF_LANGUAGE: + setLanguage(builder); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -1557,11 +1579,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { - try { - new FetchSportsSummaryOperation(this).perform(); - } catch (IOException ex) { - LOG.error("Unable to fetch MI activity data", ex); - } + } @Override @@ -1570,7 +1588,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } private HuamiSupport setDateDisplay(TransactionBuilder builder) { - DateTimeDisplay dateTimeDisplay = HuamiCoordinator.getDateDisplay(getContext()); + DateTimeDisplay dateTimeDisplay = HuamiCoordinator.getDateDisplay(getContext(), gbDevice.getAddress()); LOG.info("Setting date display to " + dateTimeDisplay); switch (dateTimeDisplay) { case TIME: @@ -1583,6 +1601,26 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { return this; } + protected HuamiSupport setDateFormat(TransactionBuilder builder) { + String dateFormat = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getString("dateformat", "MM/dd/yyyy"); + if (dateFormat == null) { + return null; + } + switch (dateFormat) { + case "MM/dd/yyyy": + case "dd.MM.yyyy": + case "dd/MM/yyyy": + byte[] command = HuamiService.DATEFORMAT_DATE_MM_DD_YYYY; + System.arraycopy(dateFormat.getBytes(), 0, command, 3, 10); + builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), command); + break; + default: + LOG.warn("unsupported date format " + dateFormat); + } + + return this; + } + private HuamiSupport setTimeFormat(TransactionBuilder builder) { boolean is24Format = DateFormat.is24HourFormat(getContext()); LOG.info("Setting 24h time format to " + is24Format); @@ -1606,7 +1644,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } private HuamiSupport setActivateDisplayOnLiftWrist(TransactionBuilder builder) { - ActivateDisplayOnLift displayOnLift = HuamiCoordinator.getActivateDisplayOnLiftWrist(getContext()); + ActivateDisplayOnLift displayOnLift = HuamiCoordinator.getActivateDisplayOnLiftWrist(getContext(), gbDevice.getAddress()); LOG.info("Setting activate display on lift wrist to " + displayOnLift); switch (displayOnLift) { @@ -1621,12 +1659,12 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { Calendar calendar = GregorianCalendar.getInstance(); - Date start = HuamiCoordinator.getDisplayOnLiftStart(); + Date start = HuamiCoordinator.getDisplayOnLiftStart(gbDevice.getAddress()); calendar.setTime(start); cmd[4] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[5] = (byte) calendar.get(Calendar.MINUTE); - Date end = HuamiCoordinator.getDisplayOnLiftEnd(); + Date end = HuamiCoordinator.getDisplayOnLiftEnd(gbDevice.getAddress()); calendar.setTime(end); cmd[6] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[7] = (byte) calendar.get(Calendar.MINUTE); @@ -1637,7 +1675,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } protected HuamiSupport setDisplayItems(TransactionBuilder builder) { - Set pages = HuamiCoordinator.getDisplayItems(); + Set pages = HuamiCoordinator.getDisplayItems(gbDevice.getAddress()); LOG.info("Setting display items to " + (pages == null ? "none" : pages)); byte[] data = HuamiService.COMMAND_CHANGE_SCREENS.clone(); @@ -1665,7 +1703,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } private HuamiSupport setRotateWristToSwitchInfo(TransactionBuilder builder) { - boolean enable = HuamiCoordinator.getRotateWristToSwitchInfo(); + boolean enable = HuamiCoordinator.getRotateWristToSwitchInfo(gbDevice.getAddress()); LOG.info("Setting rotate wrist to cycle info to " + enable); if (enable) { builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_ENABLE_ROTATE_WRIST_TO_SWITCH_INFO); @@ -1681,7 +1719,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } private HuamiSupport setDoNotDisturb(TransactionBuilder builder) { - DoNotDisturb doNotDisturb = HuamiCoordinator.getDoNotDisturb(getContext()); + DoNotDisturb doNotDisturb = HuamiCoordinator.getDoNotDisturb(gbDevice.getAddress()); LOG.info("Setting do not disturb to " + doNotDisturb); switch (doNotDisturb) { case OFF: @@ -1695,12 +1733,12 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { Calendar calendar = GregorianCalendar.getInstance(); - Date start = HuamiCoordinator.getDoNotDisturbStart(); + Date start = HuamiCoordinator.getDoNotDisturbStart(gbDevice.getAddress()); calendar.setTime(start); data[HuamiService.DND_BYTE_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); data[HuamiService.DND_BYTE_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE); - Date end = HuamiCoordinator.getDoNotDisturbEnd(); + Date end = HuamiCoordinator.getDoNotDisturbEnd(gbDevice.getAddress()); calendar.setTime(end); data[HuamiService.DND_BYTE_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY); data[HuamiService.DND_BYTE_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE); @@ -1768,7 +1806,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { } private HuamiSupport setDisconnectNotification(TransactionBuilder builder) { - DisconnectNotificationSetting disconnectNotificationSetting = HuamiCoordinator.getDisconnectNotificationSetting(getContext()); + DisconnectNotificationSetting disconnectNotificationSetting = HuamiCoordinator.getDisconnectNotificationSetting(getContext(), gbDevice.getAddress()); LOG.info("Setting disconnect notification to " + disconnectNotificationSetting); switch (disconnectNotificationSetting) { @@ -1783,12 +1821,12 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { Calendar calendar = GregorianCalendar.getInstance(); - Date start = HuamiCoordinator.getDisconnectNotificationStart(); + Date start = HuamiCoordinator.getDisconnectNotificationStart(gbDevice.getAddress()); calendar.setTime(start); cmd[4] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[5] = (byte) calendar.get(Calendar.MINUTE); - Date end = HuamiCoordinator.getDisconnectNotificationEnd(); + Date end = HuamiCoordinator.getDisconnectNotificationEnd(gbDevice.getAddress()); calendar.setTime(end); cmd[6] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[7] = (byte) calendar.get(Calendar.MINUTE); @@ -1809,6 +1847,68 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { return this; } + protected HuamiSupport setBandScreenUnlock(TransactionBuilder builder) { + boolean enable = MiBand3Coordinator.getBandScreenUnlock(gbDevice.getAddress()); + LOG.info("Setting band screen unlock to " + enable); + + if (enable) { + builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_ENABLE_BAND_SCREEN_UNLOCK); + } else { + builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_DISABLE_BAND_SCREEN_UNLOCK); + } + + return this; + } + + + protected HuamiSupport setLanguage(TransactionBuilder builder) { + String localeString = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getString("language", "auto"); + if (localeString == null || localeString.equals("auto")) { + String language = Locale.getDefault().getLanguage(); + String country = Locale.getDefault().getCountry(); + + if (country == null) { + // sometimes country is null, no idea why, guess it. + country = language; + } + localeString = language + "_" + country.toUpperCase(); + } + LOG.info("Setting device to locale: " + localeString); + final byte[] command_new = HuamiService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE.clone(); + System.arraycopy(localeString.getBytes(), 0, command_new, 3, localeString.getBytes().length); + + byte[] command_old; + switch (localeString.substring(0, 2)) { + case "es": + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; + break; + case "zh": + if (localeString.equals("zh_CN")) { + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; + } else { + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; + + } + break; + default: + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; + } + final byte[] finalCommand_old = command_old; + builder.add(new ConditionalWriteAction(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION)) { + @Override + protected byte[] checkCondition() { + if ((gbDevice.getType() == DeviceType.AMAZFITBIP && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("0.1.0.77")) < 0) || + (gbDevice.getType() == DeviceType.AMAZFITCOR && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("1.0.7.23")) < 0)) { + return finalCommand_old; + } else { + return command_new; + } + } + }); + + return this; + } + protected void writeToChunked(TransactionBuilder builder, int type, byte[] data) { final int MAX_CHUNKLENGTH = 17; int remaining = data.length; @@ -1869,4 +1969,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { return new MiBand2FWHelper(uri, context); } + + public UpdateFirmwareOperation createUpdateFirmwareOperation(Uri uri) { + return new UpdateFirmwareOperation(uri, this); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java index 980e78cf3..02bc939d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java @@ -107,6 +107,11 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(60002, "1.1.5.04"); crcToVersion.put(5229, "1.1.5.12"); crcToVersion.put(32576, "1.1.5.16"); + crcToVersion.put(28893, "1.1.5.24"); + crcToVersion.put(61710, "1.1.5.56"); + + // Latin Firmware + crcToVersion.put(52828, "1.1.5.36 (Latin)"); // resources crcToVersion.put(12586, "0.0.8.74"); @@ -130,7 +135,9 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(23073, "0.1.1.45"); crcToVersion.put(59245, "1.0.2.00"); crcToVersion.put(20591, "1.1.2.05"); - crcToVersion.put(5341, "1.1.5.02-16"); + crcToVersion.put(5341, "1.1.5.02-24"); + crcToVersion.put(22662, "1.1.5.36"); + crcToVersion.put(24045, "1.1.5.56"); // gps crcToVersion.put(61520, "9367,8f79a91,0,0,"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java index a11941d77..e67c6532e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java @@ -30,13 +30,11 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Locale; import java.util.Set; import java.util.SimpleTimeZone; import java.util.UUID; import cyanogenmod.weather.util.WeatherUtils; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; @@ -52,17 +50,15 @@ import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiIcon; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations.AmazfitBipFetchLogsOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.HuamiFetchDebugLogsOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchSportsSummaryOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import nodomain.freeyourgadget.gadgetbridge.util.Version; @@ -137,16 +133,25 @@ public class AmazfitBipSupport extends HuamiSupport { appSuffix = appName.getBytes(); suffixlength = appSuffix.length; } + if (gbDevice.getType() == DeviceType.MIBAND4) { + prefixlength += 4; + } byte[] rawmessage = message.getBytes(); int length = Math.min(rawmessage.length, maxLength - prefixlength); byte[] command = new byte[length + prefixlength + suffixlength]; - - command[0] = (byte) alertCategory.getId(); - command[1] = 1; + int pos = 0; + command[pos++] = (byte) alertCategory.getId(); + if (gbDevice.getType() == DeviceType.MIBAND4) { + command[pos++] = 0; // TODO + command[pos++] = 0; + command[pos++] = 0; + command[pos++] = 0; + } + command[pos++] = 1; if (alertCategory == AlertCategory.CustomHuami) { - command[2] = customIconId; + command[pos] = customIconId; } System.arraycopy(rawmessage, 0, command, prefixlength, length); @@ -185,14 +190,7 @@ public class AmazfitBipSupport extends HuamiSupport { return this; } - Version version = new Version(gbDevice.getFirmwareVersion()); - if (version.compareTo(new Version("0.1.1.14")) < 0) { - LOG.warn("Won't set menu items since firmware is too low to be safe"); - return this; - } - - Prefs prefs = GBApplication.getPrefs(); - Set pages = prefs.getStringSet("bip_display_items", null); + Set pages = HuamiCoordinator.getDisplayItems(gbDevice.getAddress()); LOG.info("Setting display items to " + (pages == null ? "none" : pages)); byte[] command = AmazfitBipService.COMMAND_CHANGE_SCREENS.clone(); @@ -440,7 +438,7 @@ public class AmazfitBipSupport extends HuamiSupport { } else if (dataTypes == RecordedDataTypes.TYPE_GPS_TRACKS) { new FetchSportsSummaryOperation(this).perform(); } else if (dataTypes == RecordedDataTypes.TYPE_DEBUGLOGS) { - new AmazfitBipFetchLogsOperation(this).perform(); + new HuamiFetchDebugLogsOperation(this).perform(); } else { LOG.warn("fetching multiple data types at once is not supported yet"); @@ -483,82 +481,6 @@ public class AmazfitBipSupport extends HuamiSupport { return this; } - protected AmazfitBipSupport setLanguage(TransactionBuilder builder) { - - String language = Locale.getDefault().getLanguage(); - String country = Locale.getDefault().getCountry(); - - LOG.info("Setting watch language, phone language = " + language + " country = " + country); - - final byte[] command_new; - final byte[] command_old; - String localeString; - - switch (GBApplication.getPrefs().getInt("amazfitbip_language", -1)) { - case 0: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; - localeString = "zh_CN"; - break; - case 1: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; - localeString = "zh_TW"; - break; - case 2: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; - localeString = "en_US"; - break; - case 3: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; - localeString = "es_ES"; - break; - case 4: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; - localeString = "ru_RU"; - break; - default: - switch (language) { - case "zh": - if (country.equals("TW") || country.equals("HK") || country.equals("MO")) { // Taiwan, Hong Kong, Macao - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; - localeString = "zh_TW"; - } else { - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; - localeString = "zh_CN"; - } - break; - case "es": - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; - localeString = "es_ES"; - break; - case "ru": - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; - localeString = "ru_RU"; - break; - default: - command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; - localeString = "en_US"; - break; - } - } - command_new = HuamiService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE.clone(); - System.arraycopy(localeString.getBytes(), 0, command_new, 3, localeString.getBytes().length); - - builder.add(new ConditionalWriteAction(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION)) { - @Override - protected byte[] checkCondition() { - if ((gbDevice.getType() == DeviceType.AMAZFITBIP && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("0.1.0.77")) >= 0) || - (gbDevice.getType() == DeviceType.AMAZFITCOR && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("1.0.7.23")) >= 0)) { - return command_new; - } else { - return command_old; - } - } - }); - - return this; - } - - @Override public void phase2Initialize(TransactionBuilder builder) { super.phase2Initialize(builder); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java index e25f6c5e4..f681678c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java @@ -51,8 +51,8 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(64977, "RES 1.0.6.76"); crcToVersion.put(60501, "RES 1.0.7.52-71"); crcToVersion.put(31263, "RES 1.0.7.77-91"); - crcToVersion.put(20920, "RES 1.2.5.00-65"); - crcToVersion.put(25397, "RES 1.2.7.20-69"); + crcToVersion.put(20920, "RES 1.2.5.00-69"); + crcToVersion.put(25397, "RES 1.2.7.20"); // font crcToVersion.put(61054, "8"); @@ -65,12 +65,6 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { @Override protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { - if (ArrayUtils.startsWith(bytes, RES_HEADER)) { - if (bytes.length < 700000) { // dont know how to distinguish from Bip .res - return HuamiFirmwareType.INVALID; - } - return HuamiFirmwareType.RES; - } if (ArrayUtils.equals(bytes, RES_HEADER, COMPRESSED_RES_HEADER_OFFSET) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET)) { return HuamiFirmwareType.RES_COMPRESSED; } @@ -90,6 +84,9 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { return HuamiFirmwareType.FONT_LATIN; } } + if (ArrayUtils.startsWith(bytes, RES_HEADER)) { + return HuamiFirmwareType.RES; + } return HuamiFirmwareType.INVALID; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java index 748bf725c..70d2c46cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java @@ -25,14 +25,13 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Set; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class AmazfitCorSupport extends AmazfitBipSupport { @@ -41,8 +40,7 @@ public class AmazfitCorSupport extends AmazfitBipSupport { @Override protected AmazfitCorSupport setDisplayItems(TransactionBuilder builder) { - Prefs prefs = GBApplication.getPrefs(); - Set pages = prefs.getStringSet("cor_display_items", null); + Set pages = HuamiCoordinator.getDisplayItems(getDevice().getAddress()); LOG.info("Setting display items to " + (pages == null ? "none" : pages)); byte[] command = AmazfitCorService.COMMAND_CHANGE_SCREENS.clone(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2FirmwareInfo.java new file mode 100644 index 000000000..3fcccc536 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2FirmwareInfo.java @@ -0,0 +1,87 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Daniele Gobbetti + + 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.service.devices.huami.amazfitcor2; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + +public class AmazfitCor2FirmwareInfo extends HuamiFirmwareInfo { + // this is the same as Bip + private static final byte[] FW_HEADER = new byte[]{ + 0x00, (byte) 0x98, 0x00, 0x20, (byte) 0xA5, 0x04, 0x00, 0x20, (byte) 0xAD, 0x04, 0x00, 0x20, (byte) 0xC5, 0x04, 0x00, 0x20 + }; + + private static Map crcToVersion = new HashMap<>(); + + static { + // font + crcToVersion.put(61054, "8"); + crcToVersion.put(62291, "9 (Latin)"); + } + + public AmazfitCor2FirmwareInfo(byte[] bytes) { + super(bytes); + } + + @Override + protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { + if (ArrayUtils.equals(bytes, RES_HEADER, COMPRESSED_RES_HEADER_OFFSET) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET_NEW) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET)) { + return HuamiFirmwareType.RES_COMPRESSED; + } + if (ArrayUtils.startsWith(bytes, FW_HEADER)) { + // FIXME: It would certainly better if we could check for "Cor 2" when the device name is "Cor 2" and for "Band 2" when it is "Band 2" + if (searchString32BitAligned(bytes, "Amazfit Cor 2")) { + return HuamiFirmwareType.FIRMWARE; + } + if (searchString32BitAligned(bytes, "Amazfit Band 2")) { + return HuamiFirmwareType.FIRMWARE; + } + return HuamiFirmwareType.INVALID; + } + if (ArrayUtils.startsWith(bytes, WATCHFACE_HEADER)) { + return HuamiFirmwareType.WATCHFACE; + } + if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) { + if (bytes[10] == 0x01) { + return HuamiFirmwareType.FONT; + } else if (bytes[10] == 0x02) { + return HuamiFirmwareType.FONT_LATIN; + } + } + // somebody might have unpacked the compressed res + if (ArrayUtils.startsWith(bytes, RES_HEADER)) { + return HuamiFirmwareType.RES; + } + return HuamiFirmwareType.INVALID; + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return isHeaderValid() && device.getType() == DeviceType.AMAZFITCOR2; + } + + @Override + protected Map getCrcMap() { + return crcToVersion; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2Support.java new file mode 100644 index 000000000..ae5b367d9 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor2/AmazfitCor2Support.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.service.devices.huami.amazfitcor2; + +import android.content.Context; +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor2.AmazfitCor2FWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport; + +public class AmazfitCor2Support extends AmazfitCorSupport { + + private static final Logger LOG = LoggerFactory.getLogger(AmazfitCor2Support.class); + + + @Override + public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + return new AmazfitCor2FWHelper(uri, context); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java index 26c507465..4102c7fbd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java @@ -57,6 +57,13 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(40344, "2.2.0.14"); crcToVersion.put(4467, "2.2.0.42"); crcToVersion.put(61657, "2.3.0.2"); + crcToVersion.put(62735, "2.3.0.6"); + crcToVersion.put(40949, "2.3.0.28"); + crcToVersion.put(59213, "2.4.0.12"); + crcToVersion.put(10810, "2.4.0.20"); + + // firmware (Mi Band 3 NFC) + crcToVersion.put(46724, "1.7.0.4"); // resources crcToVersion.put(54724, "1.2.0.8"); @@ -65,10 +72,14 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(25278, "1.4.0.12-1.6.0.16"); crcToVersion.put(23249, "1.8.0.0"); crcToVersion.put(1815, "2.0.0.4"); - crcToVersion.put(7225, "2.2.0.12-2.3.0.2"); + crcToVersion.put(7225, "2.2.0.12-2.3.0.6"); + crcToVersion.put(52754, "2.3.0.28"); + crcToVersion.put(17930, "2.4.0.12-20"); // font crcToVersion.put(19775, "1"); + crcToVersion.put(42959, "2 (old Jap/Kor)"); + crcToVersion.put(12052, "1 (Jap/Kor)"); } public MiBand3FirmwareInfo(byte[] bytes) { @@ -78,7 +89,7 @@ public class MiBand3FirmwareInfo extends HuamiFirmwareInfo { @Override protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { if (ArrayUtils.startsWith(bytes, FT_HEADER)) { - if (bytes[FONT_TYPE_OFFSET] == 0x03 || bytes[FONT_TYPE_OFFSET] == 0x04) { + if (bytes[FONT_TYPE_OFFSET] >= 0x03 && bytes[FONT_TYPE_OFFSET] <= 0x05) { return HuamiFirmwareType.FONT; } return HuamiFirmwareType.INVALID; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java index abec5be78..8fab17d14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java @@ -27,21 +27,18 @@ import java.io.IOException; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; -import java.util.Locale; import java.util.Set; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3FWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Service; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; -import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class MiBand3Support extends AmazfitBipSupport { @@ -49,16 +46,13 @@ public class MiBand3Support extends AmazfitBipSupport { @Override protected byte getAuthFlags() { - if (gbDevice.getType() == DeviceType.MIBAND3) { - return 0x00; - } - return super.getAuthFlags(); + return 0x00; } @Override protected MiBand3Support setDisplayItems(TransactionBuilder builder) { - Prefs prefs = GBApplication.getPrefs(); - Set pages = prefs.getStringSet("miband3_display_items", null); + Set pages = HuamiCoordinator.getDisplayItems(gbDevice.getAddress()); + LOG.info("Setting display items to " + (pages == null ? "none" : pages)); byte[] command = MiBand3Service.COMMAND_CHANGE_SCREENS.clone(); @@ -88,9 +82,17 @@ public class MiBand3Support extends AmazfitBipSupport { command[1] |= 0x40; command[9] = pos++; } + if (pages.contains("timer")) { + command[1] |= 0x80; + command[10] = pos++; + } + if (pages.contains("nfc")) { + command[2] |= 0x01; + command[11] = pos++; + } } - for (int i = 4; i <= 9; i++) { + for (int i = 4; i <= 11; i++) { if (command[i] == 0) { command[i] = pos++; } @@ -107,12 +109,9 @@ public class MiBand3Support extends AmazfitBipSupport { try { builder = performInitialized("Sending configuration for option: " + config); switch (config) { - case MiBandConst.PREF_MI3_BAND_SCREEN_UNLOCK: - setBandScreenUnlock(builder); - break; - case MiBandConst.PREF_MI3_NIGHT_MODE: - case MiBandConst.PREF_MI3_NIGHT_MODE_START: - case MiBandConst.PREF_MI3_NIGHT_MODE_END: + case MiBandConst.PREF_NIGHT_MODE: + case MiBandConst.PREF_NIGHT_MODE_START: + case MiBandConst.PREF_NIGHT_MODE_END: setNightMode(builder); break; default: @@ -125,64 +124,28 @@ public class MiBand3Support extends AmazfitBipSupport { } } - @Override - protected MiBand3Support setLanguage(TransactionBuilder builder) { - String localeString = GBApplication.getPrefs().getString("miband3_language", "auto"); - - if (localeString.equals("auto")) { - String language = Locale.getDefault().getLanguage(); - String country = Locale.getDefault().getCountry(); - - if (country == null) { - // sometimes country is null, no idea why, guess it. - country = language; - } - localeString = language + "_" + country.toUpperCase(); - } - LOG.info("Setting device to locale: " + localeString); - byte[] command_new = HuamiService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE.clone(); - System.arraycopy(localeString.getBytes(), 0, command_new, 3, localeString.getBytes().length); - - builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), command_new); - - return this; - } - - private MiBand3Support setBandScreenUnlock(TransactionBuilder builder) { - boolean enable = MiBand3Coordinator.getBandScreenUnlock(); - LOG.info("Setting band screen unlock to " + enable); - - if (enable) { - builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_ENABLE_BAND_SCREEN_UNLOCK); - } else { - builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_DISABLE_BAND_SCREEN_UNLOCK); - } - - return this; - } - private MiBand3Support setNightMode(TransactionBuilder builder) { - String nightMode = MiBand3Coordinator.getNightMode(); + String nightMode = MiBand3Coordinator.getNightMode(gbDevice.getAddress()); LOG.info("Setting night mode to " + nightMode); switch (nightMode) { - case MiBandConst.PREF_MI3_NIGHT_MODE_SUNSET: + case MiBandConst.PREF_NIGHT_MODE_SUNSET: builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_NIGHT_MODE_SUNSET); break; - case MiBandConst.PREF_MI3_NIGHT_MODE_OFF: + case MiBandConst.PREF_NIGHT_MODE_OFF: builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand3Service.COMMAND_NIGHT_MODE_OFF); break; - case MiBandConst.PREF_MI3_NIGHT_MODE_SCHEDULED: + case MiBandConst.PREF_NIGHT_MODE_SCHEDULED: byte[] cmd = MiBand3Service.COMMAND_NIGHT_MODE_SCHEDULED.clone(); Calendar calendar = GregorianCalendar.getInstance(); - Date start = MiBand3Coordinator.getNightModeStart(); + Date start = MiBand3Coordinator.getNightModeStart(gbDevice.getAddress()); calendar.setTime(start); cmd[2] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[3] = (byte) calendar.get(Calendar.MINUTE); - Date end = MiBand3Coordinator.getNightModeEnd(); + Date end = MiBand3Coordinator.getNightModeEnd(gbDevice.getAddress()); calendar.setTime(end); cmd[4] = (byte) calendar.get(Calendar.HOUR_OF_DAY); cmd[5] = (byte) calendar.get(Calendar.MINUTE); @@ -201,8 +164,10 @@ public class MiBand3Support extends AmazfitBipSupport { public void phase2Initialize(TransactionBuilder builder) { super.phase2Initialize(builder); LOG.info("phase2Initialize..."); + setLanguage(builder); setBandScreenUnlock(builder); setNightMode(builder); + setDateFormat(builder); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4FirmwareInfo.java new file mode 100644 index 000000000..5b7f3fcee --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4FirmwareInfo.java @@ -0,0 +1,89 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer + + 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.service.devices.huami.miband4; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + +public class MiBand4FirmwareInfo extends HuamiFirmwareInfo { + + private static final byte[] FW_HEADER = new byte[]{ + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x9c, (byte) 0xe3, 0x7d, 0x5c, 0x00, 0x04 + }; + + private static final int FW_HEADER_OFFSET = 16; + + private static Map crcToVersion = new HashMap<>(); + + static { + // firmware + crcToVersion.put(8969, "1.0.5.22"); + + // resources + crcToVersion.put(27412, "1.0.5.22"); + + // font + crcToVersion.put(31978, "1"); + } + + public MiBand4FirmwareInfo(byte[] bytes) { + super(bytes); + } + + @Override + protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { + if (ArrayUtils.equals(bytes, RES_HEADER, COMPRESSED_RES_HEADER_OFFSET) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET_NEW) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET)) { + return HuamiFirmwareType.RES_COMPRESSED; + } + if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET)) { + if (searchString32BitAligned(bytes, "Mi Smart Band 4")) { + return HuamiFirmwareType.FIRMWARE; + } + return HuamiFirmwareType.INVALID; + } + if (ArrayUtils.startsWith(bytes, WATCHFACE_HEADER)) { + return HuamiFirmwareType.WATCHFACE; + } + if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) { + if (bytes[10] == 0x03) { + return HuamiFirmwareType.FONT; + } + } + // somebody might have unpacked the compressed res + if (ArrayUtils.startsWith(bytes, RES_HEADER)) { + return HuamiFirmwareType.RES; + } + return HuamiFirmwareType.INVALID; + } + + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return isHeaderValid() && device.getType() == DeviceType.MIBAND4; + } + + @Override + protected Map getCrcMap() { + return crcToVersion; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java new file mode 100644 index 000000000..7e687932c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband4/MiBand4Support.java @@ -0,0 +1,45 @@ +/* Copyright (C) 2017-2019 Andreas Shimokawa + + 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.service.devices.huami.miband4; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband4.MiBand4FWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperationNew; + +public class MiBand4Support extends MiBand3Support { + + @Override + public byte getCryptFlags() { + return (byte) 0x80; + } + + @Override + public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + return new MiBand4FWHelper(uri, context); + } + + @Override + public UpdateFirmwareOperationNew createUpdateFirmwareOperation(Uri uri) { + return new UpdateFirmwareOperationNew(uri, this); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/AbstractFetchOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/AbstractFetchOperation.java index 8fa50d654..915db7cd7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/AbstractFetchOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/AbstractFetchOperation.java @@ -22,6 +22,9 @@ import android.bluetooth.BluetoothGattCharacteristic; import android.content.SharedPreferences; import android.widget.Toast; +import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,8 +36,6 @@ import java.util.GregorianCalendar; import java.util.UUID; import java.util.concurrent.TimeUnit; -import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.R; @@ -57,11 +58,10 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation { private static final Logger LOG = LoggerFactory.getLogger(AbstractFetchOperation.class); protected byte lastPacketCounter; - protected int fetchCount; + int fetchCount; protected BluetoothGattCharacteristic characteristicActivityData; protected BluetoothGattCharacteristic characteristicFetch; - protected Calendar startTimestamp; - protected int expectedDataLength; + Calendar startTimestamp; public AbstractFetchOperation(HuamiSupport support) { super(support); @@ -123,7 +123,7 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation { @CallSuper protected void handleActivityFetchFinish(boolean success) { - GB.updateTransferNotification(null,"",false,100,getContext()); + GB.updateTransferNotification(null, "", false, 100, getContext()); operationFinished(); unsetBusy(); } @@ -175,13 +175,13 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation { }); } - protected void handleActivityMetadata(byte[] value) { + private void handleActivityMetadata(byte[] value) { if (value.length == 15) { // first two bytes are whether our request was accepted if (ArrayUtils.equals(value, HuamiService.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) { // the third byte (0x01 on success) = ? // the 4th - 7th bytes epresent the number of bytes/packets to expect, excluding the counter bytes - expectedDataLength = BLETypeConversions.toUint32(Arrays.copyOfRange(value, 3, 7)); + //int expectedDataLength = BLETypeConversions.toUint32(Arrays.copyOfRange(value, 3, 7)); // last 8 bytes are the start date Calendar startTimestamp = getSupport().fromTimeBytes(Arrays.copyOfRange(value, 7, value.length)); @@ -189,7 +189,7 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation { GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, - DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), true, 0, getContext());; + DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), true, 0, getContext()); } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); handleActivityFetchFinish(false); @@ -207,30 +207,30 @@ public abstract class AbstractFetchOperation extends AbstractHuamiOperation { } } - protected void setStartTimestamp(Calendar startTimestamp) { + private void setStartTimestamp(Calendar startTimestamp) { this.startTimestamp = startTimestamp; } - protected Calendar getLastStartTimestamp() { + Calendar getLastStartTimestamp() { return startTimestamp; } - protected void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { - SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); + void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { + SharedPreferences.Editor editor = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).edit(); editor.putLong(getLastSyncTimeKey(), timestamp.getTimeInMillis()); editor.apply(); } protected GregorianCalendar getLastSuccessfulSyncTime() { - long timeStampMillis = GBApplication.getPrefs().getLong(getLastSyncTimeKey(), 0); + long timeStampMillis = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getLong(getLastSyncTimeKey(), 0); if (timeStampMillis != 0) { GregorianCalendar calendar = BLETypeConversions.createCalendar(); calendar.setTimeInMillis(timeStampMillis); return calendar; } GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.add(Calendar.DAY_OF_MONTH, - 100); + calendar.add(Calendar.DAY_OF_MONTH, -100); return calendar; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchActivityOperation.java index f5c959565..45fe7f109 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchActivityOperation.java @@ -203,6 +203,6 @@ public class FetchActivityOperation extends AbstractFetchOperation { @Override protected String getLastSyncTimeKey() { - return getDevice().getAddress() + "_" + "lastSyncTimeMillis"; + return "lastSyncTimeMillis"; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsDetailsOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsDetailsOperation.java index 67aaab3b4..dddc326e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsDetailsOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsDetailsOperation.java @@ -40,7 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.ActivityDetailsParser; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiActivityDetailsParser; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -56,7 +56,7 @@ public class FetchSportsDetailsOperation extends AbstractFetchOperation { private ByteArrayOutputStream buffer; - public FetchSportsDetailsOperation(@NonNull BaseActivitySummary summary, @NonNull HuamiSupport support, @NonNull String lastSyncTimeKey) { + FetchSportsDetailsOperation(@NonNull BaseActivitySummary summary, @NonNull HuamiSupport support, @NonNull String lastSyncTimeKey) { super(support); setName("fetching sport details"); this.summary = summary; @@ -86,7 +86,7 @@ public class FetchSportsDetailsOperation extends AbstractFetchOperation { if (success) { - ActivityDetailsParser parser = new ActivityDetailsParser(summary); + HuamiActivityDetailsParser parser = new HuamiActivityDetailsParser(summary); parser.setSkipCounterByte(false); // is already stripped try { ActivityTrack track = parser.parse(buffer.toByteArray()); @@ -131,7 +131,7 @@ public class FetchSportsDetailsOperation extends AbstractFetchOperation { super.handleActivityFetchFinish(success); } - protected ActivityTrackExporter createExporter() { + private ActivityTrackExporter createExporter() { GPXExporter exporter = new GPXExporter(); exporter.setCreator(GBApplication.app().getNameAndVersion()); return exporter; @@ -169,7 +169,6 @@ public class FetchSportsDetailsOperation extends AbstractFetchOperation { } else { GB.toast("Error " + getName() + ", invalid package counter: " + value[0] + ", last was: " + lastPacketCounter, Toast.LENGTH_LONG, GB.ERROR); handleActivityFetchFinish(false); - return; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsSummaryOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsSummaryOperation.java index e8eb4cc2d..eb2d578a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsSummaryOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/FetchSportsSummaryOperation.java @@ -43,8 +43,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSportsActivityType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.BipActivityType; import nodomain.freeyourgadget.gadgetbridge.util.GB; /** @@ -141,7 +141,7 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation { return; } - if ((byte) (lastPacketCounter + 1) == value[0] ) { + if ((byte) (lastPacketCounter + 1) == value[0]) { lastPacketCounter++; bufferActivityData(value); } else { @@ -154,6 +154,7 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation { /** * Buffers the given activity summary data. If the total size is reached, * it is converted to an object and saved in the database. + * * @param value */ @Override @@ -166,14 +167,14 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation { ByteBuffer buffer = ByteBuffer.wrap(stream.toByteArray()).order(ByteOrder.LITTLE_ENDIAN); // summary.setVersion(BLETypeConversions.toUnsigned(buffer.getShort())); short version = buffer.getShort(); // version - LOG.debug("Got verison " + version); + LOG.debug("Got sport summary version " + version + "total bytes=" + buffer.capacity()); int activityKind = ActivityKind.TYPE_UNKNOWN; try { int rawKind = BLETypeConversions.toUnsigned(buffer.getShort()); - BipActivityType activityType = BipActivityType.fromCode(rawKind); + HuamiSportsActivityType activityType = HuamiSportsActivityType.fromCode(rawKind); activityKind = activityType.toActivityKind(); } catch (Exception ex) { - LOG.error("Error mapping acivity kind: " + ex.getMessage(), ex); + LOG.error("Error mapping activity kind: " + ex.getMessage(), ex); } summary.setActivityKind(activityKind); @@ -197,50 +198,123 @@ public class FetchSportsSummaryOperation extends AbstractFetchOperation { summary.setBaseLongitude(baseLongitude); summary.setBaseLatitude(baseLatitude); summary.setBaseAltitude(baseAltitude); + + // unused data (for now) + float distanceMeters = buffer.getFloat(); + float ascentMeters = buffer.getFloat(); + float descentMeters = buffer.getFloat(); + float maxAltitude = buffer.getFloat(); + float minAltitude = buffer.getFloat(); + int maxLatitude = buffer.getInt(); // format? + int minLatitude = buffer.getInt(); // format? + int maxLongitude = buffer.getInt(); // format? + int minLongitude = buffer.getInt(); // format? + int steps = buffer.getInt(); + int activeSeconds = buffer.getInt(); + float caloriesBurnt = buffer.getFloat(); + float maxSpeed = buffer.getFloat(); + float minPace = buffer.getFloat(); // format? + float maxPace = buffer.getFloat(); // format? + float totalStride = buffer.getFloat(); + + buffer.getInt(); // unknown + + if (activityKind == ActivityKind.TYPE_SWIMMING) { + // 28 bytes + float averageStrokeDistance = buffer.getFloat(); + float averageStrokesPerSecond = buffer.getFloat(); + float averageLapPace = buffer.getFloat(); + short strokes = buffer.getShort(); + short swolfIndex = buffer.getShort(); + byte swimStyle = buffer.get(); + byte laps = buffer.get(); + buffer.getInt(); // unknown + buffer.getInt(); // unknown + buffer.getShort(); // unknown + + LOG.debug("unused swim data:" + + "\naverageStrokeDistance=" + averageStrokeDistance + + "\naverageStrokesPerSecond=" + averageStrokesPerSecond + + "\naverageLapPace" + averageLapPace + + "\nstrokes=" + strokes + + "\nswolfIndex=" + swolfIndex + + "\nswimStyle=" + swimStyle + // 1 = breast, 2 = freestyle + "\nlaps=" + laps + + "" + ); + } else { + // 28 bytes + buffer.getInt(); // unknown + buffer.getInt(); // unknown + int ascentSeconds = buffer.getInt() / 1000; //ms? + buffer.getInt(); // unknown; + int descentSeconds = buffer.getInt() / 1000; //ms? + buffer.getInt(); // unknown; + int flatSeconds = buffer.getInt() / 1000; // ms? + LOG.debug("unused non-swim data:" + + "\nascentSeconds=" + ascentSeconds + + "\ndescentSeconds=" + descentSeconds + + "\nflatSeconds=" + flatSeconds + + "" + ); + } + + short averageHR = buffer.getShort(); + short averageKMPaceSeconds = buffer.getShort(); + short averageStride = buffer.getShort(); + + LOG.debug("unused common:" + + "\ndistanceMeters=" + distanceMeters + + "\nascentMeters=" + ascentMeters + + "\ndescentMeters=" + descentMeters + + "\nmaxAltitude=" + maxAltitude + + "\nminAltitude=" + minAltitude + + //"\nmaxLatitude=" + maxLatitude + // not useful + //"\nminLatitude=" + minLatitude + // not useful + //"\nmaxLongitude=" + maxLongitude + // not useful + //"\nminLongitude=" + minLongitude + // not useful + "\nsteps=" + steps + + "\nactiveSeconds=" + activeSeconds + + "\ncaloriesBurnt=" + caloriesBurnt + + "\nmaxSpeed=" + maxSpeed + + "\nminPace=" + minPace + + "\nmaxPace=" + maxPace + + "\ntotalStride=" + totalStride + + "\naverageHR=" + averageHR + + "\naverageKMPaceSeconds=" + averageKMPaceSeconds + + "\naverageStride=" + averageStride + + "" + ); + // summary.setBaseCoordinate(new GPSCoordinate(baseLatitude, baseLongitude, baseAltitude)); - -// summary.setDistanceMeters(Float.intBitsToFloat(buffer.getInt())); -// summary.setAscentMeters(Float.intBitsToFloat(buffer.getInt())); -// summary.setDescentMeters(Float.intBitsToFloat(buffer.getInt())); -// -// summary.setMinAltitude(Float.intBitsToFloat(buffer.getInt())); -// summary.setMaxAltitude(Float.intBitsToFloat(buffer.getInt())); -// summary.setMinLatitude(buffer.getInt()); -// summary.setMaxLatitude(buffer.getInt()); -// summary.setMinLongitude(buffer.getInt()); -// summary.setMaxLongitude(buffer.getInt()); -// -// summary.setSteps(BLETypeConversions.toUnsigned(buffer.getInt())); -// summary.setActiveTimeSeconds(BLETypeConversions.toUnsigned(buffer.getInt())); -// -// summary.setCaloriesBurnt(Float.intBitsToFloat(buffer.get())); -// summary.setMaxSpeed(Float.intBitsToFloat(buffer.get())); -// summary.setMinPace(Float.intBitsToFloat(buffer.get())); -// summary.setMaxPace(Float.intBitsToFloat(buffer.get())); -// summary.setTotalStride(Float.intBitsToFloat(buffer.get())); - - buffer.getInt(); // - buffer.getInt(); // - buffer.getInt(); // - -// summary.setTimeAscent(BLETypeConversions.toUnsigned(buffer.getInt())); -// buffer.getInt(); // -// summary.setTimeDescent(BLETypeConversions.toUnsigned(buffer.getInt())); -// buffer.getInt(); // -// summary.setTimeFlat(BLETypeConversions.toUnsigned(buffer.getInt())); -// -// summary.setAverageHR(BLETypeConversions.toUnsigned(buffer.getShort())); -// -// summary.setAveragePace(BLETypeConversions.toUnsigned(buffer.getShort())); -// summary.setAverageStride(BLETypeConversions.toUnsigned(buffer.getShort())); - - buffer.getShort(); // +// summary.setDistanceMeters(distanceMeters); +// summary.setAscentMeters(ascentMeters); +// summary.setDescentMeters(descentMeters); +// summary.setMinAltitude(maxAltitude); +// summary.setMaxAltitude(maxAltitude); +// summary.setMinLatitude(minLatitude); +// summary.setMaxLatitude(maxLatitude); +// summary.setMinLongitude(minLatitude); +// summary.setMaxLongitude(maxLatitude); +// summary.setSteps(steps); +// summary.setActiveTimeSeconds(secondsActive); +// summary.setCaloriesBurnt(caloriesBurnt); +// summary.setMaxSpeed(maxSpeed); +// summary.setMinPace(minPace); +// summary.setMaxPace(maxPace); +// summary.setTotalStride(totalStride); +// summary.setTimeAscent(BLETypeConversions.toUnsigned(ascentSeconds); +// summary.setTimeDescent(BLETypeConversions.toUnsigned(descentSeconds); +// summary.setTimeFlat(BLETypeConversions.toUnsigned(flatSeconds); +// summary.setAverageHR(BLETypeConversions.toUnsigned(averageHR); +// summary.setAveragePace(BLETypeConversions.toUnsigned(averagePace); +// summary.setAverageStride(BLETypeConversions.toUnsigned(averageStride); return summary; } @Override protected String getLastSyncTimeKey() { - return getDevice().getAddress() + "_" + "lastSportsActivityTimeMillis"; + return "lastSportsActivityTimeMillis"; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/HuamiFetchDebugLogsOperation.java similarity index 89% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/HuamiFetchDebugLogsOperation.java index eb4a7c8b8..3d4471321 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/HuamiFetchDebugLogsOperation.java @@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations; import android.widget.Toast; @@ -36,18 +36,17 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipS import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { - private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipFetchLogsOperation.class); +public class HuamiFetchDebugLogsOperation extends AbstractFetchOperation { + private static final Logger LOG = LoggerFactory.getLogger(HuamiFetchDebugLogsOperation.class); private FileOutputStream logOutputStream; - public AmazfitBipFetchLogsOperation(AmazfitBipSupport support) { + public HuamiFetchDebugLogsOperation(AmazfitBipSupport support) { super(support); - setName("fetch logs"); + setName("fetch debug logs"); } @Override @@ -60,7 +59,7 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { } SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US); - String filename = "amazfitbip_" + dateFormat.format(new Date()) + ".log"; + String filename = "huamidebug_" + dateFormat.format(new Date()) + ".log"; File outputFile = new File(dir, filename ); try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java index 35f973cbd..b655a8527 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/InitOperation.java @@ -16,16 +16,16 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.content.SharedPreferences; import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.security.InvalidKeyException; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.UUID; @@ -36,6 +36,7 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; @@ -50,21 +51,25 @@ public class InitOperation extends AbstractBTLEOperation { private final TransactionBuilder builder; private final boolean needsAuth; private final byte authFlags; + private final byte cryptFlags; + private final HuamiSupport huamiSupport; - public InitOperation(boolean needsAuth, byte authFlags, HuamiSupport support, TransactionBuilder builder) { + public InitOperation(boolean needsAuth, byte authFlags, byte cryptFlags, HuamiSupport support, TransactionBuilder builder) { super(support); + this.huamiSupport = support; this.needsAuth = needsAuth; this.authFlags = authFlags; + this.cryptFlags = cryptFlags; this.builder = builder; builder.setGattCallback(this); } @Override - protected void doPerform() throws IOException { - getSupport().enableNotifications(builder, true); + protected void doPerform() { + huamiSupport.enableNotifications(builder, true); if (needsAuth) { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.AUTHENTICATING, getContext())); - // write key to miband2 + // write key to device byte[] sendKey = org.apache.commons.lang3.ArrayUtils.addAll(new byte[]{HuamiService.AUTH_SEND_KEY, authFlags}, getSecretKey()); builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUTH), sendKey); } else { @@ -75,15 +80,32 @@ public class InitOperation extends AbstractBTLEOperation { } private byte[] requestAuthNumber() { - return new byte[]{HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER, authFlags}; + if (cryptFlags == 0x00) { + return new byte[]{HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER, authFlags}; + } else { + return new byte[]{(byte) (cryptFlags | HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER), authFlags, 0x02}; + } } private byte[] getSecretKey() { - return new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45}; + byte[] authKeyBytes = new byte[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45}; + + SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); + + String authKey = sharedPrefs.getString("authkey", null); + if (authKey != null && !authKey.isEmpty()) { + byte[] srcBytes = authKey.getBytes(); + if (authKey.length() == 34 && authKey.substring(0, 2).equals("0x")) { + srcBytes = GB.hexStringToByteArray(authKey.substring(2)); + } + System.arraycopy(srcBytes, 0, authKeyBytes, 0, Math.min(srcBytes.length, 16)); + } + + return authKeyBytes; } @Override - public TransactionBuilder performInitialized(String taskName) throws IOException { + public TransactionBuilder performInitialized(String taskName) { throw new UnsupportedOperationException("This IS the initialization class, you cannot call this method"); } @@ -94,41 +116,40 @@ public class InitOperation extends AbstractBTLEOperation { if (HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) { try { byte[] value = characteristic.getValue(); - getSupport().logMessageContent(value); + huamiSupport.logMessageContent(value); if (value[0] == HuamiService.AUTH_RESPONSE && value[1] == HuamiService.AUTH_SEND_KEY && value[2] == HuamiService.AUTH_SUCCESS) { - TransactionBuilder builder = createTransactionBuilder("Sending the secret key to the band"); + TransactionBuilder builder = createTransactionBuilder("Sending the secret key to the device"); builder.write(characteristic, requestAuthNumber()); - getSupport().performImmediately(builder); + huamiSupport.performImmediately(builder); } else if (value[0] == HuamiService.AUTH_RESPONSE && - value[1] == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER && + (value[1] & 0x0f) == HuamiService.AUTH_REQUEST_RANDOM_AUTH_NUMBER && value[2] == HuamiService.AUTH_SUCCESS) { - // md5?? byte[] eValue = handleAESAuth(value, getSecretKey()); byte[] responseValue = org.apache.commons.lang3.ArrayUtils.addAll( - new byte[]{HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER, authFlags}, eValue); + new byte[]{(byte) (HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER | cryptFlags), authFlags}, eValue); - TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the band"); + TransactionBuilder builder = createTransactionBuilder("Sending the encrypted random key to the device"); builder.write(characteristic, responseValue); - getSupport().setCurrentTimeWithService(builder); - getSupport().performImmediately(builder); + huamiSupport.setCurrentTimeWithService(builder); + huamiSupport.performImmediately(builder); } else if (value[0] == HuamiService.AUTH_RESPONSE && - value[1] == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER && + (value[1] & 0x0f) == HuamiService.AUTH_SEND_ENCRYPTED_AUTH_NUMBER && value[2] == HuamiService.AUTH_SUCCESS) { TransactionBuilder builder = createTransactionBuilder("Authenticated, now initialize phase 2"); builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); - getSupport().requestDeviceInfo(builder); - getSupport().enableFurtherNotifications(builder, true); - getSupport().phase2Initialize(builder); - getSupport().phase3Initialize(builder); - getSupport().setInitialized(builder); - getSupport().performImmediately(builder); + huamiSupport.requestDeviceInfo(builder); + huamiSupport.enableFurtherNotifications(builder, true); + huamiSupport.phase2Initialize(builder); + huamiSupport.phase3Initialize(builder); + huamiSupport.setInitialized(builder); + huamiSupport.performImmediately(builder); } else { return super.onCharacteristicChanged(gatt, characteristic); } } catch (Exception e) { - GB.toast(getContext(), "Error authenticating Mi Band 2", Toast.LENGTH_LONG, GB.ERROR, e); + GB.toast(getContext(), "Error authenticating Huami device", Toast.LENGTH_LONG, GB.ERROR, e); } return true; } else { @@ -137,17 +158,11 @@ public class InitOperation extends AbstractBTLEOperation { } } - private byte[] getMD5(byte[] message) throws NoSuchAlgorithmException { - MessageDigest md5 = MessageDigest.getInstance("MD5"); - return md5.digest(message); - } - private byte[] handleAESAuth(byte[] value, byte[] secretKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException { byte[] mValue = Arrays.copyOfRange(value, 3, 19); - Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding"); + @SuppressLint("GetInstance") Cipher ecipher = Cipher.getInstance("AES/ECB/NoPadding"); SecretKeySpec newKey = new SecretKeySpec(secretKey, "AES"); ecipher.init(Cipher.ENCRYPT_MODE, newKey); - byte[] enc = ecipher.doFinal(mValue); - return enc; + return ecipher.doFinal(mValue); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java index de1178000..07ca209cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperation.java @@ -50,8 +50,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { private static final Logger LOG = LoggerFactory.getLogger(UpdateFirmwareOperation.class); protected final Uri uri; - protected final BluetoothGattCharacteristic fwCControlChar; - protected final BluetoothGattCharacteristic fwCDataChar; + final BluetoothGattCharacteristic fwCControlChar; + private final BluetoothGattCharacteristic fwCDataChar; protected final Prefs prefs = GBApplication.getPrefs(); protected HuamiFirmwareInfo firmwareInfo; @@ -81,7 +81,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { //the firmware will be sent by the notification listener if the band confirms that the metadata are ok. } - protected HuamiFirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { + private HuamiFirmwareInfo createFwInfo(Uri uri, Context context) throws IOException { HuamiFWHelper fwHelper = getSupport().createFWHelper(uri, context); return fwHelper.getFirmwareInfo(); } @@ -128,8 +128,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { * @param value */ private void handleNotificationNotif(byte[] value) { - if (value.length != 3) { - LOG.error("Notifications should be 3 bytes long."); + if (value.length != 3 && value.length != 11) { + LOG.error("Notifications should be 3 or 11 bytes long."); getSupport().logMessageContent(value); return; } @@ -160,7 +160,6 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { case HuamiService.COMMAND_FIRMWARE_REBOOT: { LOG.info("Reboot command successfully sent."); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext()); -// getSupport().onReboot(); done(); break; } @@ -170,7 +169,6 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { operationFailed(); displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); - return; } } } catch (Exception ex) { @@ -185,7 +183,8 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { done(); } } - protected void displayMessage(Context context, String message, int duration, int severity) { + + private void displayMessage(Context context, String message, int duration, int severity) { getSupport().handleGBDeviceEvent(new GBDeviceEventDisplayMessage(message, duration, severity)); } @@ -208,7 +207,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { bytes[i++] = sizeBytes[1]; bytes[i++] = sizeBytes[2]; if (!isFirmwareCode) { - bytes[i++] = getFirmwareInfo().getFirmwareType().getValue(); + bytes[i] = getFirmwareInfo().getFirmwareType().getValue(); } builder.write(fwCControlChar, bytes); @@ -241,10 +240,7 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); - if (prefs.getBoolean("mi_low_latency_fw_update", true)) { - getSupport().setLowLatency(builder); - } - builder.write(fwCControlChar, new byte[] { HuamiService.COMMAND_FIRMWARE_START_DATA }); + builder.write(fwCControlChar, getFirmwareStartCommand()); for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); @@ -262,14 +258,13 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { if (firmwareProgress < len) { byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); builder.write(fwCDataChar, lastChunk); - firmwareProgress = len; } builder.write(fwCControlChar, new byte[]{HuamiService.COMMAND_FIRMWARE_UPDATE_SYNC}); builder.queue(getQueue()); } catch (IOException ex) { - LOG.error("Unable to send fw to MI 2", ex); + LOG.error("Unable to send fw to device", ex); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_firmware_not_sent), false, 0, getContext()); return false; } @@ -277,11 +272,11 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { } - private void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { + protected void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { TransactionBuilder builder = performInitialized("send firmware checksum"); int crc16 = firmwareInfo.getCrc16(); byte[] bytes = BLETypeConversions.fromUint16(crc16); - builder.write(fwCControlChar, new byte[] { + builder.write(fwCControlChar, new byte[]{ HuamiService.COMMAND_FIRMWARE_CHECKSUM, bytes[0], bytes[1], @@ -289,7 +284,11 @@ public class UpdateFirmwareOperation extends AbstractHuamiOperation { builder.queue(getQueue()); } - private HuamiFirmwareInfo getFirmwareInfo() { + HuamiFirmwareInfo getFirmwareInfo() { return firmwareInfo; } -} + + protected byte[] getFirmwareStartCommand() { + return new byte[]{HuamiService.COMMAND_FIRMWARE_START_DATA}; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java new file mode 100644 index 000000000..e2991366c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/operations/UpdateFirmwareOperationNew.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti + + 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.service.devices.huami.operations; + +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; + +public class UpdateFirmwareOperationNew extends UpdateFirmwareOperation { + private static final Logger LOG = LoggerFactory.getLogger(UpdateFirmwareOperationNew.class); + + + public UpdateFirmwareOperationNew(Uri uri, HuamiSupport support) { + super(uri, support); + } + + + public boolean sendFwInfo() { + try { + TransactionBuilder builder = performInitialized("send firmware info"); + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); + int fwSize = getFirmwareInfo().getSize(); + byte[] sizeBytes = BLETypeConversions.fromUint24(fwSize); + byte[] bytes = new byte[10]; + int i = 0; + bytes[i++] = HuamiService.COMMAND_FIRMWARE_INIT; + bytes[i++] = getFirmwareInfo().getFirmwareType().getValue(); + bytes[i++] = sizeBytes[0]; + bytes[i++] = sizeBytes[1]; + bytes[i++] = sizeBytes[2]; + bytes[i++] = 0; // TODO: what is that? + int crc32 = firmwareInfo.getCrc32(); + byte[] crcBytes = BLETypeConversions.fromUint32(crc32); + bytes[i++] = crcBytes[0]; + bytes[i++] = crcBytes[1]; + bytes[i++] = crcBytes[2]; + bytes[i] = crcBytes[3]; + + + builder.write(fwCControlChar, bytes); + builder.queue(getQueue()); + return true; + } catch (IOException e) { + LOG.error("Error sending firmware info: " + e.getLocalizedMessage(), e); + return false; + } + } + + @Override + protected void sendChecksum(HuamiFirmwareInfo firmwareInfo) throws IOException { + TransactionBuilder builder = performInitialized("send firmware upload finished"); + builder.write(fwCControlChar, new byte[]{HuamiService.COMMAND_FIRMWARE_CHECKSUM}); + builder.queue(getQueue()); + } + + @Override + protected byte[] getFirmwareStartCommand() { + return new byte[]{HuamiService.COMMAND_FIRMWARE_START_DATA, 1}; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/id115/ID115Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/id115/ID115Support.java index 33e400ad1..77363230d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/id115/ID115Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/id115/ID115Support.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, Vadim Kaushan +/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, Sebastian + Kranz, Vadim Kaushan This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/BFH16DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/BFH16DeviceSupport.java new file mode 100644 index 000000000..923f99412 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/BFH16DeviceSupport.java @@ -0,0 +1,644 @@ +/* Copyright (C) 2019 Sophanimus + + 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 . */ + +/* + Features: + Working + work in progress + possible + not supported + Get firmware version (X) () (X) () + Get battery level (X) () (X) () + + Set alarms (1-3) (X) () (X) () + Sync date and time (X) () (X) () + Find device (X) () (X) () + + Switch 12/24 hour mode () () (X) () + Set step goal () () (X) () + Set sitting reminder () () (X) () + Trigger a photo () () (X) () + + Switch automated heartbeat detection (X) () (X) () + Switch display illumination () () (X) () + Switch vibration () () (X) () + Switch notifications () () (X) () + Set do not distract time () () (X) () + + Get Steps () () (X) () + Get Heart Rate () () (X) () + Get Blood Pressure () () (x) () + Get Blood Satiation () () (X) () + + Send Notification () () (X) () + + */ + + +package nodomain.freeyourgadget.gadgetbridge.service.devices.jyou; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.net.Uri; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16Constants; +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.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; + +public class BFH16DeviceSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(BFH16DeviceSupport.class); + + public BluetoothGattCharacteristic ctrlCharacteristic = null; + public BluetoothGattCharacteristic measureCharacteristic = null; + + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + + public BFH16DeviceSupport() { + super(LOG); + addSupportedService(BFH16Constants.BFH16_SERVICE1); + addSupportedService(BFH16Constants.BFH16_SERVICE2); + } + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + LOG.info("Initializing BFH16"); + + gbDevice.setState(GBDevice.State.INITIALIZING); + gbDevice.sendDeviceUpdateIntent(getContext()); + + measureCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_NOTIFY); + ctrlCharacteristic = getCharacteristic(BFH16Constants.BFH16_SERVICE1_WRITE); + + builder.setGattCallback(this); + builder.notify(measureCharacteristic, true); + + syncSettings(builder); + + gbDevice.setState(GBDevice.State.INITIALIZED); + gbDevice.sendDeviceUpdateIntent(getContext()); + + LOG.info("Initialization BFH16 Done"); + + return builder; + } + + //onXYZ + //______________________________________________________________________________________________ + + //TODO check TODOs in method + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + UUID characteristicUUID = characteristic.getUuid(); + byte[] data = characteristic.getValue(); + if (data.length == 0) + return true; + + switch (data[0]) { + case BFH16Constants.RECEIVE_DEVICE_INFO: + int fwVerNum = data[4] & 0xFF; + versionCmd.fwVersion = (fwVerNum / 100) + "." + ((fwVerNum % 100) / 10) + "." + ((fwVerNum % 100) % 10); + handleGBDeviceEvent(versionCmd); + LOG.info("Firmware version is: " + versionCmd.fwVersion); + return true; + case BFH16Constants.RECEIVE_BATTERY_LEVEL: + batteryCmd.level = data[8]; + handleGBDeviceEvent(batteryCmd); + LOG.info("Battery level is: " + batteryCmd.level); + return true; + case BFH16Constants.RECEIVE_STEPS_DATA: + int steps = ByteBuffer.wrap(data, 5, 4).getInt(); + //TODO handle step data + LOG.info("Number of walked steps: " + steps); + return true; + case BFH16Constants.RECEIVE_HEART_DATA: + //TODO handle heart data + LOG.info("Current heart rate: " + data[1]); + LOG.info("Current blood pressure: " + data[2] + "/" + data[3]); + LOG.info("Current satiation: " + data[4]); + return true; + case BFH16Constants.RECEIVE_PHOTO_TRIGGER: + //TODO handle photo trigger + LOG.info("Received photo trigger: " + data[8]); + return true; + default: + LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0])); + LOG.info("Unhandled characteristic data: "+ data[0]+" "+data[1]+" "+data[2]+" "+data[3]+" "+data[4]+" "+data[5]+" "+data[6]+" "+data[7]+" "+data[8]); + return true; + } + } + + + //working + @Override + public void onSetAlarms(ArrayList alarms) { + try { + TransactionBuilder builder = performInitialized("SetAlarms"); + + for (int i = 0; i < alarms.size(); i++) + { + byte cmd; + switch (i) { + case 0: + cmd = BFH16Constants.CMD_SET_ALARM_1; + break; + case 1: + cmd = BFH16Constants.CMD_SET_ALARM_2; + break; + case 2: + cmd = BFH16Constants.CMD_SET_ALARM_3; + break; + default: + return; + } + Calendar cal = AlarmUtils.toCalendar(alarms.get(i)); + builder.write(ctrlCharacteristic, commandWithChecksum( + cmd, + alarms.get(i).getEnabled() ? cal.get(Calendar.HOUR_OF_DAY) : -1, + alarms.get(i).getEnabled() ? cal.get(Calendar.MINUTE) : -1 + )); + } + builder.queue(getQueue()); + GB.toast(getContext(), "Alarm settings applied - do note that the current device does not support day specification", Toast.LENGTH_LONG, GB.INFO); + } catch(IOException e) { + LOG.warn(e.getMessage()); + } + } + + //working + @Override + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("SetTime"); + syncDateAndTime(builder); + builder.queue(getQueue()); + } catch(IOException e) { + LOG.warn(e.getMessage()); + } + } + + //working + @Override + public void onFindDevice(boolean start) { + try { + TransactionBuilder builder = performInitialized("FindDevice"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_VIBRATE, 0, start ? 1 : 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } + GB.toast(getContext(), "Your device will vibrate 3 times!", Toast.LENGTH_LONG, GB.INFO); + } + + + //TODO: checked + rework + @Override + public void onNotification(NotificationSpec notificationSpec) { + String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title); + byte icon; + switch (notificationSpec.type) { + case GENERIC_SMS: + icon = BFH16Constants.ICON_SMS; + break; + case FACEBOOK: + case FACEBOOK_MESSENGER: + icon = BFH16Constants.ICON_FACEBOOK; + break; + case TWITTER: + icon = BFH16Constants.ICON_TWITTER; + break; + case WHATSAPP: + icon = BFH16Constants.ICON_WHATSAPP; + break; + default: + icon = BFH16Constants.ICON_LINE; + break; + } + showNotification(icon, notificationTitle, notificationSpec.body); + } + + @Override + public void onDeleteNotification(int id) { + + } + + //TODO: check + @Override + public void onSetCallState(CallSpec callSpec) { + switch (callSpec.command) { + case CallSpec.CALL_INCOMING: + showNotification(BFH16Constants.ICON_CALL, callSpec.name, callSpec.number); + break; + } + } + + //TODO: check + @Override + public void onEnableRealtimeSteps(boolean enable) { + onEnableRealtimeHeartRateMeasurement(enable); + } + + //TODO: check + @Override + public void onReset(int flags) { + try { + TransactionBuilder builder = performInitialized("Reboot"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_ACTION_REBOOT_DEVICE, 0, 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } + } + + //TODO: check + @Override + public void onHeartRateTest() { + try { + TransactionBuilder builder = performInitialized("HeartRateTest"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_MEASURE_HEART, 0, 1 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } + } + + //TODO: check + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + // TODO: test + try { + TransactionBuilder builder = performInitialized("RealTimeHeartMeasurement"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_MEASURE_HEART, 0, enable ? 1 : 0 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } + } + + //TODO: check + @Override + public void onSetConstantVibration(int integer) { + try { + TransactionBuilder builder = performInitialized("Vibrate"); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_VIBRATE, 0, 1 + )); + builder.queue(getQueue()); + } catch(Exception e) { + LOG.warn(e.getMessage()); + } + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchRecordedData(int dataTypes) { + + } + + @Override + public void onScreenshotReq() { + + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + + } + + @Override + public void onSendConfiguration(String config) { + + } + + @Override + public void onReadConfiguration(String config) { + + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } + + @Override + public void onTestNewFunction() { + + showNotification((byte)0xFF, "", ""); + +// try { +// TransactionBuilder builder = performInitialized("TestNewFunction"); +// +// //Test new command +// builder.write(ctrlCharacteristic, commandWithChecksum( (byte)0x32, 0, 0)); +// builder.queue(getQueue()); +// +// GB.toast(getContext(), "TestNewFunction executed!", Toast.LENGTH_LONG, GB.INFO); +// +// } catch(IOException e) { +// LOG.warn(e.getMessage()); +// } + + } + + + + //FUNCTIONS + //______________________________________________________________________________________________ + + //TODO: check + private void showNotification(byte icon, String title, String message) { + try { + TransactionBuilder builder = performInitialized("ShowNotification"); + + byte[] titleBytes = stringToUTF8Bytes(title, 16); + byte[] messageBytes = stringToUTF8Bytes(message, 80); + + for (int i = 1; i <= 7; i++) + { + byte[] currentPacket = new byte[20]; + currentPacket[0] = BFH16Constants.CMD_ACTION_SHOW_NOTIFICATION; + currentPacket[1] = 7; + currentPacket[2] = (byte)i; + switch(i) { + case 1: + currentPacket[4] = icon; + break; + case 2: + if (titleBytes != null) { + System.arraycopy(titleBytes, 0, currentPacket, 3, 6); + System.arraycopy(titleBytes, 6, currentPacket, 10, 10); + } + break; + default: + if (messageBytes != null) { + System.arraycopy(messageBytes, 16 * (i - 3), currentPacket, 3, 6); + System.arraycopy(messageBytes, 6 + 16 * (i - 3), currentPacket, 10, 10); + } + break; + } + builder.write(ctrlCharacteristic, currentPacket); + } + builder.queue(getQueue()); + } catch (IOException e) { + LOG.warn(e.getMessage()); + } + } + + + //TODO: check + private void syncSettings(TransactionBuilder builder) { + syncDateAndTime(builder); + + // TODO: unhardcode and separate stuff + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_HEARTRATE_WARNING_VALUE, 0, 152 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_TARGET_STEPS, 0, 10000 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SWITCH_METRIC_IMPERIAL, 0, 0 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_GET_SLEEP_TIME, 0, 0 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_NOON_TIME, 12 * 60 * 60, 14 * 60 * 60 // 12:00 - 14:00 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_SLEEP_TIME, 21 * 60 * 60, 8 * 60 * 60 // 21:00 - 08:00 + )); + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_INACTIVITY_WARNING_TIME, 0, 0 + )); + + // do not disturb and a couple more features + byte dndStartHour = 22; + byte dndStartMin = 0; + byte dndEndHour = 8; + byte dndEndMin = 0; + boolean dndToggle = false; + boolean vibrationToggle = true; + boolean wakeOnRaiseToggle = true; + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_DND_SETTINGS, + (dndStartHour << 24) | (dndStartMin << 16) | (dndEndHour << 8) | dndEndMin, + ((dndToggle ? 0 : 1) << 2) | ((vibrationToggle ? 1 : 0) << 1) | (wakeOnRaiseToggle ? 1 : 0) + )); + } + + + //working + private void syncDateAndTime(TransactionBuilder builder) { + Calendar cal = Calendar.getInstance(); + String strYear = String.valueOf(cal.get(Calendar.YEAR)); + byte year1 = (byte)Integer.parseInt(strYear.substring(0, 2)); + byte year2 = (byte)Integer.parseInt(strYear.substring(2, 4)); + byte month = (byte)cal.get(Calendar.MONTH); + byte day = (byte)cal.get(Calendar.DAY_OF_MONTH); + byte hour = (byte)cal.get(Calendar.HOUR_OF_DAY); + byte minute = (byte)cal.get(Calendar.MINUTE); + byte second = (byte)cal.get(Calendar.SECOND); + byte weekDay = (byte)cal.get(Calendar.DAY_OF_WEEK); + + builder.write(ctrlCharacteristic, commandWithChecksum( + BFH16Constants.CMD_SET_DATE_AND_TIME, + (year1 << 24) | (year2 << 16) | (month << 8) | day, + (hour << 24) | (minute << 16) | (second << 8) | weekDay + )); + } + + //working + private byte[] commandWithChecksum(byte cmd, int argSlot1, int argSlot2) + { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(cmd); + buf.putInt(argSlot1); + buf.putInt(argSlot2); + + byte[] bytesToWrite = buf.array(); + + byte checksum = 0; + for (byte b : bytesToWrite) { + checksum += b; + } + + bytesToWrite[9] = checksum; + + return bytesToWrite; + } + + /** + * Checksum is calculated by the sum of bytes 0 to 8 and send as byte 9 + */ + private byte[] commandWithChecksum(byte s0, byte s1, byte s2, byte s3, byte s4, byte s5, byte s6, byte s7, byte s8) + { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(s0); + buf.put(s1); + buf.put(s2); + buf.put(s3); + buf.put(s4); + buf.put(s5); + buf.put(s6); + buf.put(s7); + buf.put(s8); + + byte[] bytesToWrite = buf.array(); + + byte checksum = 0; + for (byte b : bytesToWrite) { + checksum += b; + } + + //checksum = (byte) ((byte) checksum & (byte) 0xFF); //TODO EXPERIMENTAL + + LOG.debug("Checksum = " + checksum); + + bytesToWrite[9] = checksum; + + return bytesToWrite; + } + + private byte[] stringToUTF8Bytes(String src, int byteCount) { + try { + if (src == null) + return null; + + for (int i = src.length(); i > 0; i--) { + String sub = src.substring(0, i); + byte[] subUTF8 = sub.getBytes("UTF-8"); + + if (subUTF8.length == byteCount) { + return subUTF8; + } + + if (subUTF8.length < byteCount) { + byte[] largerSubUTF8 = new byte[byteCount]; + System.arraycopy(subUTF8, 0, largerSubUTF8, 0, subUTF8.length); + return largerSubUTF8; + } + } + } catch (UnsupportedEncodingException e) { + LOG.warn(e.getMessage()); + } + return null; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java index 92b1c2ec8..368966e15 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer, Sami Alaoui +/* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer, Sami Alaoui, + Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java index d696b9b5e..c772be99d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2019 Andreas Shimokawa, Daniele Gobbetti +/* Copyright (C) 2016-2019 Andreas Shimokawa, Daniele Gobbetti, Sebastian + Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java index 20ed2ed9a..66910462e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java @@ -59,13 +59,14 @@ public class BatteryInfo extends AbstractInfo { return BatteryState.UNKNOWN; } - public GregorianCalendar getLastChargeTime() { + public GregorianCalendar getLastChargeTime(String deviceAddress) { GregorianCalendar lastCharge = MiBandDateConverter.createCalendar(); if (mData.length >= 10) { lastCharge = MiBandDateConverter.rawBytesToCalendar(new byte[]{ - mData[1], mData[2], mData[3], mData[4], mData[5], mData[6] - }); + mData[1], mData[2], mData[3], mData[4], mData[5], mData[6]}, + deviceAddress + ); } return lastCharge; 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 534ab6ade..72dcffb8d 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 @@ -1,6 +1,6 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, atkyritsis, Carsten Pfeiffer, Christian Fischer, Daniele Gobbetti, freezed-or-frozen, JohnnySun, Julien - Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld + Pivotto, Kasha, Sebastian Kranz, Sergey Trofimov, Steffen Liebergeld This file is part of Gadgetbridge. @@ -625,7 +625,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { Calendar now = GregorianCalendar.getInstance(); Date date = now.getTime(); LOG.info("Sending current time to Mi Band: " + DateTimeUtils.formatDate(date) + " (" + date.toGMTString() + ")"); - byte[] nowBytes = MiBandDateConverter.calendarToRawBytes(now); + byte[] nowBytes = MiBandDateConverter.calendarToRawBytes(now, gbDevice.getAddress()); byte[] time = new byte[]{ nowBytes[0], nowBytes[1], @@ -943,7 +943,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { public void logDate(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { - GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value); + GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value, gbDevice.getAddress()); LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime())); } else { logMessageContent(value); @@ -1134,7 +1134,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { * @param characteristic */ private void queueAlarm(Alarm alarm, TransactionBuilder builder, BluetoothGattCharacteristic characteristic) { - byte[] alarmCalBytes = MiBandDateConverter.calendarToRawBytes(AlarmUtils.toCalendar(alarm)); + byte[] alarmCalBytes = MiBandDateConverter.calendarToRawBytes(AlarmUtils.toCalendar(alarm), gbDevice.getAddress()); byte[] alarmMessage = new byte[]{ MiBandService.COMMAND_SET_TIMER, @@ -1172,7 +1172,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { BatteryInfo info = new BatteryInfo(value); batteryCmd.level = ((short) info.getLevelInPercent()); batteryCmd.state = info.getState(); - batteryCmd.lastChargeTime = info.getLastChargeTime(); + batteryCmd.lastChargeTime = info.getLastChargeTime(gbDevice.getAddress()); batteryCmd.numCharges = info.getNumCharges(); handleGBDeviceEvent(batteryCmd); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index 391d129a3..0c275a476 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -254,7 +254,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { // byte 0 is the data type: 1 means that each minute is represented by a triplet of bytes int dataType = value[0]; // byte 1 to 6 represent a timestamp - GregorianCalendar timestamp = MiBandDateConverter.rawBytesToCalendar(value, 1); + GregorianCalendar timestamp = MiBandDateConverter.rawBytesToCalendar(value, 1, getDevice().getAddress()); // counter of all data held by the band int totalDataToRead = (value[7] & 0xff) | ((value[8] & 0xff) << 8); @@ -405,7 +405,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { * @param bytesTransferred */ private void sendAckDataTransfer(Calendar time, int bytesTransferred) { - byte[] ackTime = MiBandDateConverter.calendarToRawBytes(time); + byte[] ackTime = MiBandDateConverter.calendarToRawBytes(time, getDevice().getAddress()); Prefs prefs = GBApplication.getPrefs(); byte[] ackChecksum = new byte[]{ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java index 035ef65a3..05f99a398 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java @@ -33,6 +33,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -187,7 +188,6 @@ public class UpdateFirmwareOperation extends AbstractMiBand1Operation { * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it. * * @param newFwVersion - * @see MiBandSupport#handleNotificationNotif */ private UpdateCoordinator prepareFirmwareInfo(byte[] fwBytes, int newFwVersion) throws IOException { int newFwSize = fwBytes.length; @@ -297,7 +297,6 @@ public class UpdateFirmwareOperation extends AbstractMiBand1Operation { * * @param fwbytes * @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail. - * @see MiBandSupport#handleNotificationNotif */ private boolean sendFirmwareData(byte[] fwbytes) { int len = fwbytes.length; @@ -311,7 +310,7 @@ public class UpdateFirmwareOperation extends AbstractMiBand1Operation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); - if (prefs.getBoolean("mi_low_latency_fw_update", true)) { + if (GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()).getBoolean("low_latency_fw_update", true)) { getSupport().setLowLatency(builder); } for (int i = 0; i < packets; i++) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd02/MijiaLywsd02Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd02/MijiaLywsd02Support.java new file mode 100644 index 000000000..bc8dc318f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/mijia_lywsd02/MijiaLywsd02Support.java @@ -0,0 +1,293 @@ +/* Copyright (C) 2019 Andreas Shimokawa + + 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.service.devices.mijia_lywsd02; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Intent; +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Objects; +import java.util.SimpleTimeZone; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +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.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile; +import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; + +public class MijiaLywsd02Support extends AbstractBTLEDeviceSupport { + + private static final Logger LOG = LoggerFactory.getLogger(MijiaLywsd02Support.class); + private final DeviceInfoProfile deviceInfoProfile; + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); + private final IntentListener mListener = new IntentListener() { + @Override + public void notify(Intent intent) { + String s = intent.getAction(); + if (Objects.equals(s, DeviceInfoProfile.ACTION_DEVICE_INFO)) { + handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO)); + } + } + }; + + public MijiaLywsd02Support() { + super(LOG); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); + addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); + addSupportedService(GattService.UUID_SERVICE_DEVICE_INFORMATION); + addSupportedService(UUID.fromString("ebe0ccb0-7a0a-4b0c-8a1a-6ff2997da3a6")); + deviceInfoProfile = new DeviceInfoProfile<>(this); + deviceInfoProfile.addListener(mListener); + addSupportedProfile(deviceInfoProfile); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())); + requestDeviceInfo(builder); + setTime(builder); + setInitialized(builder); + return builder; + } + + private void setTime(TransactionBuilder builder) { + BluetoothGattCharacteristic timeCharacteristc = getCharacteristic(UUID.fromString("ebe0ccb7-7a0a-4b0c-8a1a-6ff2997da3a6")); + long ts = System.currentTimeMillis(); + byte offsetHours = (byte) (SimpleTimeZone.getDefault().getOffset(ts) / (1000 * 60 * 60)); + ts /= 1000; + builder.write(timeCharacteristc, new byte[]{ + (byte) (ts & 0xff), + (byte) ((ts >> 8) & 0xff), + (byte) ((ts >> 16) & 0xff), + (byte) ((ts >> 24) & 0xff), + offsetHours}); + } + + private void requestDeviceInfo(TransactionBuilder builder) { + LOG.debug("Requesting Device Info!"); + deviceInfoProfile.requestDeviceInfo(builder); + } + + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + private void handleDeviceInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo info) { + LOG.warn("Device info: " + info); + versionCmd.hwVersion = info.getHardwareRevision(); + versionCmd.fwVersion = info.getFirmwareRevision(); + handleGBDeviceEvent(versionCmd); + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + } + + @Override + public void onDeleteNotification(int id) { + + } + + @Override + public void onSetTime() { + // better only on connect for now + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onSetCallState(CallSpec callSpec) { + + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchRecordedData(int dataTypes) { + + } + + @Override + public void onReset(int flags) { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } + + @Override + public void onFindDevice(boolean start) { + } + + @Override + public void onSetConstantVibration(int intensity) { + + } + + @Override + public void onScreenshotReq() { + + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + + } + + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + + UUID characteristicUUID = characteristic.getUuid(); + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + return false; + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + if (super.onCharacteristicRead(gatt, characteristic, status)) { + return true; + } + UUID characteristicUUID = characteristic.getUuid(); + + LOG.info("Unhandled characteristic read: " + characteristicUUID); + return false; + } + + @Override + public void onSendConfiguration(String config) { + + } + + @Override + public void onReadConfiguration(String config) { + + } + + @Override + public void onTestNewFunction() { + + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java index 774d12321..83b009e0d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miscale2/MiScale2DeviceSupport.java @@ -1,3 +1,20 @@ +/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer, Jean-François + Greffier + + 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.service.devices.miscale2; import android.bluetooth.BluetoothGatt; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 0538c40a1..6639aa334 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -1,5 +1,5 @@ /* Copyright (C) 2017-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Pavel Elagin, protomors + Gobbetti, Pavel Elagin, protomors, Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleActiveAppTracker.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleActiveAppTracker.java new file mode 100644 index 000000000..2f8f2ce7a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleActiveAppTracker.java @@ -0,0 +1,40 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.UUID; + +public class PebbleActiveAppTracker { + private @Nullable UUID mPreviousRunningApp = null; + private @Nullable UUID mCurrentRunningApp = null; + + @Nullable + public UUID getPreviousRunningApp() { + return mPreviousRunningApp; + } + + @Nullable + public UUID getCurrentRunningApp() { + return mCurrentRunningApp; + } + + public void markAppClosed(@NonNull UUID app) { + if (mCurrentRunningApp == app) { + if (mPreviousRunningApp != null) { + markAppOpened(mPreviousRunningApp); + } else { + mCurrentRunningApp = null; + } + } + } + + public void markAppOpened(@NonNull UUID openedApp) { + if (openedApp.equals(mCurrentRunningApp)) { + return; + } + + mPreviousRunningApp = mCurrentRunningApp; + mCurrentRunningApp = openedApp; + } +} 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 0ca80d6a3..7c1ba63c0 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 @@ -27,6 +27,7 @@ import android.os.ParcelUuid; import android.webkit.ValueCallback; import android.webkit.WebView; +import androidx.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,6 +76,7 @@ class PebbleIoThread extends GBDeviceIoThread { private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; private PebbleKitSupport mPebbleKitSupport; + private final PebbleActiveAppTracker mPebbleActiveAppTracker; private final boolean mEnablePebblekit; private boolean mIsTCP = false; @@ -149,6 +151,8 @@ class PebbleIoThread extends GBDeviceIoThread { mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); mPebbleProtocol.setAlwaysACKPebbleKit(prefs.getBoolean("pebble_always_ack_pebblekit", false)); mPebbleProtocol.setEnablePebbleKit(mEnablePebblekit); + + mPebbleActiveAppTracker = new PebbleActiveAppTracker(); } private int readWithException(InputStream inputStream, byte[] buffer, int byteOffset, int byteCount) throws IOException { @@ -563,6 +567,11 @@ class PebbleIoThread extends GBDeviceIoThread { WebViewSingleton.getInstance().runJavascriptInterface(gbDevice, appMgmt.uuid); } } + + mPebbleActiveAppTracker.markAppOpened(appMgmt.uuid); + break; + case STOP: + mPebbleActiveAppTracker.markAppClosed(appMgmt.uuid); break; default: break; @@ -681,6 +690,17 @@ class PebbleIoThread extends GBDeviceIoThread { } } + void reopenLastApp(@NonNull UUID assumedCurrentApp) { + UUID currentApp = mPebbleActiveAppTracker.getCurrentRunningApp(); + UUID previousApp = mPebbleActiveAppTracker.getPreviousRunningApp(); + + if (previousApp == null || !assumedCurrentApp.equals(currentApp)) { + write(mPebbleProtocol.encodeAppStart(assumedCurrentApp, false)); + } else { + write(mPebbleProtocol.encodeAppStart(previousApp, true)); + } + } + private void finishInstall(boolean hadError) { if (!mIsInstalling) { return; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 647a1a2b2..1e2a918b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -44,6 +44,8 @@ class PebbleKitSupport { private static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; private static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; + private static final String PEBBLEKIT_EXTRA_REOPEN_LAST_APP = "com.getpebble.action.app.REOPEN_LAST_APP"; + private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA_NEW = "com.getpebble.action.dl.RECEIVE_DATA_NEW"; //private static final String PEBBLEKIT_ACTION_DL_RECEIVE_DATA = "com.getpebble.action.dl.RECEIVE_DATA"; private static final String PEBBLEKIT_ACTION_DL_ACK_DATA = "com.getpebble.action.dl.ACK_DATA"; @@ -73,7 +75,12 @@ class PebbleKitSupport { case PEBBLEKIT_ACTION_APP_STOP: uuid = (UUID) intent.getSerializableExtra("uuid"); if (uuid != null) { - mPebbleIoThread.write(mPebbleProtocol.encodeAppStart(uuid, action.equals(PEBBLEKIT_ACTION_APP_START))); + if (action.equals(PEBBLEKIT_ACTION_APP_STOP) && + intent.getBooleanExtra(PEBBLEKIT_EXTRA_REOPEN_LAST_APP, false)) { + mPebbleIoThread.reopenLastApp(uuid); + } else { + mPebbleIoThread.write(mPebbleProtocol.encodeAppStart(uuid, action.equals(PEBBLEKIT_ACTION_APP_START))); + } } break; case PEBBLEKIT_ACTION_APP_SEND: 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 5d43878f4..6422fff6a 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 @@ -2156,7 +2156,12 @@ public class PebbleProtocol extends GBDeviceProtocol { break; case APPRUNSTATE_STOP: LOG.info(ENDPOINT_NAME + ": stopped " + uuid); - break; + + GBDeviceEventAppManagement gbDeviceEventAppManagement = new GBDeviceEventAppManagement(); + gbDeviceEventAppManagement.uuid = uuid; + gbDeviceEventAppManagement.type = GBDeviceEventAppManagement.EventType.STOP; + gbDeviceEventAppManagement.event = GBDeviceEventAppManagement.Event.SUCCESS; + return new GBDeviceEvent[]{gbDeviceEventAppManagement}; default: LOG.info(ENDPOINT_NAME + ": (cmd:" + command + ")" + uuid); break; 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 858f510be..707d60063 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 @@ -1,5 +1,5 @@ /* Copyright (C) 2015-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Kasha, Steffen Liebergeld + Gobbetti, Kasha, Sebastian Kranz, Steffen Liebergeld This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/roidmi/RoidmiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/roidmi/RoidmiSupport.java index b9a3b5c00..890c5c9a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/roidmi/RoidmiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/roidmi/RoidmiSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2019 José Rebelo +/* Copyright (C) 2018-2019 José Rebelo, Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java index 6a795c0c4..4cfa59a72 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2016-2019 Andreas Shimokawa, Carsten Pfeiffer, Sebastian + Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/watch9/Watch9DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/watch9/Watch9DeviceSupport.java index 1334db757..a99a07ad8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/watch9/Watch9DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/watch9/Watch9DeviceSupport.java @@ -1,5 +1,5 @@ /* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, maxirnilian + Gobbetti, maxirnilian, Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java index 174b430f9..c3bc3a04c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, ladbsoft +/* Copyright (C) 2018-2019 Andreas Shimokawa, Carsten Pfeiffer, ladbsoft, + Sebastian Kranz This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java index 36ecf8605..ce493fb22 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/zetime/ZeTimeDeviceSupport.java @@ -76,7 +76,7 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo(); private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl(); - private final int sevenHourOffset = 25200; + private final int eightHourOffset = 28800; private byte[] lastMsg; private byte msgPart; private int availableSleepData; @@ -1171,8 +1171,10 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private void handleStepsData(byte[] msg) { ZeTimeActivitySample sample = new ZeTimeActivitySample(); + Calendar now = GregorianCalendar.getInstance(); int timestamp = (msg[10] << 24)&0xff000000 | (msg[9] << 16)&0xff0000 | (msg[8] << 8)&0xff00 | (msg[7]&0xff); - timestamp += sevenHourOffset; // the timestamp from the watch has an offset of seven hours, do not know why... + timestamp += eightHourOffset; // the timestamp from the watch has an offset of eight hours, do not know why... + timestamp -= ((now.get(Calendar.ZONE_OFFSET)/1000) + (now.get(Calendar.DST_OFFSET)/1000)); // TimeZone hour + daylight saving sample.setTimestamp(timestamp); sample.setSteps((msg[14] << 24)&0xff000000 | (msg[13] << 16)&0xff0000 | (msg[12] << 8)&0xff00 | (msg[11]&0xff)); sample.setCaloriesBurnt((msg[18] << 24)&0xff000000 | (msg[17] << 16)&0xff0000 | (msg[16] << 8)&0xff00 | (msg[15]&0xff)); @@ -1217,8 +1219,10 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private void handleSleepData(byte[] msg) { ZeTimeActivitySample sample = new ZeTimeActivitySample(); + Calendar now = GregorianCalendar.getInstance(); int timestamp = (msg[10] << 24)&0xff000000 | (msg[9] << 16)&0xff0000 | (msg[8] << 8)&0xff00 | (msg[7]&0xff); - timestamp += sevenHourOffset; // the timestamp from the watch has an offset of seven hours, do not know why... + timestamp += eightHourOffset; // the timestamp from the watch has an offset of eight hours, do not know why... + timestamp -= ((now.get(Calendar.ZONE_OFFSET)/1000) + (now.get(Calendar.DST_OFFSET)/1000)); // TimeZone hour + daylight saving sample.setTimestamp(timestamp); if(msg[11] == 0) { sample.setRawKind(ActivityKind.TYPE_DEEP_SLEEP); @@ -1260,8 +1264,10 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { private void handleHeartRateData(byte[] msg) { ZeTimeActivitySample sample = new ZeTimeActivitySample(); + Calendar now = GregorianCalendar.getInstance(); int timestamp = (msg[10] << 24)&0xff000000 | (msg[9] << 16)&0xff0000 | (msg[8] << 8)&0xff00 | (msg[7]&0xff); - timestamp += sevenHourOffset; // the timestamp from the watch has an offset of seven hours, do not know why... + timestamp += eightHourOffset; // the timestamp from the watch has an offset of eight hours, do not know why... + timestamp -= ((now.get(Calendar.ZONE_OFFSET)/1000) + (now.get(Calendar.DST_OFFSET)/1000)); // TimeZone hour + daylight saving sample.setHeartRate(msg[11]); sample.setTimestamp(timestamp); @@ -1278,10 +1284,11 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport { progressHeartRate = (msg[5]&0xff) | ((msg[6] << 8)&0xff00); GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, (int) (progressHeartRate *100 / availableHeartRateData), getContext()); - if(((msg[4] << 8)&0xff00 | (msg[3]&0xff)) == 0xe) // if the message is longer than 0x7, than it has to measurements (payload = 0xe) + if(((msg[4] << 8)&0xff00 | (msg[3]&0xff)) == 0xe) // if the message is longer than 0x7, than it has two measurements (payload = 0xe) { timestamp = (msg[17] << 24)&0xff000000 | (msg[16] << 16)&0xff0000 | (msg[15] << 8)&0xff00 | (msg[14]&0xff); - timestamp += sevenHourOffset; // the timestamp from the watch has an offset of seven hours, do not know why... + timestamp += eightHourOffset; // the timestamp from the watch has an offset of eight hours, do not know why... + timestamp -= ((now.get(Calendar.ZONE_OFFSET)/1000) + (now.get(Calendar.DST_OFFSET)/1000)); // TimeZone hour + daylight saving sample.setHeartRate(msg[18]); sample.setTimestamp(timestamp); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index b5cbf9665..034f51f10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -224,7 +224,7 @@ public class AndroidUtils { Uri contentUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".screenshot_provider", file); intent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setData(contentUri); + intent.setDataAndType(contentUri,"application/gpx+xml"); context.startActivity(intent); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java index 671d79af0..f33ef49a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.zip.CRC32; public class CheckSums { public static int getCRC8(byte[] seq) { @@ -46,9 +47,9 @@ public class CheckSums { public static int getCRC16(byte[] seq) { int crc = 0xFFFF; - for (int j = 0; j < seq.length; j++) { + for (byte b : seq) { crc = ((crc >>> 8) | (crc << 8)) & 0xffff; - crc ^= (seq[j] & 0xff);//byte to int, trunc sign + crc ^= (b & 0xff);//byte to int, trunc sign crc ^= ((crc & 0xff) >> 4); crc ^= (crc << 12) & 0xffff; crc ^= ((crc & 0xFF) << 5) & 0xffff; @@ -57,6 +58,12 @@ public class CheckSums { return crc; } + public static int getCRC32(byte[] seq) { + CRC32 crc = new CRC32(); + crc.update(seq); + return (int) (crc.getValue()); + } + public static void main(String[] args) throws IOException { if (args == null || args.length == 0) { throw new IllegalArgumentException("Pass the files to be checksummed as arguments"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 3b146638c..6d724a120 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -1,7 +1,7 @@ -/* Copyright (C) 2015-2019 0nse, Andreas Böhler, Andreas Shimokawa, - Carsten Pfeiffer, Daniele Gobbetti, João Paulo Barraca, José Rebelo, - Kranz, ladbsoft, maxirnilian, protomors, Quallenauge, Sami Alaoui, tiparega, - Vadim Kaushan +/* Copyright (C) 2015-2019 0nse, Andreas Böhler, Andreas Shimokawa, Carsten + Pfeiffer, Daniele Gobbetti, Jean-François Greffier, João Paulo Barraca, + José Rebelo, Kranz, ladbsoft, maxirnilian, protomors, Quallenauge, Sami + Alaoui, Sophanimus, tiparega, Vadim Kaushan This file is part of Gadgetbridge. @@ -48,15 +48,19 @@ import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.Q8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor2.AmazfitCor2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2HRXCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband4.MiBand4Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.miscale2.MiScale2DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd02.MijiaLywsd02Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.miscale2.MiScale2DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator; @@ -201,7 +205,9 @@ public class DeviceHelper { result.add(new MiScale2DeviceCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new AmazfitBipCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new AmazfitCorCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new AmazfitCor2Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand3Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new MiBand4Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand2HRXCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); @@ -222,6 +228,8 @@ public class DeviceHelper { result.add(new Roidmi1Coordinator()); result.add(new Roidmi3Coordinator()); result.add(new CasioGB6900DeviceCoordinator()); + result.add(new BFH16DeviceCoordinator()); + result.add(new MijiaLywsd02Coordinator()); return result; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index aace268af..2f6acb03a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -194,6 +194,16 @@ public class GB { return new String(hexChars); } + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + public static String formatRssi(short rssi) { return String.valueOf(rssi); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java index 748131542..8b4490ef9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java @@ -86,14 +86,7 @@ public class ImportExportSharedPreferences { return importFromReader(sharedPreferences, new FileReader(inFile)); } - /** - * - * @param sharedPreferences - * @param in - * @return - * @throws Exception - */ - public static boolean importFromReader(SharedPreferences sharedPreferences, Reader in) + private static boolean importFromReader(SharedPreferences sharedPreferences, Reader in) throws Exception { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.clear(); @@ -124,27 +117,31 @@ public class ImportExportSharedPreferences { } else if (STRING.equals(name)) { editor.putString(key, text); } else if (HASHSET.equals(name)) { - if (key.equals(GBPrefs.PACKAGE_BLACKLIST)) { - Set apps_blacklist = new HashSet<>(); - text=text.replace("[","").replace("]",""); - for (int z=0;z apps_pebble_blacklist = new HashSet<>(); - text=text.replace("[","").replace("]",""); - for (int z=0;z calendars_blacklist = new HashSet<>(); - text = text.replace("[", "").replace("]", ""); - for (int z = 0; z < text.split(",").length; z++) { - calendars_blacklist.add(text.split(",")[z].trim()); - } - GBApplication.setCalendarsBlackList(calendars_blacklist); + switch (key) { + case GBPrefs.PACKAGE_BLACKLIST: + Set apps_blacklist = new HashSet<>(); + text = text.replace("[", "").replace("]", ""); + for (int z = 0; z < text.split(",").length; z++) { + apps_blacklist.add(text.split(",")[z].trim()); + } + GBApplication.setAppsNotifBlackList(apps_blacklist); + break; + case GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST: //TODO: untested + Set apps_pebble_blacklist = new HashSet<>(); + text = text.replace("[", "").replace("]", ""); + for (int z = 0; z < text.split(",").length; z++) { + apps_pebble_blacklist.add(text.split(",")[z].trim()); + } + GBApplication.setAppsPebbleBlackList(apps_pebble_blacklist); + break; + case GBPrefs.CALENDAR_BLACKLIST: //TODO: untested + Set calendars_blacklist = new HashSet<>(); + text = text.replace("[", "").replace("]", ""); + for (int z = 0; z < text.split(",").length; z++) { + calendars_blacklist.add(text.split(",")[z].trim()); + } + GBApplication.setCalendarsBlackList(calendars_blacklist); + break; } } else if (!PREFERENCES.equals(name)) { throw new Exception("Unknown type " + name); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreference.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreference.java new file mode 100644 index 000000000..29b56a700 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreference.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2017-2019 Carsten Pfeiffer, 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.util; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.format.DateFormat; +import android.util.AttributeSet; + +import androidx.preference.DialogPreference; + +public class XTimePreference extends DialogPreference { + protected int hour = 0; + protected int minute = 0; + + public XTimePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getString(index); + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + String time; + + if (restoreValue) { + if (defaultValue == null) { + time = getPersistedString("00:00"); + } else { + time = getPersistedString(defaultValue.toString()); + } + } else { + if (defaultValue != null) { + time = defaultValue.toString(); + } else { + time = "00:00"; + } + } + + String[] pieces = time.split(":"); + + hour = Integer.parseInt(pieces[0]); + minute = Integer.parseInt(pieces[1]); + + updateSummary(); + } + + void updateSummary() { + if (DateFormat.is24HourFormat(getContext())) + setSummary(getTime24h()); + else + setSummary(getTime12h()); + } + + String getTime24h() { + return String.format("%02d", hour) + ":" + String.format("%02d", minute); + } + + private String getTime12h() { + String suffix = hour < 12 ? " AM" : " PM"; + int h = hour > 12 ? hour - 12 : hour; + + return h + ":" + String.format("%02d", minute) + suffix; + } + + void persistStringValue(String value) { + persistString(value); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java new file mode 100644 index 000000000..1cfb0d35e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/XTimePreferenceFragment.java @@ -0,0 +1,71 @@ +/* Copyright (C) 2017-2019 Carsten Pfeiffer, 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.util; + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.TimePicker; + +import androidx.preference.DialogPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceDialogFragmentCompat; + +public class XTimePreferenceFragment extends PreferenceDialogFragmentCompat implements DialogPreference.TargetFragment { + private TimePicker picker = null; + + @Override + protected View onCreateDialogView(Context context) { + picker = new TimePicker(context); + picker.setIs24HourView(DateFormat.is24HourFormat(getContext())); + picker.setPadding(0, 50, 0, 50); + + return picker; + } + + @Override + protected void onBindDialogView(View v) { + super.onBindDialogView(v); + XTimePreference pref = (XTimePreference) getPreference(); + + picker.setCurrentHour(pref.hour); + picker.setCurrentMinute(pref.minute); + } + + @Override + public void onDialogClosed(boolean positiveResult) { + + if (positiveResult) { + XTimePreference pref = (XTimePreference) getPreference(); + + pref.hour = picker.getCurrentHour(); + pref.minute = picker.getCurrentMinute(); + + String time = pref.getTime24h(); + if (pref.callChangeListener(time)) { + pref.persistStringValue(time); + pref.updateSummary(); + } + } + } + + + @Override + public Preference findPreference(CharSequence key) { + return getPreference(); + } +} diff --git a/app/src/main/res/drawable/ic_access_time.xml b/app/src/main/res/drawable/ic_access_time.xml new file mode 100644 index 000000000..e2de53bbd --- /dev/null +++ b/app/src/main/res/drawable/ic_access_time.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_activity_biking.xml b/app/src/main/res/drawable/ic_activity_biking.xml index 7001d100b..a2ffce6a5 100644 --- a/app/src/main/res/drawable/ic_activity_biking.xml +++ b/app/src/main/res/drawable/ic_activity_biking.xml @@ -1,7 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_deep_sleep.xml b/app/src/main/res/drawable/ic_activity_deep_sleep.xml deleted file mode 100644 index abdaea833..000000000 --- a/app/src/main/res/drawable/ic_activity_deep_sleep.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_exercise.xml b/app/src/main/res/drawable/ic_activity_exercise.xml new file mode 100644 index 000000000..9b802f2da --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_exercise.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_activity_light_sleep.xml b/app/src/main/res/drawable/ic_activity_light_sleep.xml deleted file mode 100644 index abdaea833..000000000 --- a/app/src/main/res/drawable/ic_activity_light_sleep.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_not_measured.xml b/app/src/main/res/drawable/ic_activity_not_measured.xml index 7a6bb2d01..d42708d7e 100644 --- a/app/src/main/res/drawable/ic_activity_not_measured.xml +++ b/app/src/main/res/drawable/ic_activity_not_measured.xml @@ -1,7 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_running.xml b/app/src/main/res/drawable/ic_activity_running.xml index 14c06266a..78ce0c027 100644 --- a/app/src/main/res/drawable/ic_activity_running.xml +++ b/app/src/main/res/drawable/ic_activity_running.xml @@ -1,7 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_sleep.xml b/app/src/main/res/drawable/ic_activity_sleep.xml new file mode 100644 index 000000000..6b13d8083 --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_sleep.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_swimming.xml b/app/src/main/res/drawable/ic_activity_swimming.xml new file mode 100644 index 000000000..391cb99bd --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_swimming.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_activity_unknown.xml b/app/src/main/res/drawable/ic_activity_unknown.xml index 4e323aeaa..6bde4c5f2 100644 --- a/app/src/main/res/drawable/ic_activity_unknown.xml +++ b/app/src/main/res/drawable/ic_activity_unknown.xml @@ -1,7 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_walking.xml b/app/src/main/res/drawable/ic_activity_walking.xml index 831c0b345..97036cc9d 100644 --- a/app/src/main/res/drawable/ic_activity_walking.xml +++ b/app/src/main/res/drawable/ic_activity_walking.xml @@ -1,7 +1,10 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_upward.xml b/app/src/main/res/drawable/ic_arrow_upward.xml new file mode 100644 index 000000000..631fb54b2 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_upward.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_brightness_2.xml b/app/src/main/res/drawable/ic_brightness_2.xml new file mode 100644 index 000000000..4b73de603 --- /dev/null +++ b/app/src/main/res/drawable/ic_brightness_2.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_do_not_disturb.xml b/app/src/main/res/drawable/ic_do_not_disturb.xml new file mode 100644 index 000000000..f00846d15 --- /dev/null +++ b/app/src/main/res/drawable/ic_do_not_disturb.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_font_download.xml b/app/src/main/res/drawable/ic_font_download.xml new file mode 100644 index 000000000..03a9a260a --- /dev/null +++ b/app/src/main/res/drawable/ic_font_download.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml new file mode 100644 index 000000000..e5a15e1e8 --- /dev/null +++ b/app/src/main/res/drawable/ic_language.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_lock_open.xml b/app/src/main/res/drawable/ic_lock_open.xml new file mode 100644 index 000000000..dfe29b6e6 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_open.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml index 8e1af5386..72d0a99ed 100644 --- a/app/src/main/res/drawable/ic_menu.xml +++ b/app/src/main/res/drawable/ic_menu.xml @@ -1,5 +1,10 @@ - - + + diff --git a/app/src/main/res/drawable/ic_rotate_left.xml b/app/src/main/res/drawable/ic_rotate_left.xml new file mode 100644 index 000000000..dccb97fb2 --- /dev/null +++ b/app/src/main/res/drawable/ic_rotate_left.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_signal_cellular_connected_no_internet_0_bar.xml b/app/src/main/res/drawable/ic_signal_cellular_connected_no_internet_0_bar.xml new file mode 100644 index 000000000..a6650dc31 --- /dev/null +++ b/app/src/main/res/drawable/ic_signal_cellular_connected_no_internet_0_bar.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_vpn_key.xml b/app/src/main/res/drawable/ic_vpn_key.xml new file mode 100644 index 000000000..88053a611 --- /dev/null +++ b/app/src/main/res/drawable/ic_vpn_key.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_widgets.xml b/app/src/main/res/drawable/ic_widgets.xml new file mode 100644 index 000000000..638ce0000 --- /dev/null +++ b/app/src/main/res/drawable/ic_widgets.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_device_settings.xml b/app/src/main/res/layout/activity_device_settings.xml new file mode 100644 index 000000000..c2e4426cd --- /dev/null +++ b/app/src/main/res/layout/activity_device_settings.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/device_itemv2.xml b/app/src/main/res/layout/device_itemv2.xml index ba43f08a5..6cb1c6ba4 100644 --- a/app/src/main/res/layout/device_itemv2.xml +++ b/app/src/main/res/layout/device_itemv2.xml @@ -139,12 +139,31 @@ + + + @@ -178,7 +197,6 @@ android:layout_toEndOf="@id/device_fm_frequency_box" android:background="?android:attr/selectableItemBackground" android:clickable="true" - android:contentDescription="@string/controlcenter_take_screenshot" android:padding="4dp" android:scaleType="fitXY" card_view:srcCompat="@drawable/ic_led_color" diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 78a62e54f..402d9e797 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -80,7 +80,7 @@ Общи настройки Свързване с Gadgetbridge устройството при включване на Bluetoooth Автоматично стартиране - Автоматичен reconnect + Автоматично възстановяване на връзката Предпочитан музикален player По подразбиране Включване на плъзгане в ляво/дясно в графиката за активности @@ -132,7 +132,7 @@ Настройки за разработчици Mi Band адрес Pebble настройки - Активити trackers + Активити тракери Предпочитан активити tracker Синхронизиране с Pebble Health Синхронизиране с Misfit @@ -210,9 +210,9 @@ Инсталиране на двоичен файл %1$d/%2$d Инсталацията пропадна Инсталирано - ОПИТВАТЕ СЕ ДА ИНСТАЛИРАТЕ FIRMWARE, ПРОДЪЛЖЕТЕ НА СОБСТВЕН РИСК. -\n -\n + ОПИТВАТЕ СЕ ДА ИНСТАЛИРАТЕ FIRMWARE, ПРОДЪЛЖЕТЕ НА СВОЯ ОТГОВОРНОСТ. +\n +\n \n Този firmware е за HW Revision: %s Ще се инсталира следното приложение: \n @@ -235,7 +235,7 @@ Не може да се сдвои с/със %1$s (%2$s) Установява се връзка: %1$s (%2$s) Установена е връзка с/със %1$s (%2$s), свързване… - Липсва MAC адрес, не може да се свърже + Липсва MAC адрес, не може да се установи връзка Специфични настройки за устройствата Mi Band / Amazfit настройки Mi Band 2 настройки @@ -257,7 +257,7 @@ Изображение на устройството Име/Псевдоним Брой на вибрациите - Когато часовникът вибрира, разклатете устройството или натиснете бутона + Когато часовникът завибрира, разклатете устройството или натиснете бутона Наблюдение/анализ на съня Съхраняване на log файлове Инициализиране @@ -279,7 +279,7 @@ Обща поддръжка на известия Имейл известяване Известяване за входящо обаждане - Лаф + Чат Навигация Социални мрежи Измерване на сърдечния ритъм през целия ден @@ -342,7 +342,7 @@ Общо минути Крачки в минута Търсене на загубено устройство - Спиране на вибрацията + Отменете за спиране на вибрацията Вашата активност Настройка на алармите Настройка на алармите @@ -374,4 +374,46 @@ Брой зареждания: %s %1$s батерията е разредена %1$s батерията е разредена: %2$s + Активиране на обаждане от VoIP приложения + Експортът на базата данни пропадна! Моля, проверете си настройките. + Вашият сън + Крачки за седмица + Вашите активност и сън + Недоспиване: %1$s + Обновяване на firmware… + Файлът не може да бъде инсталиран, устройството не е готово. + %1$s: %2$s %3$s + Съвместима версия + Нетествана версия! + Свързване с устройство: %1$s + Pebble Firmware %1$s + Коректна хардуерна ревизия + Несъответствие на хардуерната ревизия! + %1$s (%2$s) + Проблем при зареждането на firmware. НЕ РЕСТАРТИРАЙТЕ вашия Mi Band! + Проблем при зареждането на метаданните на фърмуера + Инсталирането на firmware завърши + Инсталирането на firmware завърши, устройството се рестартира… + Firmware обноваването е неуспешно + Крачки + Калории + Разстояние + Часовник + Сърдечен ритъм + Батерия + Без ограничение + 5 секунди + 10 секунди + 20 секунди + 30 секунди + 1 минута + 5 минути + 10 минути + 30 минути + Активност в реално време + Крачки днес, цел: %1$s + Недостиг на крачки: %1$d + Крачки в повече: %1$d + Ако данните за активност не са потвърени от устройството, те няма да се изчистят. Полезно е, когато GB се използва заедно с други приложения. + Данните за активност ще се пазят на Mi Band дори след синхронизация. Полезно е, когато GB се използва заедно с други приложения. \ No newline at end of file diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 4587ed7e9..398f607ef 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -1,99 +1,86 @@ - -Gadgetbridge - + + + Gadgetbridge Gadgetbridge - Paràmetres + Configuració Depuració - Sortir + Surt Donació Sincronitza Seguiment del son (ALPHA) - Cercar dispositiu perdut - Fer captura de pantalla - Connectar + Cerca un aparell perdut + Fes una captura de pantalla + Connecta… Desconnecta - Esborra dispositiu - Esborrar %1$s - Aquesta acció esborrarà el dispositiu i tota la seva informació associada! - Obrir el calaix de navegació - Tancar el calaix de navegació - Mantenir premut la icona per desconnecta - Desconnectant - Connectant - Fer captura de pantalla del dispositiu - - + Esborra l\'aparell + Esborra %1$s + Aquesta acció esborrarà l\'aparell i tota la seva informació associada! + Obre el calaix de navegació + Tanca el calaix de navegació + Mantingueu premut l\'element per desconnectar + S\'està desconnectant + S\'està connectant… + S\'està fent una captura de pantalla de l\'aparell Depuració - - Gestor de l\'aplicació - Aplicació en memòria cau + Gestor d\'aplicacions + Aplicacions a la memòria cau Aplicacions instal·lades - Esferes instal·lades - Esborrar - Esborra i treu de la memòria cau - Reinstal·lar - Cerca a Pebble Appstore - Activar - Desactivar - Activar seguiment d\'activitat cardíaca - Desactivar seguiment d\'activitat cardíaca - Activar l\'aplicació del temps del sistema - Desactivar l\'aplicació del temps del sistema - Instal·lar l\'aplicació del temps del sistema - Configurar - Moure a la part superior - - Llista negra de notificacions - - Calendaris en llista negra - - Instal·lador de FW/App - Estàs a punt d\'instal·lar el firmware %s en lloc del que està a la teva MiBand. - Esteu a punt d\'instal·lar el firmware %s en el vostre Amazfit Bip. + Caràtules instal·lades + Esborra + Esborra i suprimeix de la memòria cau + Reinstal·la + Cerca a la botiga d\'aplicacions de Pebble + Activa + Desactiva + Activa el seguiment de ritme cardíac + Desactiva el seguiment de ritme cardíac + Activa l\'aplicació del temps del sistema + Desactiva l\'aplicació del temps del sistema + Instal·la l\'aplicació del temps del sistema + Configura + Mou a la part superior + Bloqueig de notificacions + Calendaris bloquejats + Instal·lador de microprogramari i aplicacions + Sou a punt d\'instal·lar el %s. + Esteu a punt d\'instal·lar el microprogramari %s en el vostre Amazfit Bip. \n \nSi us plau, assegure-vos d\'instal·lar el fitxer .fw, el fitxer .res i, finalment, el fitxer .gps. El vostre rellotge reiniciarà després d\'instal·lar l\'arxiu .fw. \n -\nNota: no heu d\'instal·lar .res i .gps si aquests fitxers són els mateixos que els prèviament instal·lats. +\nNota: no heu d\'instal·lar els fitxers .res i .gps si aquests fitxers són exactament els mateixos que els prèviament instal·lats. \n \nPROCEDIU SOTA LA VOSTRA PRÒPIA RESPONSABILITAT! - Esteu a punt d\'instal·lar el firmware %s en el vostre Amazfit Cor. -\n -\nSi us plau, assegure-vos d\'instal·lar el fitxer .fw i el fitxer .res. El vostre rellotge reiniciarà després d\'instal·lar l\'arxiu .fw. -\n -\nNota: no heu d\'instal·lar .res si aquest fitxer és el mateix que el prèviament instal·lat. -\n -\nNO TESTAT. POT ESTAVELLAR EL VOSTRE DISPOSITIU. PROCEDIU SOTA LA VOSTRA PRÒPIA RESPONSABILITAT! - Estàs a punt d\'instal·lar els firmwares %1$s i %2$s en lloc dels de la vostra MiBand. - Aquest firmware ha estat provat i se sap que és compatible amb Gadgetbridge. - Aquest firmware no ha estat provat i pot ser que no sigui compatible amb Gadgetbridge. -\n -\nNO es recomana la instal·lació en el teu MiBand! - Si tot i així vols seguir i les coses continuen funcionant correctament, si us plau indica\'ls als desenvolupadors de Gadgetbridge que aquesta versió del firmware funciona bé: %s - - Paràmetres - - Paràmetres generals - Connectar-se al dispositiu quan el Bluetooth estigui activat + Sou a punt d\'instal·lar el microprogramari %s en el vostre Amazfit Cor. +\n +\nSi us plau, assegure-vos d\'instal·lar el fitxer .fw i el fitxer .res. La vostra polsera reiniciarà després d\'instal·lar l\'arxiu .fw. +\n +\nNota: no heu d\'instal·lar el fitxer .res si és exactament el mateix que el prèviament instal·lat. +\n +\nPROCEDIU SOTA LA VOSTRA PRÒPIA RESPONSABILITAT! + "Sou a punt d\'instal·lar els microprogramaris %1$s i %2$s en lloc dels que hi ha actualment a la vostra Mi Band." + Aquest microprogramari ha estat provat i se sap que és compatible amb el Gadgetbridge. + Aquest microprogramari no ha estat provat i pot ser que no sigui compatible amb el Gadgetbridge. +\n +\nNO es recomana instal·lar-lo! + Si tot i així voleu continuar i les coses continuen funcionant correctament, si us plau informeu els desenvolupadors del Gadgetbridge que la versió %s del microprogramari funciona bé. + Configuració + Configuració general + Connecta\'t a l\'aparell Gadgetbridge quan el Bluetooth estigui activat Inicia automàticament Reconnecta automàticament - Reproductor preferit + Reproductor d\'àudio preferit Per defecte - Permetre lliscar cap a l\'esquerra/dreta en els gràfics d\'activitat - + Permet lliscar cap a l\'esquerra i la dreta en els gràfics d\'activitat Data i Hora Sincronitza l\'hora - Sincronitza l\'hora en el dispositiu quan es connecti o es canviï l\'hora o zona horària en Android - + Sincronitza l\'hora en l\'aparell Gadgetbridge quan es connecti o quan es canviï l\'hora o zona horària en l\'aparell Android Aparença Clar Fosc - - Idioma - - Oculta la notificació de Gadgetbridge - La icona a la barra d\'estat i la notificació a la pantalla de bloqueig estan actius - La icona a la barra d\'estat i la notificació a la pantalla de bloqueig estan ocults - + Llengua + Oculta la notificació del Gadgetbridge + La icona a la barra d\'estat i la notificació a la pantalla de bloqueig es mostren + La icona a la barra d\'estat i la notificació a la pantalla de bloqueig no es mostren Notificacions Repeticions Trucades telefòniques @@ -102,124 +89,103 @@ Suport per a aplicacions que envien notificacions a Pebble a través de PebbleKit. Suport per a notificacions genèriques ... també amb la pantalla encesa - No molestar - Deixar d\'enviar notificacions no desitjades basant-se en el mode No Molestar - Transcripció - Utilitzar en cas que el teu dispositiu no suporti la font del teu idioma - + No molesteu + En aquest mode s\'aturen les notifications no desitjades + Transliteració + Activeu aquesta opció si el vostre aparell no pot mostrar els caràcters de la vostra llengua Sempre Quan la pantalla està apagada Mai - Privacitat Mode de privacitat de trucada - Mostrar nom i número - Oculta nom però mostrar número - Ocultar número però mostrar nom - Ocultar nom i número - - - Llista negra d\'aplicacions - Llista negra de calendaris - - Missatges predeterminants + Mostra el nom i el número + Amaga el nom però mostra el número + Amaga el número però mostra el nom + Amaga el nom i el número + Bloqueig d\'aplicacions + Bloqueig de calendaris + Missatges predefinits Respostes - Sufixe habitual - Rebutjar trucada - Actualitza a Pebble - - Opcions de desenvolupadors - Direcció de MiBand - - Paràmetres de Pebble - - Seguiment d\'activitat - Seguiment d\'activitat preferit - Sincronitzar amb Pebble Health - Sincronitzar amb Misfit - Sincronitzar amb Morpheuz - - Suport per trucades sortints + Sufix automàtic + Rebuig de trucada + Actualitza al Pebble + Opcions per a desenvolupadors + Adreça de Mi Band + Configuració del Pebble + Sistemes de seguiment d\'activitat + Sistema preferit per al seguiment d\'activitat + Sincronitza amb Pebble Health + Sincronitza amb Misfit + Sincronitza amb Morpheuz + Suport per a trucades sortints Desactivar això evitarà que Pebble 2/LE vibri en trucades sortints - - Permetre l\'accés a aplicacions Android de tercers - Permetre el suport experimental per aplicacions Android que fan servir PebbleKit - - Timeline Pebble + Permet l\'accés a aplicacions Android de tercers + Permet aplicacions d\'Android experimentals que fan servir PebbleKit + Cronologia del Pebble Sortida i posta de sol - Envia les hores de sortida i posta de sol basant-se en la localització a la timeline del Pebble - Sincronitzar calendari - Envia els esdeveniments del calendari a la línia de temps - - Esborra automàticament les notificacions rebutjades - Les notificacions s\'esborren automàticament de Pebble quan es rebutgen a Android - - Mode privat - Notificació normal + Envia les hores de sortida i posta de sol basades en la ubicació a la cronologia del Pebble + Sincronitza el calendari + Envia els esdeveniments del calendari a la cronologia + Suprimeix automàticament les notificacions rebutjades + Les notificacions se suprimeixen automàticament del Pebble quan es rebutgen a l\'aparell Android + Mode de privacitat + Notificacions normals Desplaça la notificació fora de la pantalla - Mostra només la icona de notificació - - Localització - Obtenir localització + Mostra només la icona de la notificació + Ubicació + Obté la localització Latitud Longitud - Mantenir la localització actualitzada - Intentar aconseguir la localització en marxa, fer servir la desada com a alternatiu - - Si us plau, activa la localització per xarxa - Localització trobada - - Forçar el protocol de notificació - Aquesta opció força l\'ús del darrer protocol de notificació en funció de la versió del firmware. ACTIVA-HO NOMÉS SI SAPS EL QUE ESTÀS FENT! - Activar característiques no provades - Activar les característiques que no han estat provades. ACTIVA-HO NOMÉS SI SAPS EL QUE ESTÀS FENT! - Preferir sempre BLE - Utilitza el suport experimental de Pebble LE a tots els Pebble en lloc del Bluetooth clàssic, la qual cosa requereix vincular \"Pebble LE\" si un Pebble no-LI ha estat vinculat abans - Pebble 2/LI límit de GATT MTU - Si el teu Pebble 2/Pebble LE no funciona correctament, prova aquesta opció per limitar l\'MTU (rang vàlid 20-512) + Mantingues la ubicació actualitzada + Intenta aconseguir la ubicació constantment, i si això no és possible fes servir la ubicació desada prèviament + Si us plau, activeu la ubicació per xarxa + S\'ha trobat la ubicació + Força el protocol de notificació + Aquesta opció força l\'ús del darrer protocol de notificació en funció de la versió del microprogramari. VIGILEU AMB EL QUE FEU! + Activa característiques no provades + Activa les característiques que no han estat provades. VIGILEU AMB EL QUE FEU! + Prefereix sempre BLE + Utilitza el suport experimental de Pebble LE a tots els Pebble, enlloc del Bluetooth clàssic. Primer cal aparellar primer un Pebble sense LE i després un Pebble LE + Límit de l\'MTU del GATT del Pebble 2/LE + Si el teu Pebble 2/Pebble LE no funciona correctament, prova aquesta opció per limitar l\'MTU (rang vàlid: 20-512) Activa els registres de les aplicacions del rellotge - Produirà registres de les apps del rellotge que Gadgetbridge guardarà (necessita reconnexió) - ACK abans d\'hora de PebbleKit - Permetrà als missatges enviats a apps de tercers ser reconeguts sempre i immediatament + Produirà registres de les apps del rellotge que el Gadgetbridge guardarà (necessita reconnexió) + Envia l\'ACK al PebbleKit abans d\'hora + Permetrà que missatges enviats a aplicacions de tercers siguin reconeguts sempre i immediatament Activa JS en segon pla - Permet a les pantalles mostrar el temps, informació de bateria, etc. - - Intents de reconnexions - - Preferències HPlus + Permet a les caràtules mostrar el temps, informació de bateria, etc. + Intents de reconnexió + Unitats Format d\'hora - Temps de pantalla encesa + Durada de pantalla encesa Mesura el ritme cardíac tot el dia - Paràmetres HPlus/Makibes - + Configuració de HPlus/Makibes No connectat - Connectant + S\'està connectant Connectat Estat desconegut (desconegut) Prova - Prova notificació - Això és una notificació de prova des de Gadgetbridge - El Bluetooth no està suportat. - El Bluetooth està inhabilitat. - Fes un toc al dispositiu connectat per a configurar les aplicacions - Fes un toc al dispositiu connectat per activitat - Fes un toc al dispositiu per vibració - Fes un toc a un dispositius per connectar - No és pot connectar. Direcció BT incorrecta? - Gadgetbridge funcionant - Instal·lat binari %1$d/%2$d + Notificació de prova + Això és una notificació de prova des del Gadgetbridge + El Bluetooth no és compatible o no està implementat. + El Bluetooth està desactivat. + Toca l\'aparell connectat per obrir el gestor d\'aplicacions + Toca l\'aparell connectat per gestionar-ne l\'activitat + Toca l\'aparell per fer-lo vibrar + Toca un aparell per connectar-hi + No es pot connectar. Potser la direcció Bluetooth és incorrecta\? + El Gadgetbridge està funcionant + S\'està instal·lant el binari %1$d/%2$d La instal·lació ha fallat - Instal·lació correcta + Instal·lat El temps Ubicació del temps (CM/LOS) - Exportació automàtica Exportació automàtica activada Ubicació de l\'exportació Interval de l\'exportació Exporta cada %d hores - Esteu a punt d\'instal·lar la següent aplicació: \n \n @@ -227,22 +193,21 @@ NO DISPONIBLE inicialitzat %1$s per %2$s - Detecció de dispositius - + Detecció d\'aparells Atura l\'escaneig Inicia la detecció - Connecteu un dispositiu nou + Connecta un aparell nou %1$s (%2$s) - Emparelleu el dispositiu - Utilitza el diàleg d\'emparellament Android Bluetooth per emparellar el dispositiu. - Emparelleu Mi Band - Emparellant amb %s… - Creant vincle amb %1$s (%2$s) - No s\'ha pogut emparellar amb %1$s (%2$s) - Emparellament en curs: %1$s (%2$s) - Ja emparellat amb %1$s (%2$s), connectant… - No s\'ha obtingut cap adreça MAC. No es pot emparellar. - Configuració específica del dispositiu + Aparella l\'aparell + Utilitzeu el diàleg d\'aparellament Bluetooth de l\'Android per aparellar l\'aparell. + Aparelleu la vostra Mi Band + S\'està aparellant amb %s… + S\'està creant un vincle amb %1$s (%2$s) + No s\'ha pogut aparellar amb %1$s (%2$s) + Aparellament en curs: %1$s (%2$s) + Ja s\'ha aparellat amb %1$s (%2$s). Connectant… + No s\'ha proporcionat cap adreça MAC. No es pot emparellar. + Configuració d\'aparells específics Configuració de Mi Band / Amazfit Configuració d\'Amazfit Bip Home @@ -252,28 +217,25 @@ Dreta Instal·la Nota: - Imatge de dispositiu + Imatge de l\'aparell Nom/Àlies Recompte de vibracions - Monitor de son Escriu fitxers de registre S\'està inicialitzant - S\'està obtenint les dades d\'activitat - Des de %1$s a %2$s - Rellotge a l\'esquerra o la dreta? + S\'estàn recollint les dades d\'activitat + De %1$s a %2$s + A quina mà porteu l\'aparell\? Perfil de vibració - - Molt curt - Curt - Mitjà - Llarg - Molt llarg + Molt curta + Curta + Mitjana + Llarga + Gota d\'aigua Timbre - Alarma + Despertador Vibració - - Proveu-ho + Prova-ho Notificació de SMS Configuració de vibració Notificació genèrica @@ -282,23 +244,20 @@ Xat Navegació Xarxa social - - Mesura del ritme cardíac tot el dia - cada minut - Cada 5 minuts - Cada 10 minuts - Cada 30 minuts - Cada hora - + Mesura el ritme cardíac tot el dia + una vegada per minut + cada 5 minuts + cada 10 minuts + cada 30 minuts + cada hora Zones de velocitat Minuts totals - Passos per minut - - Troba el dispositiu perdut + Passes per minut + Troba l\'aparell perdut Cancel·leu per aturar la vibració. La vostra activitat - Configureu les alarmes - Configureu les alarmes + Configura les alarmes + Configura les alarmes Detalls d\'alarma Dg. Dl. @@ -307,132 +266,448 @@ Dj. Dv. Ds. - Alarma intel·ligent - Hi ha hagut un error en posar les alarmes. Si us plau, intenteu-ho de nou! - Les alarmes han sigut enviades al dispositiu! - No hi ha dades. Sincronitzo el dispositiu? + Reactivació intel·ligent + Hi ha hagut un error en definir les alarmes. Si us plau, intenteu-ho de nou. + S\'han enviat les alarmes a l\'aparell. + No hi ha dades. Sincronitzo l\'aparell\? A punt de transferir %1$s de dades des de %2$s - Objectiu de passos diaris + Objectiu de passes diàries Error en executar \'%1$s\' La vostra activitat (ALPHA) No es pot connectar: %1$s - No s\'ha trobar un controlador per instal·lar el fitxer. + No s\'ha trobat cap controlador per instal·lar aquest fitxer. No s\'ha pogut instal·lar el fitxer: %1$s - No s\'ha pogut instal·lar el microprogramari: no coincideix amb revisió del maquinari de Peeble. - Si us plau, espereu mentre que es determina l\'estat de la instal·lació… - Bateria del gadget baixa! - A %1$s li roman: %2$s%% de bateria + No s\'ha pogut instal·lar el microprogramari: no coincideix amb la revisió de maquinari del vostre Pebble. + Si us plau, espereu mentre s\'esbrina l\'estat de la instal·lació… + L\'aparell té poca bateria! + A %1$s li roman un %2$s%% de bateria Darrera càrrega: %s Nombre de càrregues: %s L\'exportació de la base de dades ha fallat! Si us plau, comproveu la configuració. El vostre son Son per setmana Son avui, objectiu: %1$s - Passos per setmana - La vostra activitat i son - El microprogramari s\'està actualitzant… - No es pot instal·lar el fitxer. El dispositiu no està preparat. + Passes per setmana + La vostra activitat i el vostre son + El microprogramari s\'està instal·lant… + No es pot instal·lar el fitxer. L\'aparell no està preparat. %1$s: %2$s %3$s Versió compatible Versió no provada! - Connexió a dispositiu: %1$s + Connexió a l\'aparell: %1$s Microprogramari Pebble %1$s Revisió del maquinari correcta La revisió del maquinari no coincideix! %1$s (%2$s) - Hi ha hagut un problema amb la transferència del microprogramari. NO REINICIEU Mi Band! + Hi ha hagut un problema amb la transferència del microprogramari. NO REINICIEU la vostra Mi Band! Hi ha hagut un problema amb la transferència de metadades del microprogramari S\'ha completat la instal·lació del microprogramari - S\'ha completat la instal·lació del microprogramari. Es va a reiniciar el dispositiu… + S\'ha completat la instal·lació del microprogramari. S\'està reiniciant l\'aparell… La instal·lació del microprogramari ha fallat - Passos + Passes Calories Distància Rellotge - Freqüència cardíaca + Ritme cardíac Bateria - Activitat en directe - Passos avui, objectiu: %1$s + Activitat en temps real + Passes avui, objectiu: %1$s Utilitza el mode de baixa latència per actualitzar el microprogramari - Això podria ajudar en dispositius on l\'actualització del microprogramari falla - - Historial de passos - Passos actuals / min - Passos totals - Historial de passos per minut + Això podria ajudar en aparells on l\'actualització del microprogramari falla. + Historial de passes + Passes actuals / min + Passes totals + Historial de passes per minut Inicieu la vostra activitat Activitat Son lleuger Son profund No portat No connectat. - Totes les alarmes són desactivades - Manté les dades d\'activitat al dispositiu + Totes les alarmes estan desactivades + Mantingues les dades d\'activitat a l\'aparell Microprogramari incompatible - Aquest microprogramari no és compatible amb el dispositiu - Alarmes de reserva per als propers esdeveniments + Aquest microprogramari no és compatible amb l\'aparell + Alarmes a reservar per a esdeveniments futurs Utilitza el sensor de ritme cardíac per millorar la detecció del son - Temps de desplaçament en hores del dispositiu (per detectar el son de treballadors/es de torns) - Mi2: format de data + Desplaçament de temps de l\'aparell, en hores (per detectar el son de treballadors/es de torns) + Format de data Temps Hora i data - Accions dels botó - Especifiqueu les accions en prémer el botó del Mi Band 2 - Compte de pulsacions del botó - Nombre de pulsacions del botó per activar la difusió de missatge + Accions del botó + Especifiqueu les accions en prémer el botó de la Mi Band 2 + Nombre de pulsacions del botó + Nombre de vegades que cal prémer el botó per activar la difusió de missatge Missatge de difusió a enviar Activa el botó d\'acció - Activa l\'acció en fer el nombre de pulsacions especificades del botó - Activa la vibració de la pulsera + Activa un acció en prémer un botó un cert nombre de vegades + Activa la vibració de la polsera Activa la vibració de la pulsera en prémer el botó d\'acció Retard màxim entre pulsacions Retard màxim entre pulsacions del botó en mil·lisegons Retard després de l\'acció de botó Notificació d\'objectiu - La banda vibrarà quan l\'objectiu diari de passos siga assolit + La polsera vibrarà quan l\'objectiu diari de passes siga assolit Elements a mostrar - Escolliu els elements a mostrar en la pantalla del rellotge + Escolliu els elements a mostrar en la pantalla de la polsera Activa la pantalla en alçar-la Gira el canell per canviar la informació - No molestis - La pulsera no rebrà notificacions mentre siga activa + No molesteu + La polsera no rebrà notificacions mentre aquesta opció estigui activada Avisos d\'inactivitat - La banda vibrarà quan hàgiu estat inactius durant una estona + La polsera vibrarà quan hàgiu estat inactius durant una estona Llindar d\'inactivitat (en minuts) Desactiva els avisos d\'inactivitat durant un interval de temps Hora d\'inici Hora de finalització - Automàtic Xinès simplificat Xinès tradicional Anglès - Espanyol - + Castellà A punt de transferir dades des de %1$s - - Esperant a reconectar - + S\'està esperant per reconectar Quant a tu Any de naixement - Sexe + Gènere Alçada en cm Pes en kg - S\'està autenticant Es requereix autenticació - Zzz Afegeix un giny Duració preferida del son en hores - S\'ha creat una alarma a les %1$02d:%2$02d + S\'ha fixat una alarma a les %1$02d:%2$02d Revisió del maquinari: %1$s Versió del microprogramari: %1$s - S\'ha produït un error al crear un directori per als fitxers de registre: %1$s - Activitat cardiaca - S\'està actualitzant el programari + S\'ha produït un error en crear un directori per als fitxers de registre: %1$s + "Ritme cardíac: " + S\'està instal·lant el microprogramari No s\'ha enviat el microprogramari - Freqüència cardíaca - Freqüència cardíaca - - + Ritme cardíac + Ritme cardíac + Només client GATT + Això només és per a Pebble 2 i és experimental. Proveu-ho si teniu problemes de connexió + Sou a punt d\'instal·lar el microprogramari %s en la vostra Mi Band 3. +\n +\nSi us plau, assegureu-vos d\'instal·lar el fitxer .fw, i després el fitxer .res. El vostre rellotge es reiniciarà després d\'instal·lar el fitxer .fw. +\n +\nObservació: No cal que instal·leu el fitxer .res si és exactament el mateix que ja estava instal·lat. +\n +\nPROCEDIU SOTA LA VOSTRA PRÒPIA RESPONSABILITAT! + Bloqueja totes les notifications + Permet totes les notificacions + Configuració de ID115 + Orientació de la pantalla + Calibra l\'aparell + Temps mínim entre notificacions + Canvia el color del LED + Canvia la freqüència FM + De dreta a esquerra + Activeu això si el vostre aparell no pot mostrar llengües que s\'escriuen de dreta esquerra + Llargada màxima de línia de dreta a esquerra + Allarga o escurça les línies en què se separa el text escrit de dreta a esquerra + De debò voleu restablir als paràmetres de fàbrica\? + Restablir als paràmetres de fàbrica esborrarà totes les dades de l\'aparell connectat (si és compatible). En el cas dels aparells Xiaomi o Huami, també canvia la seva adreça MAC, de manera que per al Gadgetbridge apareixeran com a un aparell nou. + Configuració de ZeTime + Configuració del rítme cardíac + Durada de la pantalla encesa en segons + Alarma del ritme cardíac + El rellotge us avisarà quan el vostre ritme cardíac superi els límits. + Activa l\'alarma del ritme cardíac + Ritme cardíac màxim + Ritme cardíac mínim + Mode analògic + Només mans + Mans i passes + Activa les trucades de VoIP + Sou a punt d\'instal·lar el microprogramari %s en el vostre Amazfit Cor 2. +\n +\nSi us plau, assegureu-vos d\'instal·lar el fitxer .fw, i després el fitxer .res. La vostra polsera es reiniciarà després d\'instal·lar el fitxer .fw. +\n +\nObservació: No cal que instal·leu el fitxer .res si és exactament el mateix que ja estava instal·lat. +\n +\nPROCEDIU SOTA LA VOSTRA PRÒPIA RESPONSABILITAT! +\n +\nAIXÒ NO HA ESTAT PROVAT ABANS. PROBABLEMENT CAL QUE INSTAL·LEU UN MICROPROGRAMARI BEATS_W SI EL NOM DEL VOSTRE APARELL ÉS \"Amazfit Band 2\" + ESTEU PROVANT D\'INSTAL·LAR UN MICROPROGRAMARI. CONTINUEU SOTA LA VOSTRA RESPONSABILITAT. +\n +\n +\nAquest microprogramari és per a la revisió del maquinari: %s + No s\'han introduït dades vàlides. De moment es fan servir dades fictícies. + Quan la vostra Mi Band vibri i parpallegi, toqueu-la vàries vegades seguides. + Feu que el vostre aparell es pugui detectar. Els aparells que ara estan connectats probablement no seran detectats. Activeu la localització (per exemple, el GPS) a Android 6+. Desactiveu el Privacy Guard per al Gadgetbridge, perquè podria penjar-se i fer que el telèfon es reniciés. Si després d\'uns quants minuts no s\'ha trobat cap aparell, torneu-ho a provar després de reiniciar el telèfon. + No enviïs l\'ACK de les dades d\'activitats + Si l\'arribada de les dades d\'activitats no és notificada a la polsera, aquestes dades no s\'eliminaran. Aquesta opció pot ser útil si feu servir el Gadgebridge també amb altres aplicacions. + Les dades d\'activitats de la Mi Band es guardaran fins i tot després de sincronitzar. Aquesta opció pot ser útil si feu servir el Gadgebridge també amb altres aplicacions. + Messatge que s\'enviarà quan s\'hagi premut el botó el nombre de vegades establert + Retard després d\'una acció de botó (el nombre és a l\'intent extra de button_id) o 0 per procedir immediatament + Desa dades sense processar a la base de dades + Desa les dades \"tal qual\", tot augmentant l\'ús de la base de dades per tal de permetre possibles interpretacions més tard. + Gestió de la base de dades + Gestió de la base de dades + Les operacions de base de dades fan servir el següent camí al vostre aparell. +\nAquest camí és accessible des d\'altres aparells Android i des del vostre ordinador. +\nQuan exporteu una base de dades, la trobareu aquí, i quan vulgueu importar una base de dades, heu de deixar els fitxers també aquí: + Esborra la base de dades antiga + No es pot accedir al camí d\'exportació. Si us plau, contacteu els desenvolupadors. + S\'ha exportat a: %1$s + Hi ha hagut un error en esborrar la base de dades: %1$s + Hi ha hagut un error en exportar la preferència: %1$s + Voleu importar les dades\? + De debò voleu sobreescriure la base de dades actual\? Totes les vostres dades actuals d\'activitats (si en teniu) es perdran. + S\'ha importat. + Hi ha hagut un error en importar la base de dades: %1$s + Hi ha hagut un error en importar la preferència: %1$s + Voleu esborrar les dades d\'activitats\? + De debò voleu esborrar tota la base de dades\? Totes les vostres dades d\'activitats i tota la informació sobre els vostres aparells es perdran. + S\'han esborrat les dades. + Ha fallat la supressió de la base de dades. + Voleu esborrar la base de dades antiga d\'activitats\? + De debò voleu esborrar la base de dades antiga d\'activitats\? Les dades d\'activitats que no haguessin estat importades es perdran. + Les dades antigues d\'activitats s\'han esborrat. + Ha fallat la supressió de la base de dades antiga d\'activitats. + Sobreescriu + Cancel·la + Esborra + Vibració + Aparellament amb Pebble + Apareixerà un diàleg d\'aparellament al vostre aparell Android. Si no, cerqueu-lo entre les notificacions i accepteu la petició d\'aparellament. Després accepteu l\'aparellament també al Pebble. + Assegureu-vos que aquesta aparença està activada en l\'aplicació de notificació del temps per obtenir informació del temps al vostre Pebble. +\n +\nAquí no cal configurar res. +\n +\nPodeu activar l\'aplicació del temps del sistema del vostre Pebble des de la gestió d\'aplicacions. +\n +\nLes caràtules compatibles mostraran el temps automàticament. + Activa l\'aparellament per Bluetooth + Desactiveu això si teniu problemes de connexió + Sistema mètric + Sistema imperial + 24h + AM/PM + Despertador + Activitat Web View + (%1$s) + L\'heu trobat! + Mi2: format d\'hora + Heu d\'instal·lar la versió %1$s abans d\'instal·lar aquest microprogramari! + Notifications de text + Necessita tenir instal·lats el microrprogramari >= 1.10.1.28 i Mili_pro.ft*. + Desactivat + Desactivat + Automàtic (detecció de son) + Programat (interval de temps) + S\'està intentant aparellar amb %1$s + El vincle amb %1$s ha fallat immediatament. + S\'està intentant connectar amb: %1$s + Activa el Bluetooth per detectar aparells. + Enllaçat amb %1$s. + Voleu aparellar amb %1$s\? + Seleccioneu \"Aparella\" per aparellar els vostres aparells. Si no funciona, torneu-ho a provar sense aparellar. + Aparella + No aparellis + Obre a l\'aparell Android + Silencia + Respon + Microprogramari + Les dades no són vàlides + Tipus de lletra + Microprogramari del GPS + Almanac GPS + Correcció d\'errors del GPS + Recursos + Caràtula + Aparell desconegut + Aparell de prova + Pebble + Mi Band + Mi Band 2 + Amazfit Bip + Amazfit Cor + Vibratissimo + LiveView + HPlus + Makibes F68 + Exrizu K8 + No.1 F1 + Teclast H30 + Tria la ubicació de l\'exportació + Notificacions del Gadgetbridge + XWatch + Activat + Les vostres pistes d\'activitat + No mesurat + Activitat + Son lleuger + Son profund + Aparell no usat + Córrer + Caminar + Nedar + Activitat desconeguda + Activitats + Ciclisme + Cinta de córrer + Selecciona-ho tot + Comparteix + Reinicialitza la dada d\'extracció + Estat + Activitat + El Temps + Alarma + Temporitzador + Brúixola + Configuració + Alipay + Alipay (drecera) + El temps (drecera) + Q8 + Mi Band 3 + Recull automàticament les dades d\'activitats + Es recullen dades quan es bloqueja la pantalla. Només funciona si hi ha un sistema de bloqueig establert! + Temps mínim entre extraccions + Extraccions cada %d minuts + MyKronoz ZeTime + Rus + Horitzontal + Vertical + ID115 + Configuració d\'Amazfit Cors + Notificacions + Configuració de Mi Band 2 + Configuració de Mi Band 3 + Més + Música + Quan el vostre aparell vibri, sacsegeu-lo o premeu-ne el botó. + Watch 9 + Minuts: + Hores: + Segons: + Establiu l\'hora que l\'aparell us mostra ara mateix. + Calibra + Aparellament del Watch 9 + Calibratge del Watch 9 + Desbloqueig de la pantalla de la polsera + Passa el dit cap amunt per desbloquejar la pantalla de la polsera + Alemany + Italià + Francès + Polonès + Coreà + Japonès + Heu dormit de %1$s a %2$s + No heu dormit + Noruec bokmål + Sense límit + 5 segons + 10 segons + 20 segons + 30 segons + 1 minut + 5 minuts + 10 minuts + 30 minuts + Mode nocturn + Rebaixa la brillantor de la pantalla de la polsera automàticament a la nit + Al capvespre + D\'acord + Comparteix el registre + Si us plau, tingueu en compte que els fitxers de registre del Gadgetbridge poden contenir molta informació personal, incloent-hi, entre d\'altres, dades relacionades amb la salut, identificadors únics (com ara adreces MAC), preferències musicals, etc. Considereu editar el fitxer i suprimir aquesta informació abans d\'enviar-lo a un informe públic d\'error. + Atenció! + Roidmi + Roidmi 3 + Color del LED + Freqüència de FM + La freqüència no és vàlida + Si us plau, introduïu una freqüència entre 87,5 i 108,0 + Configuració dels gràfics + Ritme cardíac màxim + Ritme cardíac mínim + %1$s nivell de bateria baix + %1$s nivell de bateria baix: %2$s + Àrab contextual + Activeu això per a compatibilitat amb l\'àrab contextual + Compatibilitat amb escriptura de dreta a esquerra + Configuració de llengua i regió + Manca de son: %1$s + Son de més: %1$s + Manca de passes: %1$d + Passes de més: %1$d + No hi ha dades + Ritme cardíac actual / màxim: %1$d / %2$d + Casio GB-6900 + Filtre de notificacions + L\'aplicació no ha d\'estar bloquejada per poder-la configurar + Introduïu les paraules desitjades, cadascuna en una línia nova + S\'ha desat el filtre de notificacions + No filtris + Mostra quan es contenen les paraules + Bloca quan es contenen les paraules + Almenys una de les paraules + Totes les paraules + Si us plau, introduïu com a mínim una paraula + Mode de filtratge + Configuració del mode + Desa la configuració + No està connectat, no s\'ha establert l\'alarma. + Exercici + Notificació de desconnexió + Seguiment d\'activitats + Activeu aquesta opció per comptar les vostres passes i altres activitats. + Moviment de la mà + Moveu el canell per activar o desactivar la pantalla. + Tipus de calories + Només les calories cremades de forma activa + Calories cremades de forma activa i en repòs + Format d\'hora + 24h + 12h + Format de data + AA/MM/DD + DD/MM/AA + MM/DD/AA + Repeticions + Dilluns + Dimarts + Dimecres + Dijous + Divendres + Dissabte + Diumenge + Defineix el tipus de senyal per a l\'alarma + Silenciós + Vibració contínua + Timbre continu + Vibració i timbre continus + Vibra una vegada + Vibra dues vegades + Fes sonar un timbre una vegada + Fes sonar un timbre dues vegades + Vibra i fes sonar un timbre una vegada + Notificació de trucada perduda + Notificació de calendari + Notificació d\'inactivitat + Avís de bateria baixa + Avís anti-pèrdua + cada 15 minuts + cada 45 minuts + Objectiu diari: calories cremades + Objectiu diari: distància en metres + Objectiu diari: temps d\'activitat en minuts + Mi Scale 2 + Configuració específica de l\'aparell + Clau d\'autenticació + Canvia la clau d\'autenticació en tots els aparells Android des des quals et vulguis connectar. La clau anterior per defecte per a tots els aparells és 0123456789@ABCDE + BFH-16 + Neerlandès + Turc + Ucraïnès + Àrab + Indonesi + Tailandès + Vietnamita + Portuguès + Amazfit Cor 2 + Mi Band 4 + \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 91ef8dcd1..26cbd5288 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -43,12 +43,12 @@ Instalátor FW/App - Instaluje firmvér %s do Mi Band místo stávajícího. + Instaluje firmvér %s. Instaluje %1$s a %2$s firmvér, místo stávajícího. Tento firmvér byl testován a je známa jeho kompatibilita s Gadgetbridge. - Tento firmvér nebyl testován a nemusí být kompatibilní s Gadgetbridge. -\n -\nNedoporučujeme jej nahrávat do Mi Band! + Tento firmvér nebyl testován a nemusí být kompatibilní s Gadgetbridge. +\n +\nNedoporučujeme jej nahrávat! Chcete-li stále pokračovat a bude-li vše správně fungovat i po aktualizaci, oznamte vývojářům Gadgetbridge, aby označili tuto verzi firmvéru: %s jako funkční.. Nastavení @@ -435,10 +435,10 @@ Připojit… Chystáte se nainstalovat firmvér %s do vašeho Amazfit Cor. \n -\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Vaše hodinky se po instalaci souboru .fw restartují. +\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Váš se po instalaci souboru .fw restartuje. \n \nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve. -\n +\n \nPOKRAČUJTE NA VLASTNÍ NEBEZPEČÍ! Zapnout možnost potažení mezi grafy aktivit Počasí @@ -476,7 +476,7 @@ Zdroje Ciferník Neznámé zařízení - Testovací zařízení + Test zařízení Pebble Mi Band Mi Band 2 @@ -496,14 +496,14 @@ Pebble 2 experimentální nastavení, zkuste máte-li problémy se spojením Zapnuto Neměřeno - Aktivita + Činnost Lehký spánek Hluboký spánek Zařízení nepoužito Běh Chůze Plavání - Neznámá aktivita + Neznámá činnost Aktivity Cyklistika Vybrat vše @@ -524,13 +524,13 @@ Kalibrovat zařízení Zakázat všechny aplikace Povolit všechny aplikace - Chystáte se nainstalovat firmvér %s do vašeho Mi Band 3. + Chystáte se nainstalovat firmvér %s do vašeho Mi Band 3. \n -\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Vaše hodinky se po instalaci souboru .fw restartují. +\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Váš pásek se po instalaci souboru .fw restartuje. \n \nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve. -\n -\nNETESTOVÁNO, MŮŽE POŠKODIT VAŠE ZAŘÍZENÍ, POKRAČUJTE NA VLASTNÍ NEBEZPEČÍ! +\n +\nPOKRAČUJTE NA VLASTNÍ NEBEZPEČÍ! Minimální doba mezi upozorněními Zprava doleva Povolte, nepodporuje-li vaše zařízení jazyky zprava doleva @@ -584,7 +584,7 @@ Ok Při západu slunce Záznamy aktivit - Běhací pás + Běh na pásu Mi Band 3 Q8 MyKronoz ZeTime @@ -687,4 +687,38 @@ Denní cíl: vzdálenost v metrech Denní cíl: aktivita v minutách Mi Scale 2 + Povolit upozornění VOIP hovorů + Nastavení pro jednotlivá zařízení + Autorizační Klíč + Nastavte stejný klíč na všech Vašich zařízeních s Androidem, ze kterých se budete připojovat. Výchozí klíč pro všechna zařízení je 0123456789@ABCDE + BFH-16 + Chystáte se nainstalovat firmvér %s do vašeho Amazfit Cor 2. +\n +\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Váš se po instalaci souboru .fw restartuje. +\n +\nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve. +\n +\nPOKRAČUJTE NA VLASTNÍ NEBEZPEČÍ! +\n +\nABSOLUTNĚ NETESTOVÁNO, S VELKOU PRAVDĚPODOBNOSTÍ MUSÍTE NAINSTALOVAT BEATS_W FIRMVÉR, JMENUJE-LI SE VAŠE ZAŘÍZENÍ \"Amazfit Band 2\" + Holandsky + Turecky + Ukrajinsky + Arabsky + Indonézsky + Thaisky + Vietnamsky + Portugalsky + Amazfit Cor 2 + Mi Band 4 + Chystáte se nainstalovat firmvér %s do vašeho Mi Band 4. +\n +\nProsím, ujistěte se, že jste nainstalovali soubor .fw a potom soubor .res. Váš pásek se po instalaci souboru .fw restartuje. +\n +\nPoznámka: Nemusíte instalovat soubor .res, pokud je stejný jako nainstalován dříve. +\n +\nPOKRAČUJTE NA VLASTNÍ NEBEZPEČÍ! + Alarm měření tepu při sportovní aktivitě + Spodní hodnota + Horní limit \ 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 8822d8ac6..dac38fbdb 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -45,12 +45,12 @@ Kalender auf Blacklist FW/App-Installer - Du installierst jetzt die Firmware %s anstelle der Firmware, die sich derzeit auf deinem Mi-Band befindet. + Du installierst jetzt %s. Du bist dabei, die %1$s und %2$s Firmware zu installieren, anstatt die, die sich derzeit auf deinem Mi Band befinden. Diese Firmware wurde getestet und ist bekanntlich mit Gadgetbridge kompatibel. - Diese Firmware ist nicht getestet und ist möglicherweise nicht mit Gadgetbridge kompatibel. -\n -\nDu wirst GEBETEN, diese nicht auf dein Mi Band zu installieren! + Diese Firmware ist nicht getestet und ist möglicherweise nicht mit Gadgetbridge kompatibel. +\n +\nDu wirst GEBETEN, diese nicht zu flashen! Wenn du dennoch fortfahren möchtest und das Gerät anschliessend korrekt funktioniert, melde bitte den Gadgetbridge-Entwicklern, das sie die Firmwareversion %s auf die Whitelist setzen sollen. Einstellungen @@ -451,11 +451,11 @@ Traditionelles Chinesisch Englisch Du bist dabei, die Firmware %s auf deine Amazfit Cor zu installieren. -\n -\nBitte stelle sicher, dass du die .fw Datei und die .res Datei installierst. Deine Uhr wird nach der Installation der .fw Datei neu gestartet. -\n -\nHinweis: Du musst die .res nicht installieren, wenn diese Dateie genau die gleiche ist wie die zuvor installierte. -\n +\n +\nBitte stelle sicher, dass du die .fw Datei und die .res Datei installierst. Dein Band wird nach der Installation der .fw Datei neu gestartet. +\n +\nHinweis: Du musst .res nicht installieren, wenn es genau das gleiche ist, wie die zuvor installierte. +\n \nINSTALLATION AUF EIGENE GEFAHR! Anzahl der Tastendrücke Streichen nach links/rechts im Diagrammbetrieb aktivieren @@ -530,12 +530,12 @@ Einstellungen Alipay Du bist dabei, die Firmware %s auf dein Mi Band 3 zu installieren. -\n -\nBitte stelle sicher, dass du die .fw Datei und die .res Datei installierst. Deine Uhr wird nach der Installation der .fw Datei neu gestartet. -\n -\nHinweis: Du musst die .res nicht installieren, wenn diese Dateie genau die gleiche ist wie die zuvor installiert -\n -\nNICHT GETESTET, DIES KÖNNTE DEIN GERÄT UNBENUTZBAR MACHEN, INSTALLATION AUF EIGENE GEFAHR! +\n +\nBitte stelle sicher, dass du die .fw Datei und die .res Datei installierst. Dein Band wird nach der Installation der .fw Datei neu gestartet. +\n +\nHinweis: Du musst .res nicht installieren, wenn es genau das gleiche ist, wie die zuvor installierte. +\n +\nINSTALLATION AUF EIGENE GEFAHR! Dies ist nur für Pebble 2 und experimentell, versuche dies, wenn du Verbindungsprobleme hast Aktivitätsdaten automatisch abrufen Mi Band 3 @@ -700,4 +700,38 @@ Warnung vor Verlust alle 15 Minuten alle 45 Minuten + VoIP App-Anrufe aktivieren + Gerätespezifische Einstellungen + Auth-Schlüssel + Ändere den Authentifizierungsschlüssel in einen gemeinsamen Schlüssel auf all deinen Android-Geräten, von denen aus du eine Verbindung herstellen möchtet. Der bisherige Standardschlüssel für alle Geräte ist 0123456789@ABCDE + BFH-16 + Du bist dabei, die %s-Firmware auf Ihrem Amazfit Cor 2 zu installieren. +\n +\nBitte stelle sicher, dass Du die .fw-Datei und danach die .res-Datei installierst. Dein Band wird nach der Installation der.fw-Datei neu gestartet. +\n +\nHinweis: Du musst .res nicht installieren, wenn es genau das gleiche ist, wie die zuvor installierte. +\n +\nAUF EIGENE GEFAHR! +\n +\nVOLLSTÄNDIG UNGETESTET, WAHRSCHEINLICH MUSST DU EINE BEATS_W FIRMWARE FLASHEN, WENN DEIN GERÄTENAME \"Amazfit Band 2\" IST + Niederländisch + Türkisch + Ukrainisch + Arabisch + Indonesisch + Thailändisch + Vietnamesisch + Portugiesisch + Amazfit Cor 2 + Mi Band 4 + Du bist dabei, die Firmware %s auf dein Mi Band 4 zu installieren. +\n +\nBitte stelle sicher, dass du die .fw Datei und die .res Datei installierst. Dein Band wird nach der Installation der .fw Datei neu gestartet. +\n +\nHinweis: Du musst .res nicht installieren, wenn es genau das gleiche ist, wie die zuvor installierte. +\n +\nINSTALLATION AUF EIGENE GEFAHR! + Herzfrequenzalarm bei sportlicher Aktivität + Untere Grenze + Obere Grenze \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 1eccc99b2..ef1026eec 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -41,7 +41,7 @@ Αποκλεισμένες ειδοποιήσεις Αποκλεισμένα ημερολόγια Εγκαταστάτης εφαρμογών/FW - Πρόκειται να εγκαταστήσετε την έκδοση λογισμικού %s αντί για αυτή που έχετε τώρα στο Mi Band σας. + Πρόκειται να εγκαταστήσετε την έκδοση λογισμικού %s. "Πρόκειται να εγκαταστήστε το λογισμικό έκδοσης %s στο Amazfit Bip σας. \n \n @@ -64,7 +64,7 @@ Αυτό το λογισμικό έχει δοκιμαστεί και είναι συμβατό με το Gadgetbridge. Αυτό το λογισμικό δεν έχει δοκιμαστεί και ενδέχεται να μην είναι συμβατό με το Gadgetbridge. \n -\nΔΕΝ συνίσταται η εγκατάσταση του στο Mi Band σας! +\nΔΕΝ συνίσταται η εγκατάσταση του! Διαχειριστής εφαρμογών Αν ακόμη θέλετε να προχωρήσετε και μετά όλα λειτουργούν κανονικά, παρακαλούμε ενημερώστε τους δημιουργούς του Gadgetbridge σχετικά με την %s έκδοση. Ρυθμίσεις @@ -538,7 +538,6 @@ \n \nΣημείωση: Το αρχείο .res δεν χρειάζεται εγκατάσταση αν είναι ακριβώς το ίδιο με αυτό που έχει εγκατασταθεί προηγούμενα. \n -\nΜΗ ΕΠΑΡΚΩΣ ΔΟΚΙΜΑΣΜΕΝΟ, ΜΠΟΡΕΙ ΝΑ ΧΑΛΑΣΕΙ Η ΣΥΣΚΕΥΗ ΣΑΣ, \nΠΡΟΧΩΡΗΣΤΕ ΜΕ ΔΙΚΗ ΣΑΣ ΕΥΘΥΝΗ! Αυτόματη λήψη δεδομένων δραστηριότητας "Mi Band 3 " @@ -697,4 +696,27 @@ Καθημερινός στόχος: απόσταση σε μέτρα Καθημερινός στόχος: ενεργός χρόνος σε λεπτά "Mi Scale 2 " + Ενεργοποίηση κλήσεων εφαρμογής VoIP + Μοναδικές ρυθμίσεις συσκευών + Κλειδί εξουσιοδότησης + Αλλαγή του κλειδιού εξουσιοδότησης σε ένα κοινό για όλες σας τις συσκευές Android από τις οποίες θέλετε να συνδεθείτε. Το προηγούμενο προεπιλεγμένο κλειδί για όλες τις συσκευές είναι 0123456789@ABCDE + BFH - 16 + —Πρόκειται να εγκαταστήσετε το λογισμικό %s για το Amazfit Cor 2. +\n +\nΠαρακαλώ βεβαιωθείτε ότι θα εγκαταστήσετε το αρχείο .fw και μετά το αρχείο .res. Το smartwatch θα επανεκκινήσει μετά την εγκατάσταση του .fw αρχείου. +\n +\nΣημείωση: Το αρχείο .res δεν χρειάζεται εγκατάσταση αν είναι ακριβώς το ίδιο με αυτό που έχει εγκατασταθεί προηγούμενα. +\n +\nΠΡΟΧΩΡΗΣΤΕ ΜΕ ΔΙΚΗ ΣΑΣ ΕΥΘΥΝΗ! +\nΔΕΝ ΕΧΕΙ ΔΟΚΙΜΑΣΤΕΙ, ΠΙΘΑΝΟΝ ΝΑ ΠΡΕΠΕΙ ΝΑ ΕΓΚΑΤΑΣΤΕΙΣΕΤΕ ΤΟ ΛΟΓΙΣΜΙΚΟ \"BEATS_W\" ΑΝ ΤΟ ΛΟΓΙΣΜΙΚΟ ΤΟΥ SMARTWATCH ΣΑΣ ΟΝΟΜΑΖΕΤΑΙ \"Amazfit Band 2\" + Ολλανδικά + Τουρκικά + Ουκρανικά + Αραβικά + Ινδονησιακά + Ταϊλανδέζικα + Βιετναμέζικα + Πορτογαλικά + "Amazfit Cor 2" + "Mi Band 4" \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d159d4d36..e834bb5e9 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -45,13 +45,13 @@ Calendarios en lista negra Instalador de FW/App - Estás a punto de instalar el firmware %s en lugar del %s que está en tu Mi Band. + Estás a punto de instalar el firmware %s del que tu Mi Band. Estás a punto de instalar los firmware %1$s y %2$s en lugar de los que están en tu MiBand. Este firmware ha sido probado y se sabe que es compatible con Gadgetbridge. Este firmware no ha sido probado y puede que no sea compatible con Gadgetbridge. \n \n¡NO se recomienda la instalación en tu Mi Band! - "Si aún desea continuar y después las cosas continúan funcionando correctamente, pida a los desarrolladores de Gadgetbridge que incluyan en la lista blanca la versión de firmware de% s." + "Si aún desea continuar y después las cosas continúan funcionando correctamente, pida a los desarrolladores de Gadgetbridge que incluyan en la lista blanca la versión de firmware de %s." Ajustes Ajustes generales @@ -75,11 +75,11 @@ Llamadas telefónicas SMS Mensajes de Pebble - Soporte para aplicaciones que envían notificaciones a Pebble a través de PebbleKit + Soporte para aplicaciones que envían notificaciones a Pebble a través de PebbleKit. Soporte para notificaciones genéricas …también con pantalla encendida No Molestar - Dejar de enviar notificaciones no deseadas en el modo No Molestar + Las notificaciones no deseadas se detienen en este modo Transcripción Utilizar en caso de que tu dispositivo no soporte la fuente de tu idioma Siempre @@ -109,7 +109,7 @@ Soporte para llamadas salientes Desactivar esto evitará que el Pebble 2/LE vibre en llamadas salientes Permitir el acceso a aplicaciones Android de terceros - Permitir el soporte experimental para aplicaciones Android que usan PebbleKit + habilitar el soporte experimental para aplicaciones Android que usan PebbleKit Timeline Pebble Salida y puesta de Sol Enviar las horas de salida y puesta de Sol basándose en la localización a la timeline del Pebble @@ -130,14 +130,14 @@ Por favor, activa la localización por red localización encontrada Forzar protocolo de notificación - Esta opción fuerza el uso del último protocolo de notificación dependiendo de la versión de firmware. ¡HABILÍTALO SOLO SI SABES LO QUE ESTÁS HACIENDO! + Esta opción obliga a usar el último protocolo de notificación dependiendo de la versión del firmware. ¡SABE LO QUE ESTÁS HACIENDO! Habilitar características no probadas - Habilita características que no han sido probadas. ¡HABILÍTALO SOLO SI SABES LO QUE ESTÁS HACIENDO! + Habilitar funciones no probadas. ¡HABILÍTALO SÓLO SI SABES LO QUE ESTÁS HACIENDO! Preferir siempre BLE Usar el soporte experimental de Pebble LE para todos los Pebble en lugar del Bluetooth clásico. Esto requiere vincular primero con Pebble no LE, y luego Pebble LE Pebble 2/LE límite de GATT MTU Si tu Pebble 2/Pebble LE no funciona correctamente, prueba esta opción para limitar el MTU (rango válido 20–512) - Activar los logs de las aplicaciones del reloj + Habilitar el registro de la aplicación del reloj Producirá registros de las apps del reloj que Gadgetbridge guardará (necesita reconexión) ACK antes de tiempo de PebbleKit Permitirá a los mensajes enviados a apps de terceros ser reconocidos siempre e inmediatamente @@ -157,16 +157,19 @@ Notificación de prueba desde Gadgetbridge El Bluetooth no está soportado. El Bluetooth está desactivado. - Pulsa el dispositivo conectado para configurar las aplicaciones - Pulsa el dispositivo conectado para actividad - Pulsa el dispositivo conectado para vibración - Pulsar un dispositivo para conectarlo + Toque el dispositivo conectado para el administrador de aplicaciones + Toca el dispositivo conectado para actividad + Toca el dispositivo conectado para vibración + Toca un dispositivo para conectarlo No se puede conectar. ¿Dirección BT incorrecta? Gadgetbridge funcionando Instalando binario %1$d/%2$d - La instalación ha fallado + Instalación fallida Instalado - ESTÁS INTENTANDO INSTALAR UN FIRMWARE, CONTINÚA BAJO TU RESPONSABILIDAD.\n\n\n Este firmware es para la revisión de HW: %s + ESTÁS INTENTANDO INSTALAR UN FIRMWARE, PROCEDE BAJO TU PROPIA RESPONSABILIDAD. +\n +\n +\n Este firmware es para la revisión de HW: %s Estás a punto de instalar la siguiente app:\n\n\n%1$s Versión %2$s de %3$s\n N/A iniciado @@ -520,13 +523,13 @@ Al hacer un restablecimiento de fábrica se eliminarán todos los datos del dispositivo conectado (si se admite). Los dispositivos Xiaomi / Huami también cambian la dirección MAC de Bluetooth, por lo que aparecen como nuevos dispositivos para Gadgetbrige. Lista negra para todas las notificaciones Lista blanca para todas las notificaciones - Está a punto de instalar el firmware %s en su Mi Band 3. -\n -\nAsegúrese de instalar el archivo .fw y, a continuación, el archivo .res. Su reloj se reiniciará después de instalar el archivo .fw. -\n -\nNota: No tiene que instalar .res si es exactamente el mismo que el instalado anteriormente. -\n -\nNO PROBADO, PUEDE BRICKEAR SU DISPOSITIVO, ¡PROCEDE BAJO TU PROPIA RESPONSABILIDAD! + Estás a punto de instalar el firmware %s en su Mi Band 3. +\n +\nAsegúrate de instalar el archivo .fw y, a continuación, el archivo .res. Tu reloj se reiniciará después de instalar el archivo .fw. +\n +\nNota: No tienes que instalar el archivo .res si es el mismo que ha sido instalado previamente. +\n +\nNO PROBADO, PODRÍAS BRICKEAR TU DISPOSITIVO, ¡PROCEDE BAJO TU PROPIA RESPONSABILIDAD! Tiempo mínimo entre notificaciones Derecha a Izquierda Habilite esto si su dispositivo no puede mostrar idiomas de derecha a izquierda @@ -631,4 +634,67 @@ Soporte para Derecha a Izquierda Compartir registro ¡Advertencia! + Configuraciones de ZeTime + Ajustes de frecuencia cardíaca + Duración de la pantalla en seguntos + Alarma de frecuencia cardíaca + El reloj te avisará cuando tu frecuencia cardíaca exceda los límites. + Habilitar la alarma de frecuencia cardíaca + Frecuencia cardíaca máxima + Frecuencia cardíaca mínima + Modo analógico + Solo manos + Manos y pasos + "Seguimiento de actividad" + Al activar el seguimiento de actividad, contará sus pasos, etc. + Movimiento de mano + Gire la muñeca para activar o desactivar la pantalla. + Tipo de calorías + Solo activo para quemar calorías + Formato de hora + 24H + 12H + Formato de fecha + aa/mm/dd + dd/mm/aa + mm/dd/aa + Repeticiones + Lunes + Martes + Miércoles + Jueves + Viernes + Sábado + Domingo + Establecer el tipo de señalización para la alarma + Silencio + Vibración Continua + Pitido continuo + Vibración continua y pitidos + Vibrar una vez + Vibrar dos veces + Sonar una vez + Sonar dos veces + Vibrar y sonar una vez + Habilitar llamadas a la aplicación VoIP + Configuraciones específicas del dispositivo + Clave de autenticación + Cambie la clave de autenticación a una clave común en todos sus dispositivos Android desde los que desea conectarse. La clave predeterminada anterior para todos los dispositivos es 0123456789@ABCDE + "Estás a punto de instalar el firmware para su Amazfit Cor 2. +\n +\nAsegúrate de instalar el archivo .fw y luego el archivo .res. Tu banda se reiniciará después de instalar el archivo .fw. +\n +\nNota: No tienes que instalar el archivo .res si es el mismo que ya ha sido previamente instalado. +\n +\n¡PROCEDE BAJO TU PROPIO RIESGO!." + Estás a punto de instalar el firmware% s en tu Mi Band 4. +\n +\nAsegúrate de instalar el archivo .fw y luego el archivo .res. Tu banda se reiniciará después de instalar el archivo .fw. +\n +\nNota: No tienes que instalar el archivo .res si es el mismo que ha sido previamente instalado. +\n +\n¡PROCEDE BAJO TU PROPIO RIESGO! + Alarma de frecuencia cardíaca durante actividad deportiva. + Límite Bajo + Límite Alto \ No newline at end of file diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index a0275bfde..8d7204196 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -1,6 +1,6 @@ - -Gadgetbridge - + + + Gadgetbridge Gadgetbridge Seaded Silumine @@ -9,7 +9,7 @@ Sünkrooni Leia kadunud seade Tee ekraanipilt - Ühenda + Ühenda… Ühenda lahti Kustuta seade Kustuta %1$s @@ -20,10 +20,7 @@ Ühendan lahti Ühendan Teen seadme ekraanist pilti - - Silumine - Rakendustehaldur Rakendused vahemälus Paigaldatud rakendused @@ -41,11 +38,8 @@ Paigalda ilmateavituste rakendus Seadista Liiguta kõige üles - Teavituste must nimekiri - Kalendrite must nimekiri - Püsivara/rakenduse paigaldaja Olete installeerimas püsivara %s selle asemel, mis praegu teie Mi Bandil on. Olete installimas %s püsivara enda Amazfit Bip\'ile. @@ -68,7 +62,6 @@ \n \nSoovitame mitte seda teie Mi Bandile paigaldada! Seaded - Üldised seaded Ühendu seadmega kui Bluetooth lülitatakse sisse Käivitu automaatselt @@ -78,17 +71,12 @@ Kuupäev ja kellaaeg Sünkrooni aega "Sünkrooni aeg seadmega ühendumise ajal või kui Androidis aeg või ajatsoon muutub " - Kui te siiski soovite jätkata ja seade jätkab tööd ka hiljem palun andke Gadgetbridge\'i arendajatele teada, et nad lisaksid %s versiooni toetatud püsivarade nimekirja. - Luba viiped vasakule/paremale graafikute vaates - Teema Hele Tume - Keel - Peida Gadgetbridge\'i teavitus Teavitused Korduseid @@ -101,26 +89,20 @@ Alati Kui ekraan on väljalülitatud Mitte kunagi - Privaatsus Kuva nimi ja number Peida number kuid kuva nimi Peida nimi ja number - Ilm Ilma asukohta (CM/LOS) - Rakenduste must nimekiri Kalendrite must nimekiri - Olekuriba ikooni ja lukustusekraani märguandeid kuvatakse Olekuriba ikoon ja lukustuserkaani märguanded on peidetud - Tugi rakendustele, mis saadavad PebbleKit\'i kaudu märguandeid Pebble\'le. ...ka sisse lülitatud ekraaniga Peata soovimatute märguannete saatmine \"Ära sega\" režiimis Luba see, kui su seadmel puudub su keele fondi tugi - Kõne privaatrežiim Peida nimi, kuva ainult number Talletatud sõnumid @@ -128,22 +110,15 @@ Ühine järelliide Arendajate seaded Mi Band\'i aadress - Uuenda Pebble\'l - Pebble\'i seaded - Aktiivsusmonitorid Eelistatud aktiivsusmonitor Sünkrooni Pebble\'i terviseandmeid Sünkrooni Misfit\'i Kalibreeri seade - - Lisa kõikide nende teatised musta nimekirja Lisa kõikide nende teatised valgesse nimekirja - - Te kavatsete installida %s püsivara enda Mi Band 3le. \n \nVeenduge, et installite .fw faili ja seejärel .res fail. Kell taaskäivitub pärast .fw faili installimist. @@ -153,43 +128,35 @@ \nTESTIMATA, TEIE SEADE VÕIB MUUTUDA PABERIRASKUSEKS, JÄTKATE OMAL RIISIKOL! Kõne hargile panek Sünkrooni Morpheuz - Toeta väljaminevaid kõnesid Selle valiku väljalülitamine lülitab välja ka Pebble 2/LE värisemise väljaminevate kõnede puhul - Luba kolmanda osapoolte rakenduste ligipääs Lülita sisse eksperimentaalne PebbleKit\'i kasutavate rakenduste tugi - Pebble ajajoon Päikesetõus ja päikeseloojang Saada päikesetõusu ja päikeseloojangu ajad asukoha järgi Pebble ajajoonele Sünkrooni kalender Saada kalendri sündmused ajajoonele - Eemalda automaatselt eemaldatud teatised Teatised eemaldatakse automaatselt Pebble\'st kui need eemaldatakse Androidi seadmest - Privaatsusrežiim Tavalised teatised Nihuta teatise tekst ekraanilt välja Näita ainult teatise ikooni - Asukoht Küsi asukoht Laiuskraad Pikkuskraad Hoia asukoht ajakohasena Proovi küsida ajakohast asukohta jooksvalt, kasuta eelnevalt salvestatut kui eelnev ei õnnestu - Palun lülita sisse võrgu järgi seatud asukoht asukoht leitud - Sunniviisiliselt kasuta teatiste protokolli See valik sunnib kõige uuema teatiste protokolli kasutamise vastavalt püsivara versioonile. KASUTA AINULT KUI SA TEAD MIDA SA TEED! Lülita sisse testimata funktsioonid Lülita sisse testimata funktsioonid. TEA, MIDA SA TEED! Alati eelista Bluetooth LE\'d - Allalaadimine käivitatakse ekraani lahtilukustamise ajal. Töötab ainult sellel juhul kui lukustusmehhanism on seatud! + Import käivitatakse ekraani lahtilukustamise ajal. Töötab ainult sellel juhul kui lukustusmehhanism on seatud! "Pulss: " Lühim aeg teavituste vahel Kasuta eksperimentaalset Pebble LE tuge kõikide Pebble\'de jaoks tavalise Bluetoothi asemel. See nõuab sidumist tavalise Bluetoothi kaudu ja seejärel Bluetooth LE sidumist @@ -198,4 +165,121 @@ Lülita sisse kellarakenduste logimine Ainult GATT klient See on ainult Pebble 2le ja eksperimentaalne, katseta kui on ilmnenud ühenduvusprobleeme - + Muuda LED värvi + Muuda FM sagedus + Kas taastada algseaded\? + Alseadete taastamine kustutab kõik andmed ühendatud seadmetest (kui see on toetatud). Xiaomi/Huami seadmed muudavad ka Bluetooth MAC aadressi ja Gadgetbrige tuvastab need uute seadmetena. + Luba VoIP rakenduse kõned + Paremalt vasakule + Luba see, kui seade ei saa kuvada paremalt vasakule keeli + Paremalt vasakule suurim rea pikkus + Luba taust JS + Kui lubatud, võimaldab kellal kuvada ilma, aku andmeid jne. + Taasühendamise katsed + Ühikud + Kellaaja vorming + Ekraani avatuse kestus + Kogu päeva südame löögisageduse mõõtmine + HPlus/Makibes seaded + ID115 seaded + Ekraani suund + ZeTime seaded + Südame löögisageduse seaded + Ekraani avatuse kestus sekundites + Südame löögisageduse alarm + Kell hoiatab, kui südame löögisagedus ületab piirid. + Luba südame löögisageduse alarm + Suurim südame löögisagedus + Vähim südame löögisagedus + Analoog režiim + Ainult käed + Käed ja sammud + Tegevuse jälgimine + Tegevuse jälgimise sisselülitamisel loetakse sammude arvu jne. + Käte liikumine + Pööra rannet, et ekraan aktiveerida või deaktiveerida. + Kalorite tüüp + Ainult aktiivselt põletatud kalorid + Aktiivselt ja passiivselt põletatud kalorid + Kellaaja vorming + 24h + 12h + Kuupäeva vorming + Kordused + Esmaspäev + Teisipäev + Kolmapäev + Neljapäev + Reede + Laupäev + Pühapäev + Määra alarmi märguande tüüp + Vaikne + Pidev värin + Pidev piiksumine + Pidev värin ja piiksumine + Vibreeri üks kord + Vibreeri kaks korda + Üks piiks + Kaks piiksu + Vibreeri ja piiksu üks kord + Seadme spetsiifilised sätted + Automaatne eksport + Automaatne eksport lubatud + Ekspordi asukoht + Ekspordi intervall + Eksport iga %d tunni järel + Tegevuste andmete automaatne import + Vähim importimise vahemik + Import iga %d minuti järel + Pole ühendatud + Ühendamine + Ühendatud + Tundmatu olek + (teadmata) + Test + Testi märguande + See on Gadgetbridge test teavitus + Bluetooth ei ole toetatud. + Bluetooth on keelatud. + Puuduta rakenduste halduri jaoks ühendatud seadet + Puuduta ühendatud seadet tegevuse jaoks + Puuduta ühendatud seadet värina tekitamiseks + Puuduta seadet, et ühendada + Ei saa ühendust. Vigane bluetooth aadress\? + Gadgetbridge töötab + Binaarfaili %1$d/%2$d paigaldamine + Paigaldamine nurjus + Paigaldatud + ÜRITAD PAIGALDADA PÜSIVARA, JÄTKA OMAL VASTUTUSEL +\n +\n +\nSee püsivara on sobilik HW Revision: %s + Kavas on paigaldada rakendus: +\n +\n +\n%1$s versioon %2$s / %3$s +\n + %1$s / %2$s + Seadme tuvastamine + Peata skannimine + Alusta avastamist + Ühenda uus seade + %1$s (%2$s) + Seadme sidumine + Kasuta seadme sidumiseks Androidi Bluetoothi sidumise dialoogi. + Seo oma Mi Band + Sidumine %s… + Sideme loomine - %1$s (%2$s) + Sidumine nurjus - %1$s (%2$s) + Toimub sidumine: %1$s (%2$s) + Värinate arv + Värina profiil + Värin + Värina seaded + Tühista värina peatamiseks. + Luba võru värin + Luba värin nupu abil + Värin + Lähtesta impordi aeg + \ No newline at end of file diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 94d260204..ec536005f 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -48,4 +48,80 @@ همه را از لیست سیاه آگاهی خارج کن رخدادهای درون لیست سیاه نصب کننده‌ی فرم‌ویر (FW)/برنامه + شما در حال نصب فرمویر %s به جای نسخه‌ی نصب شده برروی دستگاه Mi Band خود هستید. + شما در حال نصب فرمویر %s به جای نسخه‌ی نصب شده برروی دستگاه Amazfit Bip خود هستید. +\n +\nلطفا مطمئن شوید که ابتدا فایل .fw، سپس فایل .res و در انتها فایل .gps را نصب کنید. دستگاه شما پس از نصب فایل .fw راه‌اندازی مجدد می‌شود. +\n +\nنکته: شما مجبور به نصب فایل‌های .res و .gps نیستید اگر آنها با فایل‌هایی که قبلا نصب شده‌اند یکی هستند. +\n +\nبا مسئولیت خودتان این کار را انجام می‌دهید! + شما در حال نصب فرمویر %s به جای نسخه‌ی نصب شده برروی دستگاه Amazfit Cor خود هستید. +\n +\nلطفا مطمئن شوید که ابتدا فایل .fw، سپس فایل .res و در انتها فایل .gps را نصب کنید. دستگاه شما پس از نصب فایل .fw راه‌اندازی مجدد می‌شود. +\n +\nنکته: شما مجبور به نصب فایل‌های .res و .gps نیستید اگر آنها با فایل‌هایی که قبلا نصب شده‌اند یکی هستند. +\n +\nبا مسئولیت خودتان این کار را انجام می‌دهید! + شما در حال نصب فرمویر %s به جای نسخه‌ی نصب شده برروی دستگاه Mi Band 3 خود هستید. +\n +\nلطفا مطمئن شوید که ابتدا فایل .fw، سپس فایل .res و در انتها فایل .gps را نصب کنید. دستگاه شما پس از نصب فایل .fw راه‌اندازی مجدد می‌شود. +\n +\nنکته: شما مجبور به نصب فایل‌های .res و .gps نیستید اگر آنها با فایل‌هایی که قبلا نصب شده‌اند یکی هستند. +\n +\nبا مسئولیت خودتان این کار را انجام می‌دهید! + شما در حال نصب فرمویر %1$s و %2$s به جای نسخه‌های نصب شده برروی دستگاه Mi Band خود هستید. + این نسخه‌ی فرمویر تست شده و با گجت‌بریج سازگار است. + این فرمویر تست نشده و ممکن است با گجت‌بریج سازگار نباشد. +\n +\nنصب این نسخه روی Mi Band پیشنهاد نمی‌شود! + اگر همچنان مایل به نصب هستید و بعد از نصب همه چیز درست کار کرد، لطفا به توسعه‌دهنده‌های گجت‌بریج اطلاع دهید تا فرمویر %s را به لیست سفید اضافه کنند. + تنظیمات + تنظیمات عمومی + اتصال به گجت‌بریج وقتی که بلوتوث روشن است + راه‌اندازی اتوماتیک + اتصال‌مجدد اتوماتیک + پخش کننده‌ی موسیقی مورد علاقه + پیش‌فرض + فعال سازی امکان کشیدن به چپ/راست در قسمت نمودارهای برنامه + تاریخ و ساعت + همزمان‌سازی ساعت + همزمان‌سازی ساعت با گجت‌بریج وقتی متصل می‌شود و همچنین وقتی موقعیت زمانی برروی گوشی تغییر می‌کند + پوسته + روشن + تاریک + زبان + مخفی کردن اعلان گجت‌بریج + آیکون برنامه در نوار اعلان‌ها و صفحه‌ی قفل نشان داده می‌شود + آیکون برنامه در نوار اعلان‌ها و صفحه‌ی قفل نشان داده نمی‌شود + اعلان‌ها + تکرار + تماس تلفنی + فعال‌سازی VoIP بوسیله‌ی برنامه‌ها + پیامک + حداقل زمان بین اعلان‌ها + پیام‌های Pebble + پشتیبانی از برنامه‌هایی که به Pebble بوسیله‌ی PebbleKit پیام می‌فرستند. + پشتیبانی از اعلان‌های عمومی + ...همچنین وقتی صفحه‌ی نمایش روشن است + حالت مزاحم نشوید + اعلان‌های ناخواسته در این حالت غیرفعال می‌شوند + نویسه‌گردانی (Transliteration، برای نمایش انگلیسی حروف فارسی و عربی) + این حالت را فعال کنید اگر دستگاه شما حروف زبانتان را پشتیبانی نمی‌کند + راست-به-چپ + این گزینه را اگر دستگاهتان راست‌چین را پشتیبانی نمی‌کند فعال کنید + بیشترین طول خط راست-به-چپ + خطوط راست-به-چپ را کوتاه و بلند می‌کند + همیشه + وقتی صفحه نمایش خاموش است + هرگز + حریم شخصی + حالت حریم شخصی تماس تلفنی + نمایش نام و شماره + مخفی کردن نام ولی نمایش شماره + مخفی کردن شماره ولی نمایش نام + مخفی کردن شماره و نام + هواشناسی + مکان تعیین شده برای هواشناسی (CM/LOS) + اضافه کردن برنامه‌ها به لیست سیاه \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 76946e0ef..da61eb43b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,412 +1,412 @@ - Gadgetbridge - Gadgetbridge - Paramètres - Déboguer - Quitter - Faire un don - Synchroniser - Suivi du sommeil (ALPHA) - Retrouver votre appareil perdu - Prendre une capture d\'écran - Déconnecter - Supprimer l’appareil - Supprimer %1$s - Ceci va supprimer l’appareil et toutes les données associées ! - Ouvrir le tiroir de navigation - Fermer le tiroir de navigation - Presser longuement l\'icône pour déconnecter - Déconnexion - Connexion… - Capturer l\'écran de l\'appareil - Déboguer - - Gestionnaire d\'application - Apps en cache - Apps installées - Watchfaces installées - Supprimer - Supprimer et effacer du cache - Réinstaller - Rechercher dans le magasin d’application Pebble - Activer - Désactiver - Activer la mesure du rythme cardiaque - Désactiver la mesure du rythme cardiaque - Activer l\'application météo - Désactiver l\'application météo - Installer l\'application de notification de la météo - Configurer - Haut de page - - Liste noire de notifications - - Calendriers sur liste noire - - Installateur d\'applications/firmware - Vous êtes sur le point d\'installer le micrologiciel %s à la place de celui qui est actuellement sur votre Mi Band. - Vous êtes sur le point d\'installer les micrologiciels %1$s et %2$s à la place de ceux qui sont actuellement sur votre Mi Band. - Ce micrologiciel a été testé et est connu pour être compatible avec Gadgetbridge. - Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge. + Gadgetbridge + Gadgetbridge + Paramètres + Déboguer + Quitter + Faire un don + Synchroniser + Suivi du sommeil (ALPHA) + Retrouver votre appareil perdu + Prendre une capture d\'écran + Déconnecter + Supprimer l’appareil + Supprimer %1$s + Ceci va supprimer l’appareil et toutes les données associées ! + Ouvrir le tiroir de navigation + Fermer le tiroir de navigation + Presser longuement l\'icône pour déconnecter + Déconnexion + Connexion… + Capturer l\'écran de l\'appareil + Déboguer + + Gestionnaire d\'application + Apps en cache + Apps installées + Watchfaces installées + Supprimer + Supprimer et effacer du cache + Réinstaller + Rechercher dans le magasin d’application Pebble + Activer + Désactiver + Activer la mesure du rythme cardiaque + Désactiver la mesure du rythme cardiaque + Activer l\'application météo + Désactiver l\'application météo + Installer l\'application de notification de la météo + Configurer + Haut de page + + Liste noire de notifications + + Calendriers sur liste noire + + Installateur d\'applications/firmware + Vous êtes sur le point d\'installer le micrologiciel %s. + Vous êtes sur le point d\'installer les micrologiciels %1$s et %2$s à la place de ceux qui sont actuellement sur votre Mi Band. + Ce micrologiciel a été testé et est connu pour être compatible avec Gadgetbridge. + Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge. \n -\nIl n\'est PAS conseillé de le flasher sur votre Mi Band ! - Si vous désirez continuer et que tout fonctionne correctement par la suite, veuillez en informer les développeurs de Gadgetbridge pour demander l\'ajout du micrologiciel %s à leur liste. - - Paramètres - Paramètres généraux - Connecter votre appareil quand le Bluetooth est activé - Démarrer automatiquement - Reconnexion automatique - Lecteur audio préféré - Par défaut - Date et heure - Synchroniser l\'heure - Synchroniser l\'heure sur l\'appareil lors de la connexion, et lorsque l\'heure ou le fuseau horaire changent sur Android - Thème - Clair - Sombre - Langue - Masquer la notification de Gadgetbridge - L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont affichées - L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont masquées - Notifications - Répétitions - Appels téléphoniques - Textos - Messages Pebble - Support des applications qui envoient des notification à Pebble via PebbleKit. - Support des notifications génériques - ...y compris quand l\'écran est allumé - Ne Pas Déranger - Les notifications non-sollicitées sont suspendues dans ce mode - Transcription - Activez ceci si votre appareil ne supporte pas la police de caractères - Toujours - Quand l\'écran est éteint - Jamais - Confidentialité - Mode de confidentialité d\'appel - Afficher le nom et le numéro - Masquer le nom mais afficher le numéro - Masquer le numéro mais afficher le nom - Masquer le nom et le numéro - Applications bloquées - Mettre des calendriers en liste noire - Modèles de messages - Réponses - Suffixe fréquent - Raccrocher - Mise à jour Pebble - Options développeur - Adresse Mi Band - Paramètres Pebble - Traqueurs d\'activité - Traqueur d\'activité préféré - Synchroniser Pebble Health - Synchroniser Misfit - Synchroniser Morpheuz - Support des appels sortants - Désactiver ceci empêchera la Pebble 2/LE de vibrer lors des appels sortants - Permettre l\'accès aux applications tierces Android - Activer le support expérimental pour les applications Android utilisant PebbleKit - Timeline Pebble - Lever et coucher de soleil - Envoyer les heures de lever et coucher du soleil dans l’historique Pebble en fonction de l\'emplacement - Synchroniser le calendrier - Envoyer les événements du calendrier sur la timeline - Effacer automatiquement les notifications rejetées - Les notifications sont automatiquement effacées de la Pebble lorsqu\'elles sont rejetées sur l\'appareil Android - Mode privé - Notifications normales - Déplace les notifications hors de l\'écran - Ne montre que l\'icône de notification - Emplacement - Obtenir l\'emplacement - Latitude - Longitude - Garder l’emplacement à jour - Essayer de garder la localisation à jour pendant le fonctionnement, sinon utiliser l’emplacement enregistré. - Veuillez activer la localisation réseau - Emplacement obtenu - Forcer le protocole de notification - Cette option force l\'utilisation du protocole de notification le plus récent selon votre version du micrologiciel. A VOS PROPRES RISQUES! - Activer les fonctionnalités non-testées - Activer les fonctionnalités non-testées. A VOS PROPRES RISQUES ! - Toujours préférer le BLE - Utiliser le support expérimental du LE pour toutes les Pebble au lieu du Bluetooth classique. Cela requiert le jumelage d\'une Pebble non LE, puis d\'une Pebble LE - Limite du GATT MTU de Pebble 2/LE - Si votre Pebble 2/LE ne fonctionne pas correctement, essayez d\'activer cette option pour limiter le MTU (plage valide 20-512) - Activer les logs des Watch App - Ceci permettra à Gadgetbridge de conserver les logs des Watch App (requiert une reconnexion) - ACK à l\'avance du PebbleKit - Ceci permettra aux messages envoyés à des apps tierces d\'être toujours reconnus immédiatement - Tentatives de reconnexion - Unités - Format de l\'heure - Durée d\'écran allumé - Mesure de la fréquence cardiaque toute la journée - Paramètres HPlus/Makibes - Non connecté - Connexion en cours - Connecté - État inconnu - (inconnu) - Test - Notification de test - Ceci est un test de notification venant de Gadgetbridge - Le Bluetooth n\'est pas supporté. - Le Bluetooth est désactivé. - Cliquez sur l\'appareil pour ouvrir le gestionnaire d\'application - Cliquez sur l\'appareil pour ouvrir le gestionnaire d’activité - Cliquez sur connecter pour envoyer une vibration - Tapez sur le périphérique pour le connecter - Connexion impossible. L’adresse Bluetooth est-elle valide ? - Gadgetbridge est en fonctionnement - Installation du binaire %1$d/%2$d - Échec de l\'installation - Installé - VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS RISQUES ET PÉRILS.\n\n\nCe micrologiciel est pour la version de matériel : %s - Vous êtes sur le point d\'installer l\'application suivante :\n\n\n%1$s Version %2$s par %3$s\n - N.D. - Initialisé - %1$s par %2$s - Scanner les appareils - Arrêter le scan - Démarrer le scan - Connecter un nouvel appareil - %1$s (%2$s) - Appairer l\'appareil - Utiliser l\'appairage Bluetooth d\'Android pour jumeler l\'appareil. - Appairer votre Mi Band - Jumelage avec %s… - Création d’un lien avec %1$s (%2$s) - Impossible se s’appairer avec %1$s (%2$s) - Création du lien en cours : %1$s (%2$s) - Déjà lié avec %1$s (%2$s), connexion… - Aucune adresse MAC fournie, ne peut être appairé. - Paramètres spécifiques à l\'appareil - Paramètres Mi Band / Amazfit - Homme - Femme - Autre - Gauche - Droite - Aucune donnée utilisateur valide fournie, utilisation de données fictives pour le moment. - Quand votre Mi Band vibre et clignote, appuyez dessus plusieurs fois d\'affilée. - Installer - Mettez votre appareil en mode visible. Les appareils déjà connectés ne seront pas visibles. Sur Android 6 ou supérieur, vous devez activer la localisation (ex. GPS). Désactivez Privacy Guard pour Gadgetbridge, il pourrait stopper et redémarrer votre téléphone. Si votre appareil n\'est pas visible après 2 minutes, réessayez après avoir redémarré votre téléphone. - Note : - Image de l\'appareil - Nom/Pseudo - Nombre de vibrations - Moniteur de sommeil - Écrire des fichiers journaux - Initialisation - Récupération des données d\'activité - De %1$s à %2$s - Port main gauche ou droite ? - Profil de vibration - Saccadé - Court - Moyen - Long - Goute d\'eau - Sonnette - Réveil - Vibration - Essayer - Notification Texto - Paramètres des vibrations - Notification générique - Notification des mails - Notification d\'appels entrants - Tchat - Navigation - Réseau social - Zones de vitesse - Total de minutes - Pas par minute - Trouver l\'appareil perdu - Annuler pour arrêter les vibrations. - Votre activité - Configurer les alarmes - Configurer les alarmes - Détails des alarmes - Dim - Lun - Mar - Mer - Jeu - Ven - Sam - Réveil intelligent - Une erreur s\'est produite lors du paramétrage des alarmes, veuillez réessayer. - Alarmes envoyées à l\'appareil. - Aucune donnée. Synchroniser l\'appareil ? - Sur le point de transférer %1$s de données à partir de %2$s - Objectif de pas par jour - Erreur lors de l’exécution de %1$s\' - Votre activité (ALPHA) - Impossible de se connecter : %1$s - Impossible de trouver un gestionnaire pour installer ce fichier. - Impossible d\'installer le ficher suivant : %1$s - Impossible d\'installer le micrologiciel spécifié : il ne correspond pas à la version du matériel de votre Pebble. - Veuillez patienter pendant la détermination de l\'état de l\'installation… - Niveau de batterie faible ! - %1$s batterie restante : %2$s%% - Dernière charge : %s \n - Nombre de charges : %s - Votre sommeil - Sommeil de la semaine - Sommeil aujourd\'hui, objectif : %1$s - Pas de la semaine - Votre activité et sommeil - Installation du micrologiciel… - Le fichier ne peut pas être installé, l\'appareil n\'est pas prêt. - %1$s: %2$s %3$s - Version compatible - Version non-testée ! - Connexion à l\'appareil : %1$s - Micrologiciel Pebble %1$s - Version du matériel correcte - Version du matériel incorrecte ! - %1$s (%2$s) - Problème avec le transfert du micrologiciel. Ne redémarrez pas votre Mi Band ! - Problème avec le transfert de métadonnées du micrologiciel - Installation complète du micrologiciel - Installation complète du micrologiciel, redémarrage de l\'appareil… - Échec lors de l\'installation du micrologiciel - Pas - Calories - Distance - Horloge - Fréquence cardiaque - Batterie - Activité en direct - Nombre de pas aujourd\'hui, objectif : %1$s - Ne pas confirmer le transfert de données d\'activités - Les données d\'activités ne seront pas effacées du bracelet si elles ne sont pas confirmées. Utile si GB est utilisé avec d\'autres applications. - Les données d\'activités seront conservées sur le Mi Band après la synchronisation. Utile si GB est utilisé avec d\'autres applications. - Utilisez le mode basse latence pour installer un micrologiciel - Cela peut aider sur les appareils où les installations de micrologiciel échouent. - Historique de pas - Pas/minute actuel - Nombre total de pas - Historique de pas/minute - Démarrez votre activité - Activité - Sommeil léger - Sommeil profond - Non porté - Non connecté. - Toutes alarmes désactivées - Conserver les activités sur l\'appareil - Micrologiciel non compatible - Ce micrologiciel n\'est pas compatible avec l\'appareil - Alarmes à réserver pour événements futurs - Utiliser le capteur cardiaque pour améliorer la détection du sommeil - La compensation de temps en heure (pour travailleurs en rotation, par exemple) - Format de la date - Heure - - Notification d\'objectif - Le bracelet vibrera lorsque l\'objectif de pas quotidien sera atteint - Éléments à afficher - Choisissez les éléments à afficher sur le bracelet - Allumer l\'écran lors d\'un mouvement - Tournez votre poignet pour changer d\'élément - Ne Pas Déranger - Le bracelet ne recevra pas de notifications si activé - Alertes d\'inactivité - Le bracelet vibrera si vous n\'êtes pas actif pendant un moment - Seuil d\'inactivité (en minutes) - Désactiver les alertes d\'inactivité pendant un intervalle de temps - Heure de début - Heure de fin - Sur le point de transférer des données depuis %1$s - En attente de reconnexion - À propos de vous - Année de naissance - Genre - Taille en cm - Poids en kg - Authentification - Authentification requise - ZzZz - Ajouter un widget - +\nIl n\'est PAS conseillé de le flasher ! + Si vous désirez continuer et que tout fonctionne correctement par la suite, veuillez en informer les développeurs de Gadgetbridge pour demander l\'ajout du micrologiciel %s à leur liste. + + Paramètres + Paramètres généraux + Connecter votre appareil quand le Bluetooth est activé + Démarrer automatiquement + Reconnexion automatique + Lecteur audio préféré + Par défaut + Date et heure + Synchroniser l\'heure + Synchroniser l\'heure sur l\'appareil lors de la connexion, et lorsque l\'heure ou le fuseau horaire changent sur Android + Thème + Clair + Sombre + Langue + Masquer la notification de Gadgetbridge + L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont affichées + L\'icône de la barre d\'état et la notification de l\'écran de verrouillage sont masquées + Notifications + Répétitions + Appels téléphoniques + Textos + Messages Pebble + Support des applications qui envoient des notification à Pebble via PebbleKit. + Support des notifications génériques + ...y compris quand l\'écran est allumé + Ne Pas Déranger + Les notifications non-sollicitées sont suspendues dans ce mode + Transcription + Activez ceci si votre appareil ne supporte pas la police de caractères + Toujours + Quand l\'écran est éteint + Jamais + Confidentialité + Mode de confidentialité d\'appel + Afficher le nom et le numéro + Masquer le nom mais afficher le numéro + Masquer le numéro mais afficher le nom + Masquer le nom et le numéro + Applications bloquées + Mettre des calendriers en liste noire + Modèles de messages + Réponses + Suffixe fréquent + Raccrocher + Mise à jour Pebble + Options développeur + Adresse Mi Band + Paramètres Pebble + Traqueurs d\'activité + Traqueur d\'activité préféré + Synchroniser Pebble Health + Synchroniser Misfit + Synchroniser Morpheuz + Support des appels sortants + Désactiver ceci empêchera la Pebble 2/LE de vibrer lors des appels sortants + Permettre l\'accès aux applications tierces Android + Activer le support expérimental pour les applications Android utilisant PebbleKit + Timeline Pebble + Lever et coucher de soleil + Envoyer les heures de lever et coucher du soleil dans l’historique Pebble en fonction de l\'emplacement + Synchroniser le calendrier + Envoyer les événements du calendrier sur la timeline + Effacer automatiquement les notifications rejetées + Les notifications sont automatiquement effacées de la Pebble lorsqu\'elles sont rejetées sur l\'appareil Android + Mode privé + Notifications normales + Déplace les notifications hors de l\'écran + Ne montre que l\'icône de notification + Emplacement + Obtenir l\'emplacement + Latitude + Longitude + Garder l’emplacement à jour + Essayer de garder la localisation à jour pendant le fonctionnement, sinon utiliser l’emplacement enregistré. + Veuillez activer la localisation réseau + Emplacement obtenu + Forcer le protocole de notification + Cette option force l\'utilisation du protocole de notification le plus récent selon votre version du micrologiciel. A VOS PROPRES RISQUES! + Activer les fonctionnalités non-testées + Activer les fonctionnalités non-testées. A VOS PROPRES RISQUES ! + Toujours préférer le BLE + Utiliser le support expérimental du LE pour toutes les Pebble au lieu du Bluetooth classique. Cela requiert le jumelage d\'une Pebble non LE, puis d\'une Pebble LE + Limite du GATT MTU de Pebble 2/LE + Si votre Pebble 2/LE ne fonctionne pas correctement, essayez d\'activer cette option pour limiter le MTU (plage valide 20-512) + Activer les logs des Watch App + Ceci permettra à Gadgetbridge de conserver les logs des Watch App (requiert une reconnexion) + ACK à l\'avance du PebbleKit + Ceci permettra aux messages envoyés à des apps tierces d\'être toujours reconnus immédiatement + Tentatives de reconnexion + Unités + Format de l\'heure + Durée d\'écran allumé + Mesure de la fréquence cardiaque toute la journée + Paramètres HPlus/Makibes + Non connecté + Connexion en cours + Connecté + État inconnu + (inconnu) + Test + Notification de test + Ceci est un test de notification venant de Gadgetbridge + Le Bluetooth n\'est pas supporté. + Le Bluetooth est désactivé. + Cliquez sur l\'appareil pour ouvrir le gestionnaire d\'application + Cliquez sur l\'appareil pour ouvrir le gestionnaire d’activité + Cliquez sur connecter pour envoyer une vibration + Tapez sur le périphérique pour le connecter + Connexion impossible. L’adresse Bluetooth est-elle valide ? + Gadgetbridge est en fonctionnement + Installation du binaire %1$d/%2$d + Échec de l\'installation + Installé + VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS RISQUES ET PÉRILS.\n\n\nCe micrologiciel est pour la version de matériel : %s + Vous êtes sur le point d\'installer l\'application suivante :\n\n\n%1$s Version %2$s par %3$s\n + N.D. + Initialisé + %1$s par %2$s + Scanner les appareils + Arrêter le scan + Démarrer le scan + Connecter un nouvel appareil + %1$s (%2$s) + Appairer l\'appareil + Utiliser l\'appairage Bluetooth d\'Android pour jumeler l\'appareil. + Appairer votre Mi Band + Jumelage avec %s… + Création d’un lien avec %1$s (%2$s) + Impossible se s’appairer avec %1$s (%2$s) + Création du lien en cours : %1$s (%2$s) + Déjà lié avec %1$s (%2$s), connexion… + Aucune adresse MAC fournie, ne peut être appairé. + Paramètres spécifiques à l\'appareil + Paramètres Mi Band / Amazfit + Homme + Femme + Autre + Gauche + Droite + Aucune donnée utilisateur valide fournie, utilisation de données fictives pour le moment. + Quand votre Mi Band vibre et clignote, appuyez dessus plusieurs fois d\'affilée. + Installer + Mettez votre appareil en mode visible. Les appareils déjà connectés ne seront pas visibles. Sur Android 6 ou supérieur, vous devez activer la localisation (ex. GPS). Désactivez Privacy Guard pour Gadgetbridge, il pourrait stopper et redémarrer votre téléphone. Si votre appareil n\'est pas visible après 2 minutes, réessayez après avoir redémarré votre téléphone. + Note : + Image de l\'appareil + Nom/Pseudo + Nombre de vibrations + Moniteur de sommeil + Écrire des fichiers journaux + Initialisation + Récupération des données d\'activité + De %1$s à %2$s + Port main gauche ou droite ? + Profil de vibration + Saccadé + Court + Moyen + Long + Goute d\'eau + Sonnette + Réveil + Vibration + Essayer + Notification Texto + Paramètres des vibrations + Notification générique + Notification des mails + Notification d\'appels entrants + Tchat + Navigation + Réseau social + Zones de vitesse + Total de minutes + Pas par minute + Trouver l\'appareil perdu + Annuler pour arrêter les vibrations. + Votre activité + Configurer les alarmes + Configurer les alarmes + Détails des alarmes + Dim + Lun + Mar + Mer + Jeu + Ven + Sam + Réveil intelligent + Une erreur s\'est produite lors du paramétrage des alarmes, veuillez réessayer. + Alarmes envoyées à l\'appareil. + Aucune donnée. Synchroniser l\'appareil ? + Sur le point de transférer %1$s de données à partir de %2$s + Objectif de pas par jour + Erreur lors de l’exécution de %1$s\' + Votre activité (ALPHA) + Impossible de se connecter : %1$s + Impossible de trouver un gestionnaire pour installer ce fichier. + Impossible d\'installer le ficher suivant : %1$s + Impossible d\'installer le micrologiciel spécifié : il ne correspond pas à la version du matériel de votre Pebble. + Veuillez patienter pendant la détermination de l\'état de l\'installation… + Niveau de batterie faible ! + %1$s batterie restante : %2$s%% + Dernière charge : %s \n + Nombre de charges : %s + Votre sommeil + Sommeil de la semaine + Sommeil aujourd\'hui, objectif : %1$s + Pas de la semaine + Votre activité et sommeil + Installation du micrologiciel… + Le fichier ne peut pas être installé, l\'appareil n\'est pas prêt. + %1$s: %2$s %3$s + Version compatible + Version non-testée ! + Connexion à l\'appareil : %1$s + Micrologiciel Pebble %1$s + Version du matériel correcte + Version du matériel incorrecte ! + %1$s (%2$s) + Problème avec le transfert du micrologiciel. Ne redémarrez pas votre Mi Band ! + Problème avec le transfert de métadonnées du micrologiciel + Installation complète du micrologiciel + Installation complète du micrologiciel, redémarrage de l\'appareil… + Échec lors de l\'installation du micrologiciel + Pas + Calories + Distance + Horloge + Fréquence cardiaque + Batterie + Activité en direct + Nombre de pas aujourd\'hui, objectif : %1$s + Ne pas confirmer le transfert de données d\'activités + Les données d\'activités ne seront pas effacées du bracelet si elles ne sont pas confirmées. Utile si GB est utilisé avec d\'autres applications. + Les données d\'activités seront conservées sur le Mi Band après la synchronisation. Utile si GB est utilisé avec d\'autres applications. + Utilisez le mode basse latence pour installer un micrologiciel + Cela peut aider sur les appareils où les installations de micrologiciel échouent. + Historique de pas + Pas/minute actuel + Nombre total de pas + Historique de pas/minute + Démarrez votre activité + Activité + Sommeil léger + Sommeil profond + Non porté + Non connecté. + Toutes alarmes désactivées + Conserver les activités sur l\'appareil + Micrologiciel non compatible + Ce micrologiciel n\'est pas compatible avec l\'appareil + Alarmes à réserver pour événements futurs + Utiliser le capteur cardiaque pour améliorer la détection du sommeil + La compensation de temps en heure (pour travailleurs en rotation, par exemple) + Format de la date + Heure + + Notification d\'objectif + Le bracelet vibrera lorsque l\'objectif de pas quotidien sera atteint + Éléments à afficher + Choisissez les éléments à afficher sur le bracelet + Allumer l\'écran lors d\'un mouvement + Tournez votre poignet pour changer d\'élément + Ne Pas Déranger + Le bracelet ne recevra pas de notifications si activé + Alertes d\'inactivité + Le bracelet vibrera si vous n\'êtes pas actif pendant un moment + Seuil d\'inactivité (en minutes) + Désactiver les alertes d\'inactivité pendant un intervalle de temps + Heure de début + Heure de fin + Sur le point de transférer des données depuis %1$s + En attente de reconnexion + À propos de vous + Année de naissance + Genre + Taille en cm + Poids en kg + Authentification + Authentification requise + ZzZz + Ajouter un widget + Temps de sommeil préféré en heures - Une alarme a été enregistré pour %1$02d:%2$02d - Révision matérielle : %1$s - Version du micrologiciel : %1$s - Erreur à la création de votre fichier log : %1$s - "Fréquence cardiaque : " - Installation du micrologiciel - Échec lors de l\'écriture du micrologiciel - Fréquence cardiaque - Fréquence cardiaque - Stockez les enregistrements brut dans la base de données - Si coché, les données \"brutes\" sont stockées pour une interprétation ultérieure, augmentant la taille de la base de données. - Gestion de base de données - Gestion de base de données - Les opérations sur la base de données ont utilisé le chemin suivant sur votre appareil. + Une alarme a été enregistré pour %1$02d:%2$02d + Révision matérielle : %1$s + Version du micrologiciel : %1$s + Erreur à la création de votre fichier log : %1$s + "Fréquence cardiaque : " + Installation du micrologiciel + Échec lors de l\'écriture du micrologiciel + Fréquence cardiaque + Fréquence cardiaque + Stockez les enregistrements brut dans la base de données + Si coché, les données \"brutes\" sont stockées pour une interprétation ultérieure, augmentant la taille de la base de données. + Gestion de base de données + Gestion de base de données + Les opérations sur la base de données ont utilisé le chemin suivant sur votre appareil. \nCe chemin est accessible par d\'autres applications Android ou par votre ordinateur. \nVotre base de données (ou celle que vous souhaitez importer) se trouvera ici : - Effacer l\'ancienne base de données - Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs. - Exporter vers : %1$s - Erreur d\'exportation de la base de données : %1$s - Erreur d\'exportation des préférences : %1$s - Importer des données ? - Voulez-vous vraiment effacer la base de données actuelle ? Toutes vos données (si vous en avez) seront perdues. - Importé. - Erreur lors de l\'importation de la base de données : %1$s - Erreur d\'importation des préférences : %1$s - Détruire les anciennes données ? - Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données d\'activité et vos informations issues de vos appareils seront perdues. - Les données ont été effacées. - Échec de la destruction de la base de données. - Voulez-vous détruire les anciennes activités de la base de données ? - Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données non importées seront perdues. - Les anciennes données d\'activité ont été effacées. - Échec de la destruction de l\'ancienne base de données. - Écraser - Annuler - Supprimer - - Vibration - - Jumelage avec une Pebble - Une fenêtre de jumelage va s’afficher sur votre téléphone. Si cela ne se produit pas, regardez dans vos notifications et acceptez-la. Acceptez ensuite la demande de jumelage sur votre Pebble. - Assurez vous que ce thème soit activé dans l\'application de notification de la météo pour recevoir les informations sur votre Pebble.\n\nAucune configuration n\'est requise.\n\nVous pouvez activer l\'application météo système de votre Pebble depuis la configuration de l\'application.\n\nLes watchfaces supportées afficheront la météo automatiquement. - Activer le jumelage Bluetooth - Désactivez ceci si vous avez des problèmes de connexion - Métrique - Impériale - 24H - AM/PM - Réveil - (%1$s) - Vous l\'avez trouvé ! - Mi2 : format de l\'heure - Vous devez installer la version %1$s avant d\'installer ce micrologiciel ! - Notifications textuelles - - Éteint - Éteint - Automatique (détection de sommeil) - Programmé (intervalle de temps) - Tentative de jumelage avec %1$s - Le lien avec %1$s a échoué instantanément. - Tentative de connexion à : %1$s - Activez le Bluetooth pour trouver des dispositifs. - Lié à %1$s. - Appairer avec %1$s ? - Sélectionnez Jumeler pour associer vos dispositifs. Si cela échoue, essayez à nouveau sans jumelage. - Appairer - Ne pas appairer -Vous êtes sur le point d\'installer le micrologiciel %s sur votre Amazfit Bip. + Effacer l\'ancienne base de données + Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs. + Exporter vers : %1$s + Erreur d\'exportation de la base de données : %1$s + Erreur d\'exportation des préférences : %1$s + Importer des données ? + Voulez-vous vraiment effacer la base de données actuelle ? Toutes vos données (si vous en avez) seront perdues. + Importé. + Erreur lors de l\'importation de la base de données : %1$s + Erreur d\'importation des préférences : %1$s + Détruire les anciennes données ? + Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données d\'activité et vos informations issues de vos appareils seront perdues. + Les données ont été effacées. + Échec de la destruction de la base de données. + Voulez-vous détruire les anciennes activités de la base de données ? + Voulez-vous vraiment détruire entièrement la base de données ? Toutes vos données non importées seront perdues. + Les anciennes données d\'activité ont été effacées. + Échec de la destruction de l\'ancienne base de données. + Écraser + Annuler + Supprimer + + Vibration + + Jumelage avec une Pebble + Une fenêtre de jumelage va s’afficher sur votre téléphone. Si cela ne se produit pas, regardez dans vos notifications et acceptez-la. Acceptez ensuite la demande de jumelage sur votre Pebble. + Assurez vous que ce thème soit activé dans l\'application de notification de la météo pour recevoir les informations sur votre Pebble.\n\nAucune configuration n\'est requise.\n\nVous pouvez activer l\'application météo système de votre Pebble depuis la configuration de l\'application.\n\nLes watchfaces supportées afficheront la météo automatiquement. + Activer le jumelage Bluetooth + Désactivez ceci si vous avez des problèmes de connexion + Métrique + Impériale + 24H + AM/PM + Réveil + (%1$s) + Vous l\'avez trouvé ! + Mi2 : format de l\'heure + Vous devez installer la version %1$s avant d\'installer ce micrologiciel ! + Notifications textuelles + + Éteint + Éteint + Automatique (détection de sommeil) + Programmé (intervalle de temps) + Tentative de jumelage avec %1$s + Le lien avec %1$s a échoué instantanément. + Tentative de connexion à : %1$s + Activez le Bluetooth pour trouver des dispositifs. + Lié à %1$s. + Appairer avec %1$s ? + Sélectionnez Jumeler pour associer vos dispositifs. Si cela échoue, essayez à nouveau sans jumelage. + Appairer + Ne pas appairer + Vous êtes sur le point d\'installer le micrologiciel %s sur votre Amazfit Bip. \n \nVeuillez installer le fichier .fw, puis le fichier .res, puis le fichier.gps.. Votre montre redémarrera après installation du .fw. \n @@ -432,38 +432,32 @@ Temps de sommeil préféré en heures Ouvrir sur le smartphone Android Silencieux Répondre -Activer tâche de fond JS + Activer tâche de fond JS Si activé, autorise l\'affichage de la météo, niveau de batterie, etc. - Activité Web View - - Connecter + Connexion Vous êtes sur le point d\'installer le micrologiciel %s sur votre Amazfit Cor. \n -\nVeuillez installer le fichier .fw, puis le fichier .res. Votre montre redémarrera après installation du .fw. +\nVeuillez installer le fichier .fw, puis le fichier .res. Votre montre redémarrera après installation du fichier .fw. \n -\nNote : vous ne devez pas installer .res si celui-ci est identique à celui installé précédemment. +\nNote : Il n\'est pas nécessaire d\'installer le fichier .res si celui-ci est identique à celui installé précédemment. \n -\nNON TESTÉ, PEUT BRIQUER L\'APPAREIL. CONTINUEZ À VOS RISQUES ! +\nCONTINUEZ À VOS RISQUES ! Micrologiciel Amazfit Cor %1$s Permettre le balayage gauche/droite dans les graphiques d\'activité - Paramètres Amazfit Bip Automatique Chinois simplifié Chinois traditionnel Anglais - Mesure du pouls toute la journée Une fois par minute Toutes les 5 minutes Toutes les 10 minutes Toutes les 30 minutes Une fois par heure - Météo Emplacement météo (CM/LOS) - Micrologiciel Données non valides Police @@ -472,7 +466,6 @@ Temps de sommeil préféré en heures Correction d\'erreurs GPS Ressources Cadran - Appareil inconnu Appareil de test Pebble @@ -487,19 +480,16 @@ Temps de sommeil préféré en heures Exrizu K8 No.1 F1 Teclast H30 -Exporter automatiquement + Exporter automatiquement Exportation automatique activée Emplacement de l\'exportation Intervalle d\'exportation Exporter toutes les %d heures - L\'exportation de la base de données a échoué ! Veuillez vérifier vos paramètres. Choisissez l\'emplacement d\'exportation -Espagnol - + Espagnol Notifications de Gadgetbridge -XWatch - + XWatch Actif Non mesurée Activité @@ -512,13 +502,11 @@ Temps de sommeil préféré en heures Activités Vélo Tapis de course - Suivi de vos activités Dispositif non utilisé Tout sélectionner Partager Réinitialiser la date de récupération - Etat Activité Météo @@ -527,55 +515,46 @@ Temps de sommeil préféré en heures Boussole Réglages Alipay -Bloquer toutes les notifications - Débloquer toutes les notifications - - - "Vous allez installer le micrologiciel %s dans votre Mi Band 3. -\n -\nAssurez-vous d\'installer d\'abord le fichier .fw, et ensuite le fichiers .res. Votre bracelet redémarrera après l\'installation du fichiers .fw. -\n -\nRemarque: Vous n\'avez pas besoin d\'installer le fichier .res si c\'est exactement le même que celui déja installé. -\n -\nNON TESTÉ, PEUT BRIQUER VOTRE APPAREIL, À VOS RISQUES ET PÉRILS !" + Mettre toutes les notifications en liste noire + Mettre toutes les notifications en liste blanche + "Vous allez installer le micrologiciel %s dans votre Mi Band 3. +\n +\nAssurez-vous d\'installer d\'abord le fichier .fw, et ensuite le fichiers .res. Votre bracelet redémarrera après l\'installation du fichiers .fw. +\n +\nRemarque: Vous n\'avez pas besoin d\'installer le fichier .res si c\'est exactement le même que celui déjà installé. +\n +\nÀ VOS RISQUES ET PÉRILS !" client GATT uniquement C\'est uniquement pour les Pebble 2 et expérimental, à essayer si vous avez des problèmes de connectivité réglages ID115 Orientation de l\'écran - Récupérer automatiquement les données d\'activité Récupération au déverrouillage de l\'écran. Fonctionne uniquement si un mécanisme de verrouillage est configuré ! Temps minimum entre synchronisations Synchronisation toutes les %d minutes - Réglages Mi Band 2 Réglages Mi Band 3 Réglages Amazfit Cors Horizontal Vertical Russe - Mi Band 3 Q8 MyKronoz ZeTime ID115 - Alipay (Raccourci) Météo (Raccourci) Notifications Musique Suite -Changer la couleur de la LED + Changer la couleur de la LED Changer la fréquence FM Calibrer l\'appareil - - Temps minimum entre deux notifications De droite à gauche Cocher ceci si votre appareil n\'est pas compatible avec les langues \"droite-à-gauche\" Longueur max. d\'une ligne en mode Droite-à-Gauche Lorsque votre bracelet vibre, secouez-le ou pressez son bouton. - %1$s niveau de batterie bas %1$s niveau de batterie base : %2$s Manque de sommeil: %1$s @@ -589,7 +568,6 @@ Temps de sommeil préféré en heures 5 minutes 10 minutes 30 minutes - Manque d\'activité: %1$d Trop d\'activité: %1$d Mesure cardiaque actuelle / maximum: %1$d / %2$d @@ -598,20 +576,16 @@ Temps de sommeil préféré en heures Glisser le doigt vers le haut pour débloquer l\'écran du bracelet Mode nuit Diminuer la luminosité de l\'écran du bracelet automatiquement lorsqu\'il fait sombre - Allemand Italien Français Polonais Koréen Japonais - Paramètres des graphiques Fréquence cardiaque maximum Fréquence cardiaque minimum - Ok - Au coucher du soleil Watch 9 Minutes: @@ -626,17 +600,14 @@ Temps de sommeil préféré en heures Veuillez garder à l\'esprit que les fichiers logs de Gadgetbridge peuvent contenir des tas d\'informations personnelles, incluant entre autre des données relatives à la santé, des identifiants uniques (telles que des adresses MAC), des préférences musicales, etc. Pensez à modifier le fichier et retirer ces informations personnelles avant toute publication sur un rapport de bug public. Attention! Pas de données - Couleur de la LED - Fréquence FM Fréquence non-valide Veuillez introduire une fréquence entre 87.5 et 108.0 Paramètres de langue et de région -Bokmål norvégien + Bokmål norvégien Roidmi Roidmi 3 - Arabe contextuel A cocher pour activer le support \"Arabe contextuel\" Confirmer la réinitialisation usine \? @@ -646,7 +617,7 @@ Temps de sommeil préféré en heures Casio GB-6900 Filtre de notifications L\'application ne doit pas être en liste noire pour pouvoir être configurée - Saisissez les mot désirés, chacun sur une ligne + Saisissez les mots désirés, chacun sur une ligne Filtre des notifications sauvegardé Ne pas filtrer Montrer lorsque contient les mots @@ -658,4 +629,82 @@ Temps de sommeil préféré en heures Mode Configuration Sauvegarder la configuration Non connecté, l\'alarme n\'est pas définie + Notification de déconnexion + Paramètres ZeTime + Paramètres Fréquence Cardiaque + Durée d\'écran allumé en secondes + Alarme Fréquence Cardiaque + La montre vous alertera quand votre fréquence cardiaque dépasse les limites. + Activer l\'alarme de fréquence cardiaque + Fréquence cardiaque maximale + Fréquence cardiaque minimale + Mode analogique + Aiguilles seulement + Aiguilles et Pas + Suivi des Activités + "Activer le suivi des activités : comptera vos pas, etc." + Tourner le poignet pour activer ou désactiver l\'écran. + Type de calories + Seulement les calories brûlées activement + Calories brûlées activement et au repos + Format de l\'heure + 24h + 12h + Format de la date + aaaa/mm/jj + jj/mm/aaaa + mm/jj/aaaa + Répétitions + Lundi + Mardi + Mercredi + Jeudi + Vendredi + Samedi + Dimanche + Type de signal pour l\'alarme + Silencieux + Vibration continue + Bip continu + Vibration et bip continus + Vibration unique + Vibration double + Bip unique + Bip double + Vibration et bip uniques + Notification appel manqué + Notification du calendrier + Notification d\'inactivité + Alerte batterie faible + Alerte Anti-Perte + Toutes les 15 min + Toutes les 45 min + Objectif Quotidien : Calories Brulées + Objectif Quotidien : Distance en mètres + Objectif Quotidien : Temps d\'activité en minutes + Mi Scale 2 + Activer les applications d\'appel VoIP + " " + Clé Auteur + Changez la clé auteur à une clé générale sur tous vos appareils Android sur lesquels vous souhaitez vous connecter. La précédente clé par défaut était 0123456789@ABCDE + BFH-16 + Vous êtes sur le point d\'installer le micrologiciel %s sur votre Amazfit Cor 2. +\n +\nVeuillez installer le fichier .fw, puis le fichier .res. Votre montre redémarrera après installation du .fw. +\n +\nNote : Il n\'est pas nécessaire d\'installer le fichier .res si celui-ci est identique à celui installé précédemment. +\n +\nCONTINUEZ À VOS RISQUES ! +\n +\nNON TESTÉ, IL PEUT ÊTRE NÉCESSAIRE DE FLASH UN micrologiciel BEATS_W SI LE NOM DE L\'APPAREIL EST \"Amazfit Band 2\" + Néerlandais + Turc + Ukrainien + Arabe + Indonésien + Thaïlandais + Vietnamien + Portugais + Amazfit Cor 2 + Allonge ou raccourcis les lignes dans les textes écrits de droite à gauche \ 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 2f8ee0724..2bf609b74 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -37,10 +37,12 @@ התקנת קושחה/יישומון - הקושחה המועמדת להתקנה היא %s במקום זו המותקנת על ה־Mi Band שלך נכון לעכשיו. + פעולה זו תוביל להתקנת %s. הקושחות המועמדות להתקנה הנן %1$s ו־%2$s במקום אלו המותקנת על ה־Mi Band שלך נכון לעכשיו. קושחה זו נבדקה וידוע כי היא נתמכת ב־Gadgetbridge. - קושחה זו לא נבדקה ויתכן כי אינה נתמכת ב־Gadgetbridge.\n\nלא מומלץ להתקין אותה על ה־Mi Band שלך! + קושחה זו לא נבדקה ויתכן כי אינה נתמכת ב־Gadgetbridge. +\n +\nלא מומלץ להתקין אותה! אם בכל זאת בחרת להמשיך והדברים ממשיכים לעבוד כרגיל, נא לספר למפתחים של Gadgetbridge להוסיף לרשימת ההיתר את גרסת הקושחה %s. הגדרות @@ -434,7 +436,7 @@ כשאפשרות זו פעילה, יתאפשר למסיכות השעון להציג מזג אוויר, סוללה וכו׳. פעולה זו תתקין את הקושחה %s במקום הנוכחית שעל ה־Amazfit Cor שלך. \n -\nנא לוודא את התקנת קובץ ה־‎.fw, ולאחר מכן את קובץ ה־‎.res. השעון שלך יופעל מחדש לאחר התקנת קובץ ה־‎.fw +\nנא לוודא את התקנת קובץ ה־‎.fw, ולאחר מכן את קובץ ה־‎.res. הצמיד שלך יופעל מחדש לאחר התקנת קובץ ה־‎.fw \n \nלתשומת לבך: אין צורך להתקין את קובץ ה־‎.res אם הוא זהה לקובץ שהתקנת בעבר. \n @@ -518,11 +520,11 @@ Q8 פעולה זו תתקין את הקושחה %s במקום זו שכרגע מותקנת ב־Mi Band 3 שלך. \n -\nנא לוודא את התקנת קושחת ה־‎.fw בהתחלה, לאחר מכן את קובץ ה־‎.res השעון שלך יופעל מחדש לאחר התקנת קובץ ה־fw. +\nנא לוודא את התקנת קובץ ה־‎.fw בהתחלה, לאחר מכן את קובץ ה־‎.res הרצועה שלך תופעל מחדש לאחר התקנת קובץ ה־fw. \n \nלתשומת לבך: אין צורך בהתקנת קובץ ה־‎.res אם הוא כבר זהה לזה שהתקנת בעבר. \n -\nהתהליך לא נבדק, ההתקן שלך עשוי להפסיק לפעול, המשך התהליך הוא על אחריותך! +\nהמשך התהליך הוא על אחריותך! Mi Band 3 הוספת כולם לרשימה השחורה של ההתראות הוספת כולם לרשימת ההיתר של ההתראות @@ -681,4 +683,38 @@ יעד יומי: מרחק במטרים יעד יומי: זמן פעילות בדקות Mi Scale 2 + הפעלת שיחות VoIP מהיישומון + הגדרות ייחודיות למכשיר + מפתח אימות + ניתן להחליף את מפתח האימות למפתח משותף בכל מכשירי ה־Android שלך מהם ברצונך להתחבר. מפתח בררת המחדל הקודם לכל המכשירים הוא 0123456789@ABCDE + BFH-16 + פעולה זו תתקין את הקושחה %s במקום זו שכרגע מותקנת ב־Amazfit Cor 2 שלך. +\n +\nנא לוודא את התקנת קושחת ה־‎.fw בהתחלה, לאחר מכן את קובץ ה־‎.res. הצמיד שלך יופעל מחדש לאחר התקנת קובץ ה־fw. +\n +\nלתשומת לבך: אין צורך בהתקנת קובצי ה־‎.res אם הוא כבר זהים לזה שהתקנת בעבר. +\n +\nהמשך התהליך הוא על אחריותך! +\n +\nתהליך זה לא נבדק, כנראה שיהיה עליך להתקין קושחה מסוג BEATS_W אם שם המכשיר שלך הוא „Amazfit Band 2”! + הולנדית + טורקית + אוקראינית + ערבית + אינדונזית + תאי + וייטנאמית + פורטוגלית + Amazfit Cor 2 + Mi Band 4 + פעולה זו תתקין את הקושחה %s ב־Mi Band 4 שלך. +\n +\nנא לוודא את התקנת קובץ ה־‎.fw בהתחלה, לאחר מכן את קובץ ה־‎.res השעון שלך יופעל מחדש לאחר התקנת קובץ ה־fw. +\n +\nלתשומת לבך: אין צורך בהתקנת קובץ ה־‎.res אם הוא כבר זהה לזה שהתקנת בעבר. +\n +\nהמשך התהליך הוא על אחריותך! + התראת דופק במהלך פעילות ספורטיבית + גבול תחתון + גבול עליון \ No newline at end of file diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 11f247b72..6ff2148a9 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,402 +1,461 @@ - + - Gadgetbridge - Gadgetbridge - Beállítások - Hibakeresés - Kilépés - Szinkronizálás - Alvási adatok (ALFA) - Elveszett eszköz keresése... - Képernyőkép készítése - Szétcsatlakoztatás - Eszköz törlése - %1$s törlése - Az eszköz és az összes hozzátartozó adat törlésre kerül! - Navigáció megnyitása - Navigáció bezárása - Nyomd hosszan a szétkapcsoláshoz - Szétkapcsolódás - Kapcsolódás - Képernyőkép készítése az eszközről - Hibakeresés - - Alkalmazáskezelő - Gyorsítótárazott alkalmazások - Telepített alkalmazások - Telepített számlapok - Törlés - Törlés és eltávolítás a gyorsítótárból - Újratelepítés - Keresés a Pebble alkalmazás-áruházban - Aktiválás - Deaktiválás - Pulzusmérő aktiválása - Pulzusmérő deaktiválása - Rendszer időjárás alkalmazás aktiválása - Rendszer időjárás alkalmazás deaktiválása - Időjárás értesítő alkalmazás telepítése - Beállítás - A tetejére mozgatás - - Értesítés tiltólista - - FW/Alkalmazás telepítő - A %s firmware-változatot tervezed telepíteni a Mi Band-edre a mostani helyett. - A %1$s és %2$s firmware-változatokat tervezed telepíteni a Mi Band-edre a mostani helyett. - Ez a firmware tesztelt és ismerten kompatibilis a Gadgetbridge-dzsel. - Ez a firmware nincs tesztelve, lehetséges, hogy nem kompatibilis a Gadgetbridge-dzsel.\n\nNEM javasoljuk, hogy feltelepítsd a Mi Band-edre! - Ha ennek ellenére is telepíteni szeretnéd, és a dolgok jól működnek, utána kérlek jelezd a Gadgetbridge fejlesztőinek, hogy fehérlistára tehessék a %s firmware-változatot. - - Beállítások - Általános beállítások - Kapcsolódjon az eszközhöz a Bluetooth bekapcsolásakor - Automatikus indulás - Újracsatlakozás automatikusan - Preferált zenelejátszó - Alapértelmezett - Dátum és idő - Idő szinkronizálás - Idő szinkronizálása csatlakozásnál és Android idő- vagy időzónaváltoztatásnál. - Téma - Világos - Sötét - Nyelv - Gadgetbridge értesítések elrejtése - Az ikon és az értesítés mutatása a záróképernyőn - Az ikon és az értesítés elrejtése a záróképernyőn - Értesítések - Ismétlések - Telefonhívások - SMS - Pebble üzenetek - Támogatás az alkalmazásoknak, amik értesítéseket küldenek a Pebble-nek a PebbleKit-en keresztül. - Általános értesítési támogatás - … és amikor a kijelző be van kapcsolva - Ne zavarj mód - Értesítések tiltása a Ne zavarj üzemmód használatakor. - Transzliteráció - Engedélyezd ezt, ha a nyelvedet nem támogatja a betűtípus - mindig - amikor a kijelző ki van kapcsolva - soha - Adatvédelem - Hívás Adatvédelmi Mód - Név és szám mutatása - Név rejtése, de a szám mutatása - Szám rejtése, de a név mutatása - Név és szám rejtése - Tiltott alkalmazások - Előre beállított üzenetek - Előre megírt válaszok - Közös előtag - Hívás elutasítás - Frissítés a Pebble-n - Fejlesztői beállítások - Mi Band cím - Pebble beállítások - Aktivitásmérők - Preferált Aktivitásmérő - Pebble Health szinkronizálása - Misfit szinkronizálás - Morpheuz szinkronizálás - Kimenő hívások támogatása - Ennek a letiltásával a kimenő hívás rezgését is letiltja a Pebble 2/LE eszközön - Harmadik féltől származó Android alkalmazások hozzáférésének engedélyezése - Kísérleti támogatás engedélyezése az Android alkalmazások számára a PebbleKit használatára - Pebble Idővonal - Napkelte és napnyugta - A napkelte és napnyugta idejének küldése a Pebble idővonalra a hely és idő alapján - Naptár szinkronizálása - Naptárbejegyzések küldése az idővonalra - Automatikusan törölje az eltávolított értesítéseket - Az értesítések automatikus eltávolítsa, ha az Androidos eszközön eltávolítottad - Adatvédelmi mód - Normál értesítések - Az értesítés eltolása képernyőn - Csak az értesítési ikon mutatása - Pozició - Pozició meghatározása - Szélesség - Hosszúság - Pozició frissentartása - Aktuális pozició meghatározásának megpróbálása futásidőben, hiba esetén a letárolt pozició használata - Kérlek, engedélyezd a hálózati helymeghatározást. - Hely meghatározva - Értesítési protokoll erőltetése - Ez az opció a legfrissebb értesítési protokollt erőlteti a firmware verziótól függően. CSAK AKKOR ENGEDÉLYEZD, HA TUDOD MIT CSINÁLSZ! - Nem tesztelt funkciók engedélyezése - Nem tesztelt funkciók engedélyezése. CSAK AKKOR ENGEDÉLYEZD, HA TUDOD MIT CSINÁLSZ! - Mindig preferálja a BLE-t - A kísérleti LE támogatás használata minden Pebble-hez a klasszikus BT helyett, párosítani kell hozzá egy \"Pebble LE\"-t, miután a nem-LE-t párosítva lett egyszer. - Pebble 2/LE GATT MTU limit - Ha a Pebble 2/Pebble LE nem működik elvártan, próbáld beállítani az MTU limitet (az érvényes tartomány: 20-512) - Óraalkalmazás-naplózás engedélyezése - Az óra-alkalmazások naplóit a Gadgetbridge is naplózni fogja (újracsatlakozást igényel) - Korai ACK PebbleKit - Minden esetben azonnal felismeri az üzeneteket, amelyeket külső harmadik alkalmazásoknak küldenek. - Újracsatlakozási kísérletek száma - Egységek - Időformátum - Kijelző bekapcsolvatartásának ideje - Egésznapos pulzusmérés - HPlus/Makibes Beállítások - nincs csatlakozva - csatlakozás - csatlakoztatva - ismeretlen állapot - (ismeretlen) - Teszt - Teszt értesítés - Ez egy tesztértesítés a Gadgetbridge-től - A Bluetooth nem támogatott. - A Bluetooth ki van kapcsolva. - Koppints az eszközre az alkalmazáskezelőhöz - Koppints az eszközre az Aktivitáshoz - Koppints az eszközre a rezgetéshez. - Koppints az eszközre a csatlakozáshoz - Nem lehet csatlakozni. Rossz Bluetooth cím? - A Gadgetbridge fut. - %1$d/%2$d bináris telepítése - sikertelen telepítés! - sikeres telepítés - FIRMWARE-T PRÓBÁLSZ TELEPÍTENI, CSAK SAJÁT FELELŐSSÉGRE TEDD!.\n\n\n Ez a firmware ehhez a hardware-verzióhoz tartozik: %s - A következő alkalmazást fogod telepíteni:\n\n\n%1$s Verzió %2$s , készítette: %3$s\n - N/A - inicializált - %1$s , készítette: %2$s - Eszköz keresés - Keresés leállítása - Keresés kezdése - Új eszköz csatlakoztatása - %1$s (%2$s) - Eszköz párosítása - Használja az Android Bluetooth párosítás ablakot eszköz párosításához. - Párosítsd a Mi Band-ed - %s párosítása... - Összeköttetés létesítése %1$s (%2$s) eszközzel - Sikertelen párosítás %1$s (%2$s) eszközzel - Összeköttetés folyamatban %1$s (%2$s) eszközzel - Meglévő párosítás %1$s (%2$s) eszközzel, kapcsolódás... - Nincs találat a MAC címre, nem lehet párosítani. - Eszközspecifikus beállítások - MI Band beállítások - férfi - - egyéb - bal - jobb - Nem helyes felhasználói adatok vannak megadva, alapértelmezett adatokat fogok használni. - Amikor vibrálni kezd a Mi Band-ed, érintsd meg párszor egymás után. - Telepítés - Tedd láthatóvá a készüléked. A jelenleg csatlakoztatott készülékek nem fognak láthatóként megjelenni. Aktiváld a helymeghatározást (Pl: GPS) az Android 6 készülékeden. Kapcsold ki a Privacy Guard-ban a Gadgetbridge-et (Ha van Privacy Guard beállításod), különben az alkalmazás kifagyhat, illetve újraindíthatja a készükéked. Ha nem található az eszköz pár percen bellül, próbáld újraindítani a telefonod. - Megjegyzés: - Eszköz kép - Név/Alias - Rezgésszám - Alvástörténet - Naplófájlok írása - inicializálás - Aktivitási adatok lekérdezése. - %1$s és %2$s között - Melyik kezeden hordod? - Rezgés profil - Szaggatott - Rövid - Közepes - Hosszú - Vízcsepp - Csörgés - Ébresztőóra - Rezgés - Próba - SMS értesítés - Rezgésbeállítások - Általános értesítések - E-Mail értesítés - Bejővő hívás értesítés - Csevegés - Navigáció - Közösségi háló - Sebesség Zónák - Összes perc - Percenkénti lépések - Elveszett eszköz keresése - A vibrálás leállításához: Mégse. - Aktivitásod - Ébresztések beállítása - Ébresztők beállítása - Ébresztés részletek - Vas - Hét - Ked - Sze - Csü - Pén - Szo - Okos ébresztés - Hiba történt az ébresztések beállításakor, kérlek próbáld újra! - Ébresztések beállítva! - Nincs adat. Szinkronizáljunk? - Adatok fogadása: %1$s %2$s -tól - Napi céllépésszám - Hiba a futtatáskor: \'%1$s\' - Aktivitásod (ALFA) - Nem lehet kapcsolódni: %1$s - A telepítéshez szükséges segédfájl nem található. - Nem sikerült telepíteni a fájlt: %1$s - Sikertelen telepítés: a firmware nem kompatibilis a Pebble hardver verzióddal. - Kérlek várj a telepítési státusz megállapításáig... - Alacsony akkumulátorszint - %1$s fennmaradó akkumulátor töltés: %2$s%% - Utoljára töltve: %s \n - Töltések száma: %s - Az alvásod - Heti alvás - Mai alvás, cél: %1$s - Lépések a héten - Aktivitásod és alvásod - Firmware frissítése… - A fájlt nem lehet telepíteni, a készülék nincs készen. - %1$s: %2$s %3$s - Kompatibilis verzió - Nem tesztelt verzió! - Csatlakozás az eszközhöz: %1$s - Pebble Firmware %1$s - Helyes hardververzió - Hardververzió eltérés! - %1$s (%2$s) - Probléma volt a firmware küldésekor. NE INDÍTSD ÚJRA a Mi Band-ed! - Probléma volt a Firmware metaadatainak küldésekor - A Firmware telepítés kész - A Firmware telepítés kész, az eszköz újraindítása… - Firmware írási probléma - Lépések - Kalória - Távolság - Óra - Pulzus - Akkumulátor - Aktuális aktivitás - Lépések ma, cél: %1$s - Ne erősítse meg az aktivitásadatok átvitelét - Ha az aktivitásadatok átvitele nincs megerősítve a band felé, az nem kerülnek törlésre. Hasznos, ha a GB-t más alkalmzásokkal együtt használod. - Az adatokat meghagyja a Mi Band-en szinkronizálás után is. Hasznos, ha a GadgetBridge-et együtt használod más alkalmazásokkal. - Használja a low-latency módot a firmware frissítésnél - Ez segíthet, ha a firmware frissítés sikertelen. - Lépésnapló - Jelenlegi lépés/perc - Összes lépés - Lépés/perc napló - Aktivitás kezdése - Aktivitás - Könnyű alvás - Mély alvás - Nem viselt - Nincs csatlakoztatva. - Mindegyik ébresztés tiltva van - Aktivitásadatok eszközön hagyása - Nem kompatibilis firmware - Ez a firmware nem kompatibilis az eszközöddel. - Naptári események számára fentartott riasztások száma - Pulzus szenzor használata az alvás érzékelés javításához - Eszköz időeltolása órákban (Hogy az éjszakai műszakban dolgozók alvását is érzékelje.) - Mi2: dátumformátum - Idő - - Cél értesítés - A karkötő vibrálni fog amikor elérted a napi lépés célod - Megjelenítendő elemek - Válaszd ki milyen elemek jelenjenek meg a karkötő kijelzőjén - Kijelző aktiválása felemeléskor - Forgasd el a karkötőt információ váltáshoz - Ne zavarj mód - A karkötő nem fog értesítéseket jelezni amíg ez aktív - Inaktivitás figyelmeztetés - A karkötő rezegni fog amikor inaktív vagy már egy ideje - Inaktivitás határ (percben) - Inaktivitás figyelmeztetések tiltása adott időre - Kezdete - Vége - Adatok átvitele %1$s óta - várakozás az újracsatlakozásra - Rólad - Születési év - Nem - Magasság cm-ben - Testsúly kg-ban - hitelesítés - hitelesítés szükséges - Zzz - Widget hozzáadása - Preferált alvási idő órában - Ébresztő beállítva: %1$02d:%2$02d - HW: %1$s - FW: %1$s - Hiba a könyvtár létrehozásakor a naplófájlok számára: %1$s - HR: - Firmware frissítés folyamatban - Nem küldött Firmware - Pulzus - Pulzus - Nyers adatok tárolása az adatbázisban - Ha bejelölöd, az adatok eredeti formában lesznek tárolva későbbi értelmezéshez. Megj.: az adatbázis ebben az esetben nagyobb lesz! - Adatbáziskezelés - Adatbáziskezelés - Az adatbázisművelet ezt a helyet fogja használni. Ez a hely elérhető másik Android-alkalmazások és a számítógép számára. Itt keresd az exportált adatbázist is, illetve ide rakd az importálni kívánt adatbázist is. - Hagyaték adatbázis törlése - Nem elérhető az elérési út. Kérlek vedd fel a kapcsolatot a fejlesztőkkel. - %1$s exportálva - Hiba az exportálás során: %1$s - Hiba a beállítások exportálása során: %1$s - Adatok importálása? - Biztosan felül akarod írni a jelenlegi adatbázist? Az összes jelenlegi aktivitás adatod (ha van) elfog veszni. - Sikeres importálás. - Hiba az importálás során: %1$s - Hiba a beállítások importálása során: %1$s - Törlöd az aktivitásadatokat? - Tényleg törölni akarod az egész adatbázist? Minden aktivitásadatod és rólad tárolt információ elfog veszni. - Az adatok sikeresen törölve lettek. - Adatbázistörlési hiba. - Törlöd a régi aktivitási adatokat? - Biztosan törlöd a régi aktivitásadatokat? A nem importált aktivitási adatok elfognak veszni! - Sikeresen törlésre kerültek a régi aktivitásadatok. - Nem sikerült a régi aktivitás-adatbázis törlése. - Felülírás - Mégse - Törlés - - Rezgés - - Pebble párosítása - Egy párosítási párbeszédablaknak kéne megjelennie az Android eszközödön. Ha nem látod, nézd meg az értesítési sávban és fogadd el a párosítási kérelmet. Utána fogadd ela kérelmet a Pebble-n. - Bizonyosodj meg róla, hogy a skin támogatja-e az Időjárás értesítések megjelenítését. Nincs szükség konfigurációra. A Pebble app management-ben tudod engedélyezni a rendszer időjárás alkalmazását. Azok a skinek amik támogatják az időjárás értesítésket, automatikusan megfogják jeleníteni azt. - Bluetooth párosítás engedélyezése - Kapcsold ki ezt, ha gondod van a csatlakozással - Metrikus - Imperiál - 24H - AM/PM - Ébresztőóra - (%1$s) - Megtaláltad! - Mi2: Időformátum - Ezt a verziót kell telepítened %1$s firmware telepítés előtt! - Szöveges értesítés - = 1.0.1.28 és Mili_pro.ft* telepítve.]]> - ki - Ki - Automatikus (alvás érzékelés) - Időzített (idő intervallum) - Csatlakozási kísérlet az eszközhöz: %1$s - A csatlakozás azonnal megszkadt: %1$s - Csatlakozási kísérlet: %1$s - Engedélyezd a Bluetooth-ot az eszközök felderítéséhez. - Sikeres párosítás: %1$s. - Párosítsam a(z) %1$s? - Válaszd ki a Párosítást az eszköz párosításához. Ha ez nem sikerül, próbáld meg újra párosítás nélkül. - Párosítás - Nem párosított -Támogatás - Csatlakoztatás - + Gadgetbridge + Gadgetbridge + Beállítások + Hibakeresés + Kilépés + Szinkronizálás + Alvási adatok (ALFA) + Elveszett eszköz keresése + Képernyőkép készítése + Kapcsolat megszüntetése + Eszköz törlése + %1$s törlése + Az eszköz és az összes hozzátartozó adat törlésre kerül! + Navigáció megnyitása + Navigáció bezárása + Nyomd hosszan a szétkapcsoláshoz + Szétkapcsolódás + Kapcsolódás… + Képernyőkép készítése az eszközről + Hibakeresés + + Alkalmazáskezelő + Gyorsítótárazott alkalmazások + Telepített alkalmazások + Telepített számlapok + Törlés + Törlés és eltávolítás a gyorsítótárból + Újratelepítés + Keresés a Pebble alkalmazás-áruházban + Aktiválás + Deaktiválás + Pulzusmérő aktiválása + Pulzusmérő deaktiválása + Rendszer időjárás alkalmazás aktiválása + Rendszer időjárás alkalmazás deaktiválása + Időjárás értesítő alkalmazás telepítése + Beállítás + A tetejére mozgatás + + Értesítés tiltólista + + FW/Alkalmazás telepítő + A %s firmware-változatot tervezed telepíteni. + A(z) %1$s és %2$s firmware-változatokat tervezed telepíteni a Mi Band-edre a mostani helyett. + Ez a firmware tesztelt és ismerten kompatibilis a Gadgetbridge-dzsel. + Ez a firmware nincs tesztelve, lehetséges, hogy nem kompatibilis a Gadgetbridge alkalmazással. +\n +\nNEM javasoljuk, ezen firmware telepítését! + Ha ennek ellenére is telepíteni szeretnéd, és a dolgok jól működnek, utána kérlek jelezd a Gadgetbridge fejlesztőinek, hogy fehérlistára tehessék a %s firmware-változatot. + + Beállítások + Általános beállítások + Kapcsolódjon az eszközhöz a Bluetooth bekapcsolásakor + Automatikus indulás + Újracsatlakozás automatikusan + Preferált zenelejátszó + Alapértelmezett + Dátum és idő + Idő szinkronizálás + Idő szinkronizálása csatlakozásnál és Android idő- vagy időzónaváltoztatásnál. + Téma + Világos + Sötét + Nyelv + Gadgetbridge értesítések elrejtése + Az ikon és az értesítés mutatása a záróképernyőn + Az ikon és az értesítés elrejtése a záróképernyőn + Értesítések + Ismétlések + Telefonhívások + SMS + Pebble üzenetek + Támogatás az alkalmazásoknak, amik értesítéseket küldenek a Pebble-nek a PebbleKit-en keresztül. + Általános értesítési támogatás + … és amikor a kijelző be van kapcsolva + Ne zavarj mód + A nem kívánt értesítések ebben a módban ki vannak kapcsolva + Transzliteráció + Engedélyezd ezt, ha a nyelvedet nem támogatja a betűtípus + Mindig + Amikor a kijelző ki van kapcsolva + Soha + Adatvédelem + Hívás Adatvédelmi Mód + Név és szám mutatása + Név rejtése, de a szám mutatása + Szám rejtése, de a név mutatása + Név és szám rejtése + Tiltott alkalmazások + Előre beállított üzenetek + Előre megírt válaszok + Közös előtag + Hívás elutasítás + Frissítés a Pebble-n + Fejlesztői beállítások + Mi Band cím + Pebble beállítások + Aktivitásmérők + Preferált Aktivitásmérő + Pebble Health szinkronizálása + Misfit szinkronizálás + Morpheuz szinkronizálás + Kimenő hívások támogatása + Ennek a letiltásával a kimenő hívás rezgését is letiltja a Pebble 2/LE eszközön + Harmadik féltől származó Android alkalmazások hozzáférésének engedélyezése + Kísérleti támogatás engedélyezése a PebbleKit-et használó Android alkalmazások számára + Pebble Idővonal + Napkelte és napnyugta + A napkelte és napnyugta idejének küldése a Pebble idővonalra a hely és idő alapján + Naptár szinkronizálása + Naptárbejegyzések küldése az idővonalra + Automatikusan törölje az eltávolított értesítéseket + Az értesítések automatikus eltávolítsa, ha az Androidos eszközön eltávolítottad + Adatvédelmi mód + Normál értesítések + Az értesítés eltolása képernyőn + Csak az értesítési ikon mutatása + Pozició + Pozició meghatározása + Szélesség + Hosszúság + Pozició frissentartása + Aktuális pozició meghatározásának megpróbálása futásidőben, hiba esetén a letárolt pozició használata + Kérlek, engedélyezd a hálózati helymeghatározást + Hely meghatározva + Értesítési protokoll erőltetése + Ez az opció a legfrissebb értesítési protokollt erőlteti a firmware verziótól függően. CSAK AKKOR ENGEDÉLYEZD, HA TUDOD MIT CSINÁLSZ! + Nem tesztelt funkciók engedélyezése + Nem tesztelt funkciók engedélyezése. CSAK AKKOR ENGEDÉLYEZD, HA TUDOD MIT CSINÁLSZ! + Mindig preferálja a BLE-t + A kísérleti LE támogatás használata minden Pebble-hez a klasszikus BT helyett, párosítani kell hozzá egy \"Pebble LE\"-t, miután a nem-LE-t párosítva lett egyszer. + Pebble 2/LE GATT MTU limit + Ha a Pebble 2/Pebble LE nem működik elvártan, próbáld beállítani az MTU limitet (az érvényes tartomány: 20-512) + Óraalkalmazás-naplózás engedélyezése + Az óra-alkalmazások naplóit a Gadgetbridge is naplózni fogja (újracsatlakozást igényel) + Korai ACK PebbleKit + Minden esetben azonnal felismeri az üzeneteket, amelyeket külső harmadik alkalmazásoknak küldenek + Újracsatlakozási kísérletek száma + Egységek + Időformátum + Kijelző bekapcsolvatartásának ideje + Egésznapos pulzusmérés + HPlus/Makibes Beállítások + Nincs csatlakozva + Csatlakozás + Csatlakozva + Ismeretlen állapot + (ismeretlen) + Teszt + Teszt értesítés + Ez egy teszt értesítés a Gadgetbridge-től + A Bluetooth nem támogatott. + A Bluetooth ki van kapcsolva. + Koppints az eszközre az alkalmazáskezelőhöz + Koppints az eszközre az Aktivitáshoz + Koppints az eszközre a rezgetéshez. + Koppints az eszközre a csatlakozáshoz + Nem lehet csatlakozni. Rossz Bluetooth cím? + A Gadgetbridge fut + %1$d/%2$d bináris telepítése + Sikertelen telepítés + Telepítve + FIRMWARE-T PRÓBÁLSZ TELEPÍTENI, CSAK SAJÁT FELELŐSSÉGRE TEDD!.\n\n\n Ez a firmware ehhez a hardware-verzióhoz tartozik: %s + A következő alkalmazást fogod telepíteni:\n\n\n%1$s Verzió %2$s , készítette: %3$s\n + N/A + inicializált + %1$s , készítette: %2$s + Eszköz keresése + Keresés leállítása + Keresés indítása + Új eszköz csatlakoztatása + %1$s (%2$s) + Eszköz párosítása + Használja az Android Bluetooth párosítás ablakot eszköz párosításához. + Párosítsd a Mi Band-ed + %s párosítása… + Összeköttetés létesítése %1$s (%2$s) eszközzel + Sikertelen párosítás %1$s (%2$s) eszközzel + Összeköttetés folyamatban %1$s (%2$s) eszközzel + Meglévő párosítás %1$s (%2$s) eszközzel, kapcsolódás… + Nincs találat a MAC címre, nem lehet párosítani. + Eszköz specifikus beállítások + MI Band / Amazfit beállítások + Férfi + + Egyéb + Bal + Jobb + Nem helyes felhasználói adatok vannak megadva, alapértelmezett adatokat fogok használni. + Amikor vibrálni kezd a Mi Band-ed, érintsd meg párszor egymás után. + Telepítés + Tedd láthatóvá a készüléked. A jelenleg csatlakoztatott készülékek nem fognak láthatóként megjelenni. Aktiváld a helymeghatározást (Pl: GPS) az Android 6 készülékeden. Kapcsold ki a Privacy Guard-ban a Gadgetbridge-et (Ha van Privacy Guard beállításod), különben az alkalmazás kifagyhat, illetve újraindíthatja a készükéked. Ha nem található az eszköz pár percen bellül, próbáld újraindítani a telefonod. + Megjegyzés: + Eszköz képe + Név/Alias + Rezgések száma + Alvástörténet + Naplófájlok írása + Inicializálás + Aktivitási adatok lekérdezése. + %1$s és %2$s között + Melyik kezeden hordod? + Rezgés profil + Szaggatott + Rövid + Közepes + Hosszú + Vízcsepp + Csörgés + Ébresztőóra + Rezgés + Próba + SMS értesítés + Rezgés beállítások + Általános értesítés + E-Mail értesítés + Bejővő hívás értesítés + Csevegés + Navigáció + Közösségi háló + Sebesség zónák + Összes perc + Percenkénti lépések + Elveszett eszköz keresése + A vibrálás leállításához: Mégse. + Aktivitásod + Ébresztések beállítása + Ébresztők beállítása + Ébresztés részletek + Vas + Hét + Ked + Sze + Csü + Pén + Szo + Okos ébresztés + Hiba történt az ébresztések beállításakor, kérlek próbáld újra! + Ébresztések beállítva. + Nincs adat. Szinkronizáljunk? + Adatok fogadása: %1$s %2$s -tól + Napi céllépésszám + Hiba \'%1$s\' futtatásakor + Aktivitásod (ALFA) + Nem lehet kapcsolódni: %1$s + A telepítéshez szükséges segédfájl nem található. + Nem sikerült telepíteni a fájlt: %1$s + Sikertelen telepítés: a firmware nem kompatibilis a Pebble hardver verzióddal. + Kérlek várj a telepítési státusz megállapításáig… + Alacsony az eszköz akkumulátor töltése! + %1$s fennmaradó akkumulátor töltés: %2$s%% + Utoljára töltve: %s \n + Töltések száma: %s + Az alvásod + Heti alvás + Mai alvás, cél: %1$s + Lépések a héten + Aktivitásod és alvásod + Firmware telepítése… + A fájlt nem lehet telepíteni, a készülék nincs készen. + %1$s: %2$s %3$s + Kompatibilis verzió + Nem tesztelt verzió! + Csatlakozás az eszközhöz: %1$s + Pebble Firmware %1$s + Helyes hardververzió + Hardververzió eltérés! + %1$s (%2$s) + Probléma volt a firmware küldésekor. NE INDÍTSD ÚJRA a Mi Band-ed! + Probléma volt a Firmware metaadatainak küldésekor + A Firmware telepítés kész + A Firmware telepítés kész, az eszköz újraindítása… + Firmware telepítése sikertelen + Lépések + Kalória + Távolság + Óra + Pulzus + Akkumulátor + Aktuális aktivitás + Lépések ma, cél: %1$s + Ne erősítse meg az aktivitásadatok átvitelét + Ha az aktivitásadatok átvitele nincs megerősítve a band felé, az nem kerülnek törlésre. Hasznos, ha a GB-t más alkalmzásokkal együtt használod. + Az adatokat meghagyja a Mi Band-en szinkronizálás után is. Hasznos, ha a GadgetBridge-et együtt használod más alkalmazásokkal. + Használja a low-latency módot a firmware telepítésnél + Ez segíthet, ha a firmware telepítés sikertelen. + Lépésnapló + Jelenlegi lépés/perc + Összes lépés + Lépés/perc napló + Aktivitás kezdése + Aktivitás + Könnyű alvás + Mély alvás + Nem viselt + Nincs csatlakoztatva. + Mindegyik ébresztés tiltva van + Aktivitásadatok eszközön hagyása + Nem kompatibilis firmware + Ez a firmware nem kompatibilis az eszközöddel + Naptári események számára fentartott riasztások száma + Pulzus szenzor használata az alvás érzékelés javításához + Eszköz időeltolása órákban (Hogy az éjszakai műszakban dolgozók alvását is érzékelje.) + Dátumformátum + Idő + Idő és Dátum + Cél értesítés + A karkötő vibrálni fog amikor elérted a napi lépés célod + Megjelenítendő elemek + Válaszd ki milyen elemek jelenjenek meg a karkötő kijelzőjén + Kijelző aktiválása felemeléskor + Forgasd el a karkötőt információ váltáshoz + Ne zavarj mód + A karkötő nem fog értesítéseket jelezni amíg ez aktív + Inaktivitás figyelmeztetés + A karkötő rezegni fog amikor inaktív vagy már egy ideje + Inaktivitás határ (percben) + Inaktivitás figyelmeztetések tiltása adott időre + Kezdete + Vége + Adatok átvitele %1$s óta + Várakozás az újracsatlakozásra + Rólad + Születési év + Nem + Magasság cm-ben + Testsúly kg-ban + hitelesítés + hitelesítés szükséges + Zzz + Widget hozzáadása + Preferált alvási idő órában + Ébresztő beállítva: %1$02d:%2$02d + Hardware változat: %1$s + Firmware változat: %1$s + Hiba a könyvtár létrehozásakor a naplófájlok számára: %1$s + "HR: " + Firmware telepítése + Nem küldött Firmware + Pulzus + Pulzus + Nyers adatok tárolása az adatbázisban + Ha bejelölöd, az adatok eredeti formában lesznek tárolva későbbi értelmezéshez. Megj.: az adatbázis ebben az esetben nagyobb lesz! + Adatbáziskezelés + Adatbázis kezelés + Az adatbázisművelet ezt a helyet fogja használni. Ez a hely elérhető másik Android-alkalmazások és a számítógép számára. Itt keresd az exportált adatbázist is, illetve ide rakd az importálni kívánt adatbázist is. + Hagyaték adatbázis törlése + Nem elérhető az elérési út. Kérlek vedd fel a kapcsolatot a fejlesztőkkel. + %1$s exportálva + Hiba az exportálás során: %1$s + Hiba a beállítások exportálása során: %1$s + Adatok importálása? + Biztosan felül akarod írni a jelenlegi adatbázist? Az összes jelenlegi aktivitás adatod (ha van) elfog veszni. + Sikeres importálás. + Hiba az importálás során: %1$s + Hiba a beállítások importálása során: %1$s + Törlöd az aktivitásadatokat? + Tényleg törölni akarod az egész adatbázist? Minden aktivitásadatod és rólad tárolt információ elfog veszni. + Adatok törölve. + Adatbázistörlési hiba. + Törlöd a régi aktivitási adatokat? + Biztosan törlöd a régi aktivitásadatokat\? A nem importált aktivitási adatok el fognak veszni. + Régi aktivitásadatok törölve. + Nem sikerült a régi aktivitás-adatbázis törlése. + Felülírás + Mégse + Törlés + + Rezgés + + Pebble párosítása + Egy párosítási párbeszédablaknak kéne megjelennie az Android eszközödön. Ha nem látod, nézd meg az értesítési sávban és fogadd el a párosítási kérelmet. Utána fogadd ela kérelmet a Pebble-n. + Bizonyosodj meg róla, hogy a skin támogatja-e az Időjárás értesítések megjelenítését. Nincs szükség konfigurációra. A Pebble app management-ben tudod engedélyezni a rendszer időjárás alkalmazását. Azok a skinek amik támogatják az időjárás értesítésket, automatikusan megfogják jeleníteni azt. + Bluetooth párosítás engedélyezése + Kapcsold ki ezt, ha gondod van a csatlakozással + Metrikus + Imperiál + 24H + AM/PM + Ébresztőóra + (%1$s) + Megtaláltad! + Mi2: Idő formátum + Ezt a verziót kell telepítened %1$s firmware telepítés előtt! + Szöveges értesítés + = 1.0.1.28 és Mili_pro.ft* telepítve.]]> + Ki + Ki + Automatikus (alvás érzékelés) + Időzített (idő intervallum) + Csatlakozási kísérlet az eszközhöz: %1$s + A csatlakozás %1$s eszközzel azonnal megszkadt. + Csatlakozási kísérlet: %1$s + Engedélyezd a Bluetooth-ot az eszközök felderítéséhez. + Sikeres párosítás: %1$s. + Párosítsam a(z) %1$s? + Válaszd ki a Párosítást az eszköz párosításához. Ha ez nem sikerül, próbáld meg újra párosítás nélkül. + Párosítás + Nem párosított + Támogatás + Csatlakoztatás… + Amazfit Bip beállítások + Időjárás + Aktivitás adatok automata letöltése + Vízszintes + Függőleges + Amazfit Cor beállításoks + Mi Band 2 beállítások + Mi Band 3 beállítások + LED szín változtatása + FM Frekvencia Változtatása + Jobbról-Balra + Biztos a gyári beállítások visszaállításában\? + Analóg mód + Képernyő be és kikapcsolása csukló forgatása által. + Időformátum + 24 óra + 12 óra + Dátum formátum + Ismétlés + Hétfő + Kedd + Szerda + Csütörtök + Péntek + Szombat + Vasárnap + Néma + Folytonos rezgés + Folytonos csipogás + Folytonos rezgés és csipogás + Nem fogadott hívás értesítés + Naptár értesítés + Eszköz specifikus beállítások + Eszköz kalibrálása + Gombnyomások száma + Ha bekapcsolja, lehetővé teszi az időjárás, akkuállapot stb. megjelenítését a számlapokon + percenként + 5 percenként + 10 percenként + 30 percenként + óránként + Hely az időjáráshoz (CM/LOS) + Amikor az óra rezeg, rázza meg az eszközt, vagy nyomja meg a gombját. + Ön %1$s és %2$s között aludt + Ön nem aludt + Nincs korlát + 5 másodperc + 10 másodperc + 20 másodperc + 30 másodperc + 1 perc + 5 perc + 10 perc + 30 perc + %1$ akkumulátora merül + %1$ akkumulátora merül: %2$s + A gyári beállítások visszaállítása minden adatot töröl a kapcsolódott eszközről (ha támogatott). A Xiaomi/Huami eszközökön emellett megváltozik a Bluetooth MAC cím is, így új eszközként jelennek meg a Gadgetbridge számára. + \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 86efb18d9..5e90eb3dc 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -14,8 +14,8 @@ Rimuovi dispositivo Rimuovi %1$s Il dispositivo verrà rimosso e tutti i dati ad esso associati verranno cancellati! - Apri menu - Chiudi menu + Apri menu di navigazione + Chiudi menu di navigazione Pressione prolungata sulla scheda per scollegare Disconnessione Connessione … @@ -45,12 +45,12 @@ Calendari ignorati Installazione FW/App - Il firmware %s verrà installato al posto di quello attualmente sulla Mi Band. + Stai per installare il firmware %s. Stai per installare i firmware %1$s e %2$s al posto di quelli al momento sulla tua Mi Band. Questo firmware è stato testato ed è compatibile con Gadgetbridge. Questo firmware non è stato testato e potrebbe non essere compatibile con Gadgetbridge. \n -\nNON è consigliato installarlo sulla Mi Band! +\nNON è consigliato installarlo sulla tua Mi Band! Se si procede e tutto continua a funzionare ti invitiamo a contattare gli sviluppatori e dire loro di aggiungere il firmware %s all\'elenco di quelli compatibili. Impostazioni @@ -62,7 +62,7 @@ Default Data e ora Sincronizza l\'ora - Sincronizza l\'orario al collegamento con il dispositivo Gadgetbridge e quando viene cambiato l\'orario o il fuso orario nel dispositivo Android + Sincronizza l\'orario del dispositivo al collegamento con Gadgetbridge e quando viene cambiato l\'orario o il fuso orario nel dispositivo Android Tema Chiaro Scuro @@ -312,7 +312,7 @@ Notifica raggiungimento obbiettivi La band vibrerà una volta raggiunto l\'obbiettivo passi giornalieri Elementi mostrati - Scelta elementi da mostrare sullo schermo della band + Scegli gli elementi da mostrare sullo schermo della band Attiva il display quando sollevato Rotazione polso per cambiare vista Non disturbare @@ -339,7 +339,7 @@ Versione Hardware: %1$s Versione Firmware: %1$s Errore durante la creazione della directory per i file di log: %1$s - "HR: " + "Battito Cardiaco: " Aggiornamento firmware in corso Firmware non inviato Battito cardiaco @@ -406,7 +406,7 @@ \nPROCEDI A TUO RISCHIO E PERICOLO! Si sta per installare il firmware %s sul Amazfit Cor. \n -\nSi prega di installare prima il file .fw e successivamente il file .res. Il tuo smartwatch si riavvierà dopo l\'installazione del file .fw. +\nSi prega di installare prima il file .fw e successivamente il file .res. La tua band si riavvierà dopo l\'installazione del file .fw. \n \nNOTA: Non è necessario installare il file .res se risulta essere uguale a quello precedentemente installato. \n @@ -432,7 +432,7 @@ ogni 10 minuti ogni 30 minuti ogni ora - Esportazione del database fallita! Si prega di controllare le impostazioni. + Esportazione del database fallita! Per favore controlla le impostazioni. Azioni tasto Specifica le azioni alla pressione del pulsante del Mi Band 2 Conteggio pressione tasto @@ -492,15 +492,15 @@ Consenti le notifiche da tutte le applicazioni Si sta per installare il firmware %s sul Mi Band 3. \n -\nSi prega di installare prima il file .fw e successivamente il file .res. Il tuo Miband si riavvierà dopo l\'installazione del file .fw. +\nSi prega di installare prima il file .fw e successivamente il file .res. La Miband3 si riavvierà dopo l\'installazione del file .fw. \n \nNOTA: Non è necessario installare il file .res se risulta essere uguale a quello precedentemente installato. -\n -\nNON TESTATO, POTREBBE DANNEGGIARE IL DISPOSITIVO IN MANIERA IRREVERSIBILE, PROCEDI A TUO RISCHIO E PERICOLO! +\n +\nPROCEDI A TUO RISCHIO E PERICOLO! Modalità GATT client Sperimentale, compatibile solo con Pebble 2, attivare solo in caso di problemi di connettività "Recupero automatico dati delle attività" - Il recupero avviene quando lo schermo viene sbloccato. Funziona solo se è attivata una modalità di blocco! + Sincronizza i dati quando lo schermo viene sbloccato. Funziona solo se è attivata una modalità di blocco! Traccia delle tua attività Non misurato Attività @@ -529,7 +529,7 @@ Bussola Impostazioni Alipay - Cambio colore LED + Cambia colore LED Cambia frequenza FM Calibra Dispositivo Tempo minimo tra le notifiche @@ -609,8 +609,8 @@ Sonno extra: %1$s Passi mancanti: %1$d Passi eccedenti: %1$d - Proseguire davvero con il reset totale\? - Effettuando un reset totale si perderanno tutti i dati relativi ai dispositivi connessi (se supportati). I dispositivi Xiaomi/Huami potrebbero cambiare il proprio indirizzo MAC Bluetooth e Gadgetbridge potrebbe riconoscerli come dispositivi completamente nuovi. + Vuoi ripristinare alle impostazioni di fabbrica\? + Effettuare il ripristino delle impostazioni di fabbrica cancellerà tutti i dati presenti sul dispositivo connesso (se supportato). I dispositivi Xiaomi/Huami cambieranno anche il proprio MAC Address Bluetooth, in modo da risultare dispositivi nuovi per Gadgetbrige. Casio GB-6900 Filtro Notifiche L\'app non deve trovarsi nella blacklist per essere configurata @@ -627,4 +627,93 @@ Salva Configurazione Non connesso, sveglia non impostata. Esercizio + Impostazioni Battito cardiaco + Durata schermo acceso in secondi + L\'orologio ti avviserà quando la frequenza del tuo battito cardiaco supererà il limite. + Frequenza cardiaca massima + Frequenza cardiaca minima + Ruota il polso per attivare o disattivare il display. + 24h + 12h + Formato data + AA/MM/GG + GG/MM/AA + MM/GG/AA + Lunedì + Martedì + Mercoledì + Giovedì + Venerdì + Sabato + Domenica + Abilita chiamate VoIP + Impostazioni ZeTime + Avviso frequenza cardiaca + Abilita l\'avviso per la frequenza cardiaca + Analogico + Solo mani + Mani e passi + Registrazione attività + Attivando la registrazione dell\'attività, verranno contati i passi e altre misure. + Movimento della mano + Tipo calorie + Solo calorie bruciate attivamente + Calorie bruciate attivamente e passivamente + Formato ora + Ripetizioni + Imposta il tipo di segnale per l\'allarme + Silenzioso + Vibrazione continua + Beep continuo + Beep e vibrazione continui + VIbra una volta + VIbra due volte + Beep singolo + Beep doppio + Vibrazione e beep singoli + Impostazioni specifiche del dispositivo + Chiave di autenticazione + Cambia la chiave di autenticazione con una chiave comune a tutti i tuoi dispositivi Android dai quali vuoi connetterti. La chiave predefinita precedente per tutti i dispositivi è 0123456789@ABCDE + Notifica chiamata persa + Notifica calendario + Notifica inattività + Avviso batteria scarica + Avviso anti-perdita + ogni 15 minuti + ogni 45 minuti + Notifica disconnessione + Obiettivo giornaliero: calorie bruciate + Obiettivo giornaliero: distanza percorsa in metri + Obiettivo giornaliero: tempo di attività in minuti + Mi Scale 2 + BFH-16 + Stai per installare il firmware %s sul tuo Amazfit Cor 2. +\n +\nAssicurati di installare il file .fw e successivamente il file .res. L\' Amazfit Cor 2 si riavvierà dopo aver installato il file .fw. +\n +\nNota: non è necessario installare .res se è esattamente lo stesso di quello precedentemente installato. +\n +\nPROCEDI A TUO RISCHIO! +\n +\nNON COMPLETAMENTE TESTATO, PROBABILMENTE DOVRAI FLASHARE IL FIRMWARE BEATS_W SE IL NOME DEL TUO DISPOSITIVO è \"Amazfit Band 2\" + Olandese + Turco + Ucraino + Arabo + Indonesiano + Tailandese + Vietnamita + Portoghese + Amazfit Cor 2 + Mi Band 4 + Si sta per installare il firmware %s sul Mi Band 4. +\n +\nSi prega di installare prima il file .fw e successivamente il file .res. La Mi Band 4 si riavvierà dopo l\'installazione del file .fw. +\n +\nNOTA: Non è necessario installare il file .res se risulta essere uguale a quello precedentemente installato. +\n +\nPROCEDI A TUO RISCHIO E PERICOLO! + Imposta allarme frequenza cardiaca durante l\'attività sportiva + Limite inferiore + Limite Superiore \ No newline at end of file diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml new file mode 100644 index 000000000..de7c902a5 --- /dev/null +++ b/app/src/main/res/values-my/strings.xml @@ -0,0 +1,39 @@ + + + Gadgetbridge + Gadgetbridge + ခ်ိန္ညွိရန္ + ျပစ္ခ်က္ + ထြက္မည္ + လွဴဒါန္းရန္ + အခ်ိန္ကိုက္လႈပ္ရွားမည္ + ေပ်ာက္ေနေသာအရာကိုရွာမည္ + စကရင္ကိုဓာတ္ပံုရိုက္မည္ + အေရာင္ေျပာင္းမည္ + ေရဒီယိုလႈိင္းေျပာင္းလဲမည္ + ခ်ိတ္ဆက္မိပါျပီ… + အဆက္အသြယ္ျပက္သြားသည္ + ဤအရာကိုဖ်က္မည္ + %1$s ကိုဖ်က္မည္ + ထိုအရာသည္ခ်ိတ္ဆက္ထားေသာအရာႏွင့္ေဒတာအားလံုးကိုဖ်က္ပစ္ပါမည္ + ခ်ိတ္ဆက္မႈျဖတ္ေတာက္ရန္ ဤကဒ္ကိုဖိထားပါ + ခ်ိတ္ဆက္မႈျဖတ္ေတာက္ေနသည္ + ခ်ိတ္ဆက္ေနပါသည္… + ျပစ္ခ်က္ + အရာအားလံုးကိုမူလအတိုင္းျပန္ထားမည္မွသခ်ာပါသလား + အက္ပလီေကးရွင္းကိုစီမံခန္႔ခြဲမည္ + သြင္းထားေသာ အက္ပလီေကးရွင္းမ်ား + သြင္းထားေသာ နာရီပံုစံမ်ား + ဖ်က္မည္ + ေဒတာမ်ားႏွင့္အတူဖ်က္မည္ + အက္ပလီေကးရွင္းျပန္လည္သြင္းရန္ + Pebble စတိုးတြင္ရွာမည္ + စတင္မည္ + ပိတ္မည္ + HRM စတင္မည္ + HRM ပိတ္မည္ + + အခ်က္အလက္ကြဲမ်ား + အခ်က္အလက္မ်ားကိုသိမ္းဆည္းမည္ + မခ်ိတ္ဆက္ထားျခင္းမရွိပါ , သတိေပးခ်က္မထားရေသးပါ + \ No newline at end of file diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index b4f92e7a0..04b687a26 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -39,7 +39,7 @@ Merknadssvarteliste Svartelistede kalendere Fastvare-/program-installerer - Du er i ferd med å installere %s-fastvaren i stedet for den som er installert på ditt Mi Band nå. + Du er i ferd med å installere %s. Gadgetbru Gadgetbru Dette vil slette enheten og all tilknyttet data! @@ -52,7 +52,7 @@ \nFORTSETT PÅ EGEN RISIKO! Du er i ferd med å installere %s-fastvaren på din Amazfit Cor. \n -\nForsikre deg om at du installerer .fw-fila, og så .res-fila. Armbåndsuret ditt vil starte på nytt etter at .fw-fila er installert. +\nForsikre deg om at du installerer .fw-fila, og så .res-fila. Båndet ditt vil starte på ny etter at .fw-fila er installert. \n \nMerk: Du må ikke installere .res og .gps hvis disse er akkurat de samme som dem som allerede er installert. \n @@ -60,8 +60,8 @@ Du er i ferd med å installere %1$s- og %2$s-fastvarene isteden for dem som er installert nå på ditt Mi Band. Denne fastvaren har blitt testet og er kompatibel med Gadgetbru. Denne fastvaren er utestet og er kanskje ikke kompatibel med Gadgetbru. -\n -\nDu FRARÅDES fra å skrive den til ditt Mi Band! +\n +\nDu FRARÅDES fra å skrive den til enheten! Hvis du fremdeles ønsker å fortsette og ting fungerer etterpå, fortell Gadgetbru-utviklerne at de skal hvitliste %s-fastvareversjonen. Innstillinger Hovedinnstillinger @@ -522,13 +522,13 @@ Dette er kun for Pebble 2 og eksperimentelt, prøv dette hvis du har tilkoblingsproblemer Alipay (snarvei) Vær (snarvei) - Du er i ferd med å installere %s-fastvaren på din Mi Band 3. -\n -\nInstaler først .fw-fila, og dernest .res-fila. Klokka di vil starte på nytt etter at du installerer .fw-fila. -\n -\nMerk: Du trenger ikke å installere .res hvis du har akkurat den samme installert. -\n -\nUTESTET, KAN HAVARERE ENHETEN, FORTSETT PÅ EGEN RISIKO! + Du er i ferd med å installere %s-fastvaren på din Mi Band 3. +\n +\nInstaler først .fw-fila, og dernest .res-fila. Båndet ditt vil starte på nytt etter at du installerer .fw-fila. +\n +\nMerk: Du trenger ikke å installere .res hvis du har akkurat den samme installert. +\n +\nFORTSETT PÅ EGEN RISIKO! Mi Band 3 Q8 Svartelist merknader fra alle disse @@ -606,7 +606,7 @@ Sovet for lenge: %1$s Ikke nok steg: %1$d For mange steg: %1$d - Nåværende / maksimal puls: %1$d /%2$d + Nåværende / makspuls: %1$d /%2$d Diagraminnstillinger Makspuls Minimumspuls @@ -675,17 +675,51 @@ Daglig mål: Distanse i meter Daglig mål: Aktiv tid i minutter Mi Scale 2 - Hjertefrekvensinnstillinger - Skjerm på varighet i sekunder - Hjertefrekvensalarm - Klokken vil advare deg når hjertefrekvensen overskrider grensene. - Aktiver hjertefrekvensalarmen - Maks hjertefrekvens - Min hjertefrekvens + Pulsinnstillinger + Skjermaktivitetsvarighet, i sekunder + Pulsalarm + Klokken vil advare deg når pulsen overskrider grensene. + Aktiver pulsalarmen + Makspuls + Minimumspuls Hender og trinn - Hvis du slår aktivitetssporing på, teller trinnene dine og så videre. - Vri håndleddet for å aktivere eller deaktivere skjermen. - Bare brenne aktive kalorier - Aktiv og inaktiv brent kalorier + Påslag av aktivitetssporing vil telle skrittene dine, osv. + Vri håndleddet for å skru på eller av skjermen. + Kun aktivt forbrente kalorier + Aktivt og inaktivt -forbrente kalorier Advarsel om tap + Skru på VoIP-programsamtaler + Enhetsspesifikke innstillinger + Identitetsbekreftelsesnøkkel + Endre identitetsbekreftelsesnøkkelen til en vanlig nøkkel på alle Android-enhetene du ønsker å koble til fra. Forrige forvalgte nøkkel for alle enheter var 0123456789@ABCDE + BFH-16 + Du er i ferd med å installere %s-fastvaren på din Amazfit Cor 2. +\n +\nInstaller .fw-filen, og deretter .res-filen. Ditt bånd vil starte på ny etter installering av .fw-filen. +\n +\nMerk: Du trenger ikke å installere .res hvis denne er den samme versjon som den som finnes på båndet. +\n +\nFORTSETT PÅ EGEN RISIKO! +\n +\nHELT UTESTET, DU MÅ ANTAGELIG BRENNE INN FLASH A BEATS_W -FASTVARE HVIS ENHETSNAVNET DITT ER \"Amazfit Band 2\" + Hollandsk + Tyrkisk + Ukrainsk + Arabisk + Indonesisk + Thai + Vietnamesisk + Portugisisk + Amazfit Cor 2 + Mi Band 4 + Du er i ferd med å installere %s-fastvaren på ditt Mi Band 4. +\n +\nForsikre deg om at du installerer .fw-filen, og deretter .res-filen. Båndet vil startes på ny etter installasjon av .fw-filen. +\n +\nMerk: Du trenger ikke å installere .res-filen hvis den er samme som du hadde installert fra før. +\n +\nFORTSETT PÅ EGEN RISIKO! + Pulsalarm under sportsaktiviteter + Nedre grense + Øvre grense \ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c66a8ec8e..719b006fa 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -10,210 +10,217 @@ Buscar dispositivo desconectado Print da tela Desconectar - Apagar dispositivo - Apagar %1$s - Isto irá apagar o dispositivo e todos os dados associados! + Excluir dispositivo + Excluir %1$s + Isto vai excluir o dispositivo e todos os dados associados! Depurar - Gerenciador de App - Apps em cache + Gerenciador de aplicativos + Aplicativos em cache Apps instalados Mostradores instalados - Apagar - Apagar e remover do cache + Excluir + Excluir e remover do cache Reinstalar - Buscar na loja Pebble + Pesquisar na loja Pebble Ativar Desativar Ativar HRM Desativar HRM - Ativar app de clima do sistema - Desativar app de clima do sistema - Instalar notificações do app de clima + Ativar o aplicativo de clima do sistema + Desativar o aplicativo de clima do sistema + Instalar o aplicativo de notificação de clima Configurar Mover para o topo - Lista Negra de Notificações + Lista negra de notificações - Instalador FW/App - Você está prestes a instalar o firmware %s no lugar do atual em sua Mi Band. - Você está prestes a instalar os firmwares %1$s e %2$s no lugar dos que estão atualmente na sua Mi Band. + Instalador de FW/aplicativo + Você está prestes a instalar o %s. + Você está prestes a instalar os firmwares %1$s e %2$s no lugar dos que estão atualmente no seu Mi Band. O firmware foi testado e é compatível com Gadgetbridge. Este firmware não foi testado e pode não ser compatível com o Gadgetbridge. \n -\nNÃO recomendamos que instale em sua Mi Band! - Se você ainda desejar continuar e tudo funcionar corretamente, por favor, diga aos desenvolvedores do Gadgetbridge para incluírem essa versão de firmware na lista: %s +\nNÃO É RECOMENDADO que você instale-o! + Se você ainda desejar continuar e tudo funcionar corretamente, por favor, diga aos desenvolvedores do Gadgetbridge para incluírem a versão de firmware %s na lista. Configurações - Configurações Gerais - Conecta o dispositivo quando o Bluetooth estiver ligado + Configurações gerais + Conecta o dispositivo Gadgetbridge quando o Bluetooth estiver ligado Iniciar automaticamente Reconectar automaticamente - Player de música preferencial + Preferência de player de música Padrão - Data e Hora - Sincronizar hora - Sincronizar horário quando o dispositivo estiver conectando ou quando o fuso horário mudar no Android + Data e hora + Horário de sincronização + Horário de sincronização para quando o dispositivo Gadgetbridge estiver conectando ou quando o fuso horário mudar no dispositivo Android Tema Claro Escuro Idioma - Ocultar notificações do Gadgetbridge + Ocultar notificação do Gadgetbridge Exibir ícone na barra de status e nas notificações na tela inicial - Ocultar ícone na barra de status e nas notificações na tela inicial + O ícone na barra de status e as notificações na tela de bloqueio ficam ocultos Notificações Repetições Chamadas de telefone SMS Mensagens do Pebble - Suportar notificações de aplicações que enviam notificações pelo PebbleKit. + Suporte a aplicativos que enviam notificações ao Pebble pelo PebbleKit. Suporte a notificações genéricas - ...quando a tela estiver ligada - Não perturbe - Parar com notificações indesejadas enquanto estiver no modo Não Perturbe. - Representação - Ativar isso se o dispositivo não tiver suporte para seu idioma - sempre - quando a tela estiver desligada + …quando a tela estiver ligada + Não perturbar + Notificações indesejadas são bloqueadas neste modo + Transliteração + Ative isso se seu dispositivo não tiver suporte à fonte do seu idioma + Sempre + Quando a tela estiver desligada Nunca Privacidade - Modo de chamada privada + Modo de privacidade de chamadas Exibir nome e número - Ocultar nome e exibir número + Ocultar nome, mas exibir número Ocultar nome e número - Apps em Blacklist - Histórico de mensagens + Lista negra de aplicativos + Mensagens predefinidas Respostas Sufixo comum - Chamadas recusadas - Atualizar no Pebble - Opções do desenvolvedor - Endereço do Mi Band - Configurações Pebble - Ativar monitores - Monitor de atividades preferido + Chamadas ignoradas + Atualização no Pebble + Opções de desenvolvedor + Endereço da Mi Band + Configurações de Pebble + Monitores de atividade + Preferência de monitor de atividades Sincronizar com Pebble Health Sincronizar com Misfit Sincronizar com Morpheuz - Suporta originar chamadas - Desabilitar isso também irá impedir o Pebble 2/LE vibrar ao originar chamadas - Permitir acesso a App Android de terceiros - Habilitar suporte experimental ao App Android que use PebbleKit - Despertar e pôr do sol - Enviar despertar e pôr do sol baseado na localização do Pebble - Auto remover notificações rejeitadas - Notificações são automaticamente removidas quando rejeitadas no Android + Suporte a chamadas originadas + Desabilitar isso também impedirá o Pebble 2/LE de vibrar ao originar chamadas + Permitir acesso a aplicativos de terceiros + Habilitar suporte experimental a aplicativos de Android que usem PebbleKit + Nascer e por do sol + Envia horários de nascer e por do sol baseado na localização para a linha do tempo do Pebble + Remover automaticamente as notificações ignoradas + Notificações são automaticamente removidas do Pebble quando ignoradas no Android Modo de privacidade - Notificações + Notificações normais Deslocar texto de notificações que extrapolar a tela - Apenas mostrar ícone de notificações + Mostrar apenas o ícone de notificações Localização Obter localização Latitude Longitude - Mantenha a localização atualizada - Tente obter a localização online, use dados armazenados como fallback - Por favor, habilite localização de rede + Manter localização atualizada + Tenta obter a localização atual em tempo real, use a localização armazenada como reserva + Por favor, ative a localização de rede localização obtida Forçar o protocolo de notificação - Esta opção força o uso do protocolo de notificação mais recente. HABILITE APENAS SE SABE O QUE ESTÁ FAZENDO! + Esta opção força o uso do protocolo de notificação mais recente dependendo da versão do firmware. FAÇA ISSO APENAS SE SOUBER O QUE ESTÁ FAZENDO! Habilitar recursos não certificados - Habilitar recursos não certificados. FAÇA ISSO SE SOUBER O QUE REALMENTE ESTÁ FAZENDO! + Habilitar recursos não testados. FAÇA ISSO APENAS SE SOUBER O QUE ESTÁ FAZENDO! Sempre preferir BLE - Use suporte experimental do Pebble LE em vez do Bluetooth clássico, requer parear um \"Pebble LE\" depois de que nenhum LE tenha sido conectado - Pebble 2/LE GATT MTU limite + Usa suporte experimental a Pebble LE em vez do Bluetooth clássico. Requer ter nenhum LE pareado para, em seguida, parear o Pebble LE + Limite do MTU de GATT do Pebble 2/LE Se seu Pebble 2/Pebble LE não funciona como esperado, tente esta configuração para limitar o MTU (faixa válida 20–512) - Ativas registro de Log do App do Relógio - Faz com que os logs de aplicativos de monitoramento sejam registrados por Gadgetbridge (exige reconectar) - Prematuramente ACK PebbleKit - Isso fará com que as mensagens enviadas para aplicativos de terceiros sejam reconhecidas sempre e imediatamente - Tentativas de Reconexão + Ativar log do aplicativo de monitoramento + Faz com que os registros log de aplicativos de monitoramento sejam registrados pelo Gadgetbridge (exige reconexão) + ACK prematuro ao PebbleKit + Faz com que as mensagens enviadas para aplicativos de terceiros sejam reconhecidas sempre e imediatamente + Tentativas de reconexão Unidades Formato da hora - Duração de tela - Medição da frequência cardíaca durante todo o dia - Configurações do HPlus/Makibes - desconectado - conectando - conectado - estado desconhecido + Duração da tela ligada + Medição da frequência cardíaca ao longo do dia + Configurações de HPlus/Makibes + Desconectado + Conectando + Conectado + Estado desconhecido (desconhecido) Teste - Teste de notificação - Esta é uma Notificação de Teste de Gadgetbridge + Notificações teste + Esta é uma notificação teste do Gadgetbridge Bluetooth não suportado. Bluetooth desabilitado. - Toque no dispositivo conectado - Toque no dispositivo conectado - Toque no dispositivo conectado para Vibração + Toque no dispositivo conectado para o gerenciador de aplicativos + Toque no dispositivo conectado para atividade + Toque no dispositivo conectado para vibração Toque num dispositivo para ligar - Não pode conectar. Endereço BT inválido? + Não foi possível conectar. Endereço de Bluetooth inválido\? Gadgetbridge em execução - Instalação binária %1$d/%2$d - falha na instalação! - instalação bem sucedida - VOCÊ ESTÁ TENTANDO INSTALAR UM FIRMWARE, A SEU PRÓPRIO RISCO. \n\n\nEste firmware é para HW Revisão: %s - Você está prestes a instalar o seguinte aplicativo: \n\n\n %1$s Versão %2$s por %3$s\n + Instalando binário %1$d/%2$d + Instalação falhou + Instalado + VOCÊ ESTÁ TENTANDO INSTALAR UM FIRMWARE, A SEU PRÓPRIO RISCO. +\n +\n +\n Este firmware é para HW Revisão: %s + Você está prestes a instalar o seguinte aplicativo: +\n +\n +\n%1$s Versão %2$s por %3$s +\n N/A inicializado %1$s de %2$s - Dispositivo encontrado + Descoberta de dispositivo Parar com a busca - Iniciar busca - Conecte novo dispositivo + Iniciar descoberta + Conectar novo dispositivo %1$s (%2$s) Parear dispositivo - Use a caixa de diálogo Android Bluetooth para emparelhar o dispositivo. + Use a caixa de diálogo Android Bluetooth para parear o dispositivo. Parear sua Mi Band Pareando com %s… Criar vínculo com %1$s (%2$s) - Não é possível emparelhar com %1$s (%2$s) + Não é possível parear com %1$s (%2$s) Vinculação em andamento: %1$s (%2$s) Já ligado com %1$s (%2$s), conectando… - Nenhum endereço do mac passou, não pode emparelhar. - Configurações Específicas do Dispositivo - Configurações da Mi Band - masculino - feminino - outro - esquerda - direita + Nenhum endereço MAC passado, não é possível parear. + Configurações específicas do dispositivo + Configurações de Mi Band / Amazfit + Masculino + Feminino + Outro + Esquerda + Direita Nenhum dado válido informado, usando dados fictícios por enquanto. - Quando a sua Mi Band vibra e pisca, toque nele algumas vezes seguidas. + Quando a sua Mi Band vibrar e piscar, toque nele algumas vezes seguidas. Instalar Nota: - Imagem do Dispositivo + Imagem do dispositivo Nome/Apelido Quantidade de vibrações Monitor de sono - Escrever arquivos de Log + Escrever arquivos de log Inicializando - Adquirindo Dados de Atividades + Coletando dados de atividade De %1$s a %2$s Dispositivo na esquerda ou direita? - Prefil de Vibração + Perfil de vibração Destacado Pequeno Médio Longo - Pingo D\'água + Pingo d\'água Anel Alarme Vibração - Tente - Notificações SMS + Experimente + Notificação SMS Configurações de vibrações Notificação genérica - Notificação de email - Notificações de chamadas - Bate papo + Notificação de e-mail + Notificação de chamada recebida + Bate-papo Navegação Rede social - Busca Dispositivo Perdido + Encontrar dispositivo perdido Cancele para parar a vibração. Sua atividade - Configurar Alarmes + Configurar alarmes Configurar alarmes Detalhes do alarme Dom @@ -222,15 +229,15 @@ Qua Qui Sex - Sab - despertar inteligente - Tem algum erro ao definir o alarme, tente novamente! - Alarme enviado para o dispositivo! + Sáb + Despertar inteligente + Houve um erro ao definir os alarmes. Por favor, tente novamente. + Alarmes enviado para o dispositivo. Sem data. Sincronizar com dispositivo? Transferir %1$s dados a partir de %2$s - Objetivo de passos por dia + Meta diária de passos Erro executando \'%1$s\' - Sua Atividade (ALPHA) + Sua atividade (ALFA) Impossível conectar: %1$s Não foi possível encontrar um manipulador para instalar o arquivo. Impossível instalar o arquivo fornecido: %1$s @@ -240,10 +247,10 @@ %1$s bateria a: %2$s%% Última carga: %s \n Número de cargas: %s - Seu Sono - Passos na semana - Sua Atividade e Sono - Atualizando Firmware... + Seu sono + Passos por semana + Sua atividade e sono + Instalando firmware… Arquivo não pode ser instalado, o dispositivo não está pronto. %1$s: %2$s %3$s Versão compatível @@ -253,19 +260,19 @@ Corrigir a revisão do hardware Incompatibilidade de revisão de hardware! %1$s (%2$s) - Problema com a transferência do firmware. NÃO REBOTAR sua Mi Band! + Problema com a transferência do firmware. NÃO REINICIAR sua Mi Band! Problemas ao transferir os metadados do firmware - Instalação do Firmware completa - Instalação do Firmware completa, reiniciando o dispositivo… - Falha ao instalar o Firmware + Instalação do firmware concluída + Instalação do firmware concluída, reiniciando o dispositivo… + Falha ao instalar o firmware Passos Atividade ao vivo - Etapas de hoje, meta: %1$s - Não transferir dados da atividade ack - Se os dados da atividade não forem chamados para a pulseira, eles não serão apagados. Útil se GB for usado em conjunto com outros aplicativos. - Manterão os dados da atividade no Mi Band mesmo após a sincronização. Útil se GB for usado em conjunto com outros aplicativos. - Use o modo de baixa latência para atualizações FW - Isso pode ajudar em dispositivos nos quais as atualizações de firmware falham + Passos hoje, meta: %1$s + Não reconhecer transferência de dados da atividade + Se os dados da atividade não forem reconhecidos para a pulseira, eles não serão apagados. Útil se GB for usado em conjunto com outros aplicativos. + Vai manter os dados de atividade no Mi Band mesmo após a sincronização. Útil se GB for usado em conjunto com outros aplicativos. + Usar o modo de baixa latência para instalação de firmware + Isso pode ajudar em dispositivos no caso de eventual falha na instalação de firmware. Histórico de passos Passos/min atuais Total de passos @@ -273,121 +280,129 @@ Iniciar sua atividade Atividade Sono leve - Sono pesado - Não utilizado + Sono profundo + Não usado Desconectado. Todos os alarmes desabilitados Manter dados de atividade no dispositivo Firmware incompatível Este firmware não é compatível com seu dispositivo - Alarmes reservados para eventos próximos - Use o sensor de freqüência cardíaca para melhorar a detecção do sono + Alarmes reservados para próximos eventos + Usar o sensor de frequência cardíaca para melhorar a detecção do sono Desvio do tempo do dispositivo em horas (para detectar o sono dos trabalhadores por turnos) - Mi2: Formato de data + Formato da data Hora - + Horário & data Ativar exibição ao levantar Sobre para transferir dados desde %1$s - aguarde para reconectar + Aguardando para reconectar Sobre você - Ano de anoversário + Ano de nascimento Gênero Altura em cm Peso em kg - autenticando - autenticação requerida + Autenticando + Autenticação necessária Zzz Adicionar widget - Preferir duração de sono em horas + Preferência de duração de sono em horas Um alarme foi definido para %1$02d:%2$02d - HW: %1$s - FW: %1$s + Hardware revisão: %1$s + Firmware versão: %1$s Erro ao criar o diretório para arquivos de log: %1$s "FC: " - Atualização de Firmware em progresso + Instalando firmware Firmware não enviado Frequência cardíaca Frequência cardíaca Armazenar registro bruto no banco de dados - Se marcado, os dados são armazenados \"como\" e estão disponíveis para interpretação posterior. NB: a base de dados será maior neste caso! + Armazena os dados \"como estão\", aumentando o uso do banco de dados para permitir posterior interpretação. Gerenciamento de banco de dados Gerenciamento de banco de dados - As operações do banco de dados usam o seguinte caminho no dispositivo.\nEste caminho está acessível a outras aplicações Android e ao seu computador.\nApós encontrar o banco de dados exportado (ou colocar o banco de dados que você deseja importar): + As operações do banco de dados usam o caminho abaixo em seu dispositivo. +\nEste caminho está acessível a outros aplicativos do Android e ao seu computador. +\nEspere encontrar seu banco de dados (ou coloque o banco de dados que você deseja importar) em: Não é possível acessar o caminho de exportação. Entre em contato com os desenvolvedores. Exportado para: %1$s Erro ao exportar BD: %1$s Importar dados? Realmente sobrescrever o banco de dados atual? Todos os seus dados atuais de atividade (se houver) serão perdidos. - Sucesso ao importar. + Importado. Erro ao importar BD: %1$s - Excluir Dados de Atividade? - Excluir realmente o banco de dados inteiro? Todos os seus dados de atividade e informações sobre seus dispositivos serão perdidos. - Dados eliminados com êxito. - Falha na eliminação do banco de dados. - Excluir antigo banco de dados de atividades? - Excluir realmente o banco de dados de atividade antigo? Os dados da atividade que não foram importados serão perdidos. - Dados de atividade antiga excluídos com êxito. - Falha na eliminação da base de dados da Actividade Antiga. + Excluir dados de atividade\? + Realmente excluir o banco de dados inteiro\? Todos os seus dados de atividade e informações sobre seus dispositivos serão perdidos. + Dados excluídos. + Falha na exclusão do banco de dados. + Excluir o banco de dados de atividades antigo\? + Realmente excluir o banco de dados de atividades antigo\? Os dados de atividade que não foram importados serão perdidos. + Os dados de atividades antigos foram excluídos. + Falha na exclusão do banco de dados da atividades antigo. Sobrepor Cancelar - Apagar + Excluir Vibração - Pareando Pebble - Espera-se que um diálogo de emparelhamento apareça no seu dispositivo Android. Se isso não acontecer, procure na gaveta de notificação e aceite o pedido de emparelhamento. Depois que aceitar o pedido de emparelhamento em seu Pebble - Certifique-se de que esta capa está ativada no aplicativo Notificação de Tempo para obter informações sobre o tempo em seu Pebble.\n\nNão é necessário configurar aqui.\n\nVocê pode ativar o aplicativo de tempo do sistema de seu Pebble do gerenciamento de aplicativos.\n\nOs relógios assistidos mostrarão o tempo automaticamente. - Ativar o emparelhamento Bluetooth - Desactive esta opção se tiver problemas para - Métrico + Pareamento do Pebble + Um diálogo de pareamento vai aparecer no seu dispositivo Android. Se isso não acontecer, procure na gaveta de notificação e aceite o pedido de pareamento. Também aceite-o no seu Pebble em seguida. + Certifique-se de que esta capa está ativada no aplicativo Notificação de Clima para obter informações sobre o tempo em seu Pebble. +\n +\nNão é necessário configurar aqui. +\n +\nVocê pode ativar o aplicativo de tempo do sistema de seu Pebble do gerenciamento de aplicativos. +\n +\nOs relógios assistidos mostrarão o tempo automaticamente. + Ativar pareamento Bluetooth + Desative isso se tiver problemas de conexão + Metro Imperial - 24H - AM/PM + 24h + am/pm Alarme Doar - Conectar + Conectar… Abrir gaveta de navegação Fechar gaveta de navegação - Pressione prolongadamente o cartão para desconectar + Pressione e segure o cartão para desconectar Desconectando - Conectando... + Conectando… Tirar captura de tela do dispositivo Blacklist do Calendário - Você está prestes a instalar o firmware %s no seu Amazip Bip. -\n -\nCertifique-se de instalar o firmware .gps, depois o arquivo .res e, finalmente, o arquivo .fw. Seu relógio será reiniciado após a instalação do arquivo .fw. -\n -\nNota: você não precisa instalar .res e .gps se esses arquivos forem exatamente os mesmos que os instalados anteriormente. -\n + Você está prestes a instalar o firmware %s no seu Amazip Bip. +\n +\nCertifique-se de instalar o firmware .gps, depois o arquivo .res e, finalmente, o arquivo .fw. Seu relógio será reiniciado após a instalação do arquivo .fw. +\n +\nNota: você não precisa instalar .res e .gps se esses arquivos forem exatamente os mesmos que os instalados anteriormente. +\n \nPROSSIGA POR SUA CONTA E RISCO! Você está prestes a instalar o firmware %s no seu Amazfit Cor. \n -\nCertifique-se de instalar o arquivo .res, e depois disso o arquivo .fw. Seu relógio será reiniciado após a instalação do arquivo .fw. +\nCertifique-se de instalar o arquivo .fw, e depois disso o arquivo .res. Sua pulseira será reiniciado após a instalação do arquivo .fw. \n \nNota: você não precisa instalar .res se for exatamente o mesmo que o instalado anteriormente. \n -\nNÃO TESTADO, PODE DANIFICAR SEU DISPOSITIVO, PROSSIGA AO SEU PRÓPRIO RISCO! +\nPROSSIGA AO SEU PRÓPRIO RISCO! Ativar deslizar para a esquerda/direita no gráfico de atividades - Ocultar número e exibir nome - Calendários na Blacklist + Ocultar número, mas exibir nome + Calendários na lista negra Clima Localização climática (CM/LOS) Sincronizar calendário Habilitar JS em segundo plano - Quando habilitado, permite que as Watchfaces mostrem o clima, informações da bateria, etc. - Exportar preferências automaticamente + Quando habilitado, permite que os watchfaces mostrem o clima, informações da bateria, etc. + Exportação automática Exportação automática habilitada - Exportar localização + Localização da exportação Intervalo de exportação Exportar a cada %d hora(s) Ajustes do Amazfit Bip - Permita que seu dispositivo seja detectado. Os dispositivos já pareados não serão detectados. Para o Android 6 ou superior, é necessário habilitar a localização de GPS. Desative o Privacy Guard para o Gadgetbridge, pois o telefone poderá travar ou reiniciar caso esteja ativo. Se nenhum dispositivo for encontrado após alguns minutos, tente novamente após reiniciar seu aparelho telefônico. - Medição diária dos batimentos cardíacos - A cada minuto - A cada 5 minutos - A cada 10 minutos - A cada 30 minutos - A cada hora + Permita que seu dispositivo seja detectado. Os dispositivos já pareados não serão detectados. Para o Android 6 ou superior, é necessário habilitar a localização de GPS. Desative o Privacy Guard para o Gadgetbridge, pois o telefone poderá travar ou reiniciar caso esteja ativo. Se nenhum dispositivo for encontrado após alguns minutos, tente novamente após reiniciar seu dispositivo móvel. + Medição diária de frequência cardíaca + a cada minuto + a cada 5 minutos + a cada 10 minutos + a cada 30 minutos + a cada hora Áreas de velocidade Minutos totais Passos por minuto @@ -397,7 +412,7 @@ Calorias Distância Relógio - Ritmo cardíaco + Frequência cardíaca Bateria Ações do botão Especifica as ações quando pressionado o botão da Mi Band 2 @@ -405,24 +420,312 @@ Número de toques no botão para enviar a mensagem Mensagem para enviar Enviar mensagem após um número definido de toques no botão - Enviar eventos do calendário para linha do tempo - Ativar botão - Ativar vibração - Notificações - Gadgetbridge + Envia eventos do calendário para linha do tempo + Ativar botão de ação + Ativar vibração da pulseira + Notificações do Gadgetbridge Alterar cor do LED Alterar frequência FM Calibrar Dispositivo - Bloquear todas as Notificações - Desbloquear todas as Notificações + Bloquear todas as notificações + Desbloquear todas as notificações Você está prestes a instalar o firmware %s na sua Mi Band 3. \n -\nCertifique-se de instalar o arquivo .fw, e depois o arquivo .res. Seu relógio será reiniciado após a instalação do arquivo .fw. +\nCertifique-se de instalar o arquivo .fw, e depois o arquivo .res. Sua pulseira será reiniciada após instalar o arquivo .fw. \n -\nNota: você não precisa instalar .res se esse arquivo é exatamente o mesmo instalado anteriormente. +\nNota:Vvocê não precisa instalar .res se esse arquivo é exatamente o mesmo instalado anteriormente. \n -\nNÃO FOI TESTADO, PODE QUEBRAR SEU DISPOSITIVO, PROSSIGA POR SUA CONTA E RISCO! +\nPROSSIGA POR SUA CONTA E RISCO! Tempo mínimo entre notificações - Da Direita para Esquerda - Ative isto se seu dispositivo não pode mostrar linguagens da Direita para Esquerda + Direita para a esquerda + Ative isto se seu dispositivo não consegue mostrar idiomas com a escrita da direita para a esquerda Gostaria realmente de restaurar as configurações de fábrica\? + Linha do tempo do Pebble + Notificação de meta atingida + A pulseira vai vibrar quando meta diária de passos for atingida + Exibir itens + Escolha os itens exibidos na tela da pulseira + Girar o pulso para alternar informações + Não perturbar + A pulseira não vai receber notificações enquanto ativo + Avisos de inatividade + A pulseira vai vibrar quando você estiver inativo por um tempo + Limite de inatividade (em minutos) + Desabilita avisos de inatividade por um intervalo de tempo + Horário de início + Horário de término + Exclusão de banco de dados legado + Erro ao exportar preferências: %1$s + Erro ao importar preferências: %1$s + (%1$s) + Você encontrou ele! + Mi2: Formato da hora + Você precisa instalar a versão %1$s antes de instalar esse firmware! + Notificações de texto + Precisa do firmware >= 1.0.1.28 e Mili_pro.ft* instalados. + Desligado + Desligado + Automático (detecção de sono) + Agendado (intervalo de tempo) + Tentando parear com %1$s + Vínculo com %1$s falhou imediatamente. + Tente se conectar a: %1$s + Ative o Bluetooth para encontrar dispositivos. + Vinculado a %1$s. + Parear com %1$s\? + Selecione Parear para parear seus dispositivos. Se isso falhar, tente novamente sem pareamento. + Parear + Não parear + Ativa ação em número especificado de pressionamentos do botão + Ativa vibração da pulseira quando a ação do botão é disparada + Atraso máximo entre pressionamentos + Atraso máximo entre pressionamentos do botão em milissegundos + Abrir no Android + Sem som + Responder + Atrasar após ação do botão + Atraso após uma ação do botão ser considerada válida (número está em button_id intent extra) ou 0 para imediatamente + Web View Atividades + Automático + Chinês simplificado + Chinês tradicional + Inglês + Firmware + Dados inválidos + Fonte + Firmware do GPS + GPS Almanac + Correção de erro de GPS + Recursos + Watchface + Dispositivo desconhecido + Dispositivo de teste + Pebble + Mi Band + Mi Band 2 + Amazfit Bip + Amazfit Cor + Vibratissimo + LiveView + HPlus + Makibes F68 + Exrizu K8 + No.1 F1 + Teclast H30 + Escolha a localização da exportação + Espanhol + XWatch + Ligado + Monitoramentos de suas atividades + Não medida + Atividade + Sono leve + Sono profundo + Dispositivo não usado + Corrida + Caminhada + Natação + Atividade desconhecida + Atividades + Ciclismo + Esteira + Selecionar todos + Compartilhar + Redefinir data de coleta + Status + Atividade + Clima + Alarme + Cronômetro + Bússola + Configurações + Alipay + Cliente GATT apenas + Isso é para Pebble 2 apenas e experimental, tente isso se você tiver problemas de conectividade + Alipay (Atalho) + Clima (Atalho) + Q8 + Mi Band 3 + Coletar automaticamente os dados de atividade + A coleta acontece ao bloquear a tela. Só funciona se um mecanismo de bloqueio estiver definido! + Tempo mínimo entre coletas + Coleta a cada %d minutos + MyKronoz ZeTime + Russo + Configurações ID115 + Orientação da tela + Horizontal + Vertical + ID115 + Configurações do Amazfit Cors + Notificações + Configurações do Mi Band 2 + Configurações do Mi Band 3 + Mais + Música + Quando seu relógio vibrar, agite o dispositivo ou pressione seu botão. + Watch 9 + Minutos: + Horas: + Segundos: + Defina o horário que seu dispositivo está mostrando para você agora mesmo. + Calibrar + Pareamento de Watch 9 + Calibração de Watch 9 + Desbloqueio de tela da pulseira + Deslize para cima para desbloquear a tela da pulseira + Alemão + Italiano + Francês + Polonês + Coreano + Japonês + Você dormir de %1$s para %2$s + Você não dormiu + Bokmål norueguês + Sem limite + 5 segundos + 10 segundos + 20 segundos + 30 segundos + 1 minuto + 5 minutos + 10 minutos + 30 minutos + Modo noturno + Reduz o brilho da tela da pulseira automaticamente à noite + No por do sol + OK + Compartilhar log + Lembre-se de arquivos log do Gadgetbridge que podem conter muitas informações pessoais, incluindo, mas não se limitando a, dados de saúde, identificadores únicos (como o endereço MAC de um dispositivo), preferências de música etc. Considere editar o arquivo e remover essas informações antes de enviá-lo para um relatório público dos problemas. + Atenção! + Roidmi + Roidmi 3 + Cor do LED + Frequência FM + Frequência inválida + Insira uma frequência entre 87.5 e 108.0 + Configurações do gráfico + Frequência cardíaca máxima + Frequência cardíaca mínima + %1$s com bateria baixa + %1$s com bateria baixa: %2$s + Tamanho máximo de linha RTL + Alonga ou encurta as linhas nas quais um texto com escrita da direita para a esquerda (RTL) é separado + Árabe contextual + Habilite isso para ter suporte a árabe contextual + Suporte a RTL (direita para a esquerda) + Configurações de idioma e região + Carência de sono: %1$s + Dormiu demais: %1$s + Carência de passos: %1$d + Passos excedentes: %1$d + Nenhum dado + Frequência cardíaca atual / máx: %1$d / %2$d + Restaurar as configurações de fábrica vai excluir todos os dados do dispositivo conectado (se houver suporte). Dispositivos Xiami/Huami também mudam o endereço MAC do Bluetooth, então eles aparecem como novos dispositivos no Gadgetbrige. + Casio GB-6900 + Filtro de notificação + Para ser configurado, o aplicativo não pode estar na lista negra + Insira as palavras desejadas, nova linha para cada + Filtro de notificação salvo + Não filtrar + Mostrar quando contiver as palavras + Bloquear quando contiver as palavras + Pelo menos uma das palavras + Todas as palavras + Insira pelo menos uma palavra + Modo do filtro + Configuração do modo + Salvar configuração + Não conectado, alarme não definido. + Exercício + Notificação de desconexão + Configurações de Zetime + Configurações de frequência cardíaca + Duração da tela ligada em segundos + Alarme de frequência cardíaca + O relógio vai avisar você quando sua frequência cardíaca exceder os limites. + Ativar o alarme de frequência cardíaca + Frequência cardíaca máxima + Frequência cardíaca mínima + Modo analógico + Apenas mãos + Mãos e passos + Monitoramento de atividade + Ao ativar o monitoramento de atividade, os passos serão contados etc. + Movimento da mão + Gire seu pulso para ativar ou desativar a tela. + Tipo de calorias + Só ativar calorias queimadas + Calorias queimadas ativas e inativas + Formato da hora + 24h + 12h + Formato da data + AA/MM/DD + DD/MM/AA + MM/DD/AA + Repetições + Segunda-feira + Terça-feira + Quarta-feira + Quinta-feira + Sexta-feira + Sábado + Domingo + Definir tipo de sinalização para o alarme + Silêncio + Vibração contínua + Bipe contínuo + Vibração e bipe contínuos + Vibrar uma vez + Vibrar duas vezes + Bipar uma vez + Bipar duas vezes + Vibrar e bipar uma vez + Notificação de ligação perdida + Notificação de calendário + Notificação de inatividade + Aviso de bateria baixa + Aviso contra perda + a cada 15 minutos + a cada 45 minutos + Meta diária: calorias queimadas + Meta diária: distância em metros + Meta diária: tempo ativo em minutos + Mi Scale 2 + Ativar chamadas de aplicativo VoIP + Configurações específicas do dispositivo + Chave de autenticação + Altere a chave de autenticação para um chave comum em todos os dispositivos Android a partir dos quais você gostaria de se conectar. A chave padrão anterior para todos dispositivos é 0123456789@ABCDE + BFH-16 + Você está prestes a instalar o firmware %s no seu Amazfit Cor 2. +\n +\nCertifique-se de instalar o arquivo .fw, e depois disso o arquivo .res. Sua pulseira será reiniciado após a instalação do arquivo .fw. +\n +\nNota: você não precisa instalar .res se for exatamente o mesmo que o instalado anteriormente. +\n +\nPROSSIGA AO SEU PRÓPRIO RISCO! +\n +\nCOMPLETAMENTE NÃO TESTADO, PROVAVELMENTE VOCÊ PRECISA INSTALAR UM FIRMWARE BEATS_W SE O NOME DO SEU DISPOSITIVO FOR \"Amazfit Band 2\" + Holandês + Turco + Ucraniano + Árabe + Indonésio + Tailandês + Vietnamita + Português + Amazfit Cor 2 + Mi Band 4 + Você está prestes a instalar o firmware %s na sua Mi Band 4. +\n +\nCertifique-se de instalar o arquivo .fw e, após, o arquivo .res. Sua pulseira vai reiniciar após instalar o arquivo .fw. +\n +\nNota: Você não tem que instalar .res se ele for exatamente o mesmo arquivo que o instalado anteriormente. +\n +\nPROSSIGA POR SUA CONTA E RISCO! + Alarme de frequência cardíaca durante atividades esportivas + Limite baixo + Limite alto \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 969e75d0c..1ca54b3c7 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -11,33 +11,33 @@ Captura de ecrã Mudar cor do LED Mudar frequência FM - Desligar + Desconectar Apagar dispositivo Apagar %1$s Isto irá apagar o dispositivo e apagar todos os dados associados! Abrir gaveta de navegação Fechar gaveta de navegação - Pressione o cartão longamente para desligar - A desligar - A ligar… + Pressione longo no cartão para desconectar + A desconectar + A conectar… Capturar o ecrã do dispositivo Depurar - Gestor de Aplicações - Apps em cache + Gestor de aplicações + Aplicações em cache Aplicações instaladas Temas instalados Apagar - Apagar e remover do cache + Apagar e remover da cache Reinstalar Procurar na loja Pebble Ativar Desativar - Ativar Mon. Ritmo Cardíaco - Desativar Mon. Ritmo Cardíaco + Ativar monitor de ritmo cardíaco + Desativar monitor de ritmo cardíaco Ativar aplicação de meteorologia do sistema Desativar aplicação de meteorologia do sistema - Instalar notificações da aplicação de meteorologia + Instalar a aplicação de notificação de meteorologia Configurar Mover para o topo @@ -194,7 +194,7 @@ Nome/Apelido Quantidade de vibrações Monitor de sono - Escrever arquivos de registo + Escrever ficheiros de registo Inicializando A Descarregar Dados de Atividade De %1$s até %2$s @@ -217,7 +217,7 @@ Conversas Navegação Rede social - Encontrar dispositivo perdido + Encontrar aparelho perdido Cancele para parar a vibração. A sua atividade Configurar Alarmes @@ -248,7 +248,7 @@ Última carga: %s \n Número de cargas: %s O Seu Sono - Sono na semana + Dormir por semana Sono hoje, objetivo: %1$s Passos na semana A Sua Atividade e Sono @@ -271,7 +271,7 @@ Calorias Distância Relógio - Ritmo Cardíaco + Ritmo cardíaco Bateria Atividade em tempo real Passos hoje, objetivo: %1$s @@ -378,7 +378,7 @@ Deve instalar a versão %1$s antes de instalar este firmware! Notificações de texto = 1.0.1.28 e Mili_pro.ft* instalado.]]> - desligado + Desligado Desligado Automático (deteção de sono) Agendado (intervalo de tempo) @@ -388,8 +388,8 @@ Emparelhar Não emparelhar Doar - Ligar… - Calendários Bloqueados + Conectar… + Calendários bloqueados Está prestes a instalar o firmware %s na sua Amazfit Bip. \n \nPor favor garanta que instala o firmware .fw, o ficheiro .res e finalmente o ficheiro .gps. O seu relógio vai reiniciar depois de instalar o ficheiro .fw . @@ -520,4 +520,15 @@ Ative isto se o seu dispositivo pode mostrar idiomas que se escrevem da direita para a esquerda Comprimento máximo da linha da direita para a esquerda Alonga ou encurta as linhas da direita para a esquerda em que o texto é cortado + Enviar eventos do calendário para linha do tempo + Ativar JS em segundo plano + Quando ativado, permite que as mostradores mostrem o clima, informações da bateria, etc. + Medição diária dos batimentos cardíacos + Contagem de acionamento do botão + Número de toques no botão para enviar a mensagem + Mensagem para enviar + Enviar mensagem após um número definido de toques no botão + Ativar botão + Ativar vibração + Efetuar uma reposição de fábrica eliminará todos os dados do aparelhos ligado (caso seja suportado). Além disso, aparelhos Xiaomi/Huami mudarão os seus endereços MAC, aparecendo como aparelhos novos para o Gadgetbridge. \ 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 5c1dd416e..4d453c2c4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -214,7 +214,7 @@ Активність Швидкий сон Глибокий сон - + Не з\'єднано. Всі будильники вимкнено Лишати дані на пристрої @@ -280,7 +280,7 @@ Значок в рядку стану та сповіщення на екрані блокування відображаються Значок в рядку стану та сповіщення на екрані блокування не відображаються Підтримка застосунків, які відправляють повідомлення Pebble через PebbleKit. - Припинить надсилання небажаних сповіщень у режимі \"Не турбувати\" + Запобігати надсилання небажаних сповіщень у режимі \"Не турбувати\" Транслітерація "Увімкніть цей параметр, якщо пристрій не підтримує шрифт вашої мови " Режим приватності @@ -456,4 +456,22 @@ Введіть хоча б одне слово Режим фільтрації Зберегти конфігурацію + Завжди надавати перевагу BLE + Тривалість роботи екрану + Налаштування HPlus/Makibes + Налаштування ID115 + Налаштування ZeTime + Налаштування серцебиття + Тривалість екрану в секундах + Максимальне серцебиття + Мінімальне серцебиття + Аналоговий режим + Лише вручну + Формат часу + 24г + 12г + Формат дати + РР/ММ/ДД + ДД/ММ/РР + ММ/ДД/РР \ 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 65147f156..123d85ccb 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -41,7 +41,7 @@ 通知黑名单 阻止记录 FW/App 安装器 - 您即将安装固件 %s ,代替当前在小米手环上的固件。 + 您即将安装 %s 。 即将在您的米动手表青春版(Amazfit Bip)上安装固件 %s 。 \n \n请确保安装.fw文件,然后安装.res文件,最后安装.gps文件。 安装.fw文件后,手表将重新启动。 @@ -49,18 +49,18 @@ \n注意:如果这些文件与之前安装的完全一样,则不必安装.res和.gps文件。 \n \n***继续需要您自担风险! - 您即将在您的米动手环(Amazfit Cor)上安装固件 %s。 + 您即将在您的米动手环(Amazfit Cor)上安装固件 %s 。 \n -\n请确保安装了.fw文件,然后安装.rec文件。 安装.fw文件后,手表将重新启动。 +\n请确保安装了 .fw 文件,然后安装 .rec 文件。 安装 .fw 文件后,手表将重新启动。 \n -\n注意:如果这些文件与之前安装的完全一样,则不必安装.res。 +\n注意:如果这些文件与之前安装的完全一样,则不必安装 .res 。 \n \n您需要自行承担风险! 您即将安装固件 %1$s 和 %2$s,代替当前在小米手环上的固件。 该固件已经过测试,并且已知与 Gadgetbridge 兼容。 此固件未经测试,可能与 Gadgetbridge 不兼容。 \n -\n不建议把它安装到你的小米手环! +\n不建议刷入! 如果您仍想继续并且此固件在安装之后能够正常工作,请告知 Gadgetbridge 开发者将固件 %s 列入白名单。 设置 常规设置 @@ -303,7 +303,7 @@ 今天的步数, 目标:%1$s 不发送活动数据 如果活动数据未传输到设备,则不会被清除。 Gadgetbridge 与其他应用程序一起使用时很有用。 - 即使在同步之后, 也会将活动数据保留在小米手环上。如果 Gadgetbridge 与其他应用程序一起使用, 这将非常有用。 + 即使在同步之后,也会将活动数据保留在小米手环上。如果 Gadgetbridge 与其他应用程序一起使用,这将非常有用。 使用低延迟模式进行固件更新 这可能对固件失败的设备有帮助。 步数历史 @@ -326,7 +326,7 @@ 仅时间 时间和日期 按钮操作 - 指定小米手环2按钮上的操作 + 指定小米手环2按钮上的操作 按钮按下计数 触发消息广播的按钮按数 启用按钮操作 @@ -450,7 +450,7 @@ 资源 未知设备 小米手环 - 小米手环2 + 小米手环2 米动手表青春版 米动手环 天气 @@ -486,13 +486,13 @@ 校准设备 添加所有通知到黑名单 添加所有通知到白名单 - 您即将安装小米手环3的固件 %s 。 -\n -\n请确保先安装 .fw 文件,再安装 .res 文件。您的手环将会在安装 .fw 文件后重启。 -\n -\n注意:如果您已经安装相同版本的 .res 文件了,则无需安装。 -\n -\n未经测试,可能会导致您的设备变砖,您必须自行承担风险! + 您即将安装 小米手环3 的固件 %s 。 +\n +\n请确保先安装 .fw 文件,再安装 .res 文件。您的手环将会在安装 .fw 文件后重启。 +\n +\n注意:如果您已经安装相同版本的 .res 文件了,则无需安装。 +\n +\n风险自担! 通知间隔最短时间 从右到左 如果您的设备无法显示从右到左的语言,请选中此选项 @@ -509,9 +509,9 @@ 屏幕解锁时会自动获取。仅在设置了锁定机制才会有效! 刷新最小间隔 每隔 %d 分钟刷新 - 小米手环2 设置 - 小米手环3 设置 - 米动 Cor 设置s + 小米手环2 设置 + 小米手环3 设置 + 米动手环设置s 横向 纵向 当您的手环震动时,晃动设备或者按下按钮。 @@ -570,7 +570,7 @@ 分享 重设刷新日期 GPS 历史 - 小米手环3 + 小米手环3 实时查看 Makibes F68 Exrizu K8 @@ -582,7 +582,7 @@ ID115 Watch 9 睿米 - 睿米3 + 睿米 3 支付宝(快捷方式) 天气(快捷方式) 状态 @@ -688,4 +688,38 @@ 每日目标:距离(米) 每日目标:运动时长(分钟) 小米手环2 + 启用 VoIP 应用呼叫 + 设备特殊设置 + 认证密钥 + 将您想要连接的所有 Android 设备上的认证密钥更改为通用的密钥。默认所有设备的密钥是 0123456789@ABCDE + BFH-16 + 您即将在米动手环2(Amazfit Cor 2)上安装 %s 版本的固件。 +\n +\n请确保先安装 .fw 文件,再安装 .res 文件。您的手环将会在安装了 .fw 文件后重启。 +\n +\n备注:如果您安装的 .res 文件和之前相同,则无需安装 .res 文件。 +\n +\n风险自担! +\n +\n此功能未经测试,如果您的设备名为“Amazfit Band 2”,您可能需要刷入 BEATS_W 固件 + 荷兰语 + 土耳其语 + 乌克兰语 + 阿拉伯语 + 印尼语 + 泰语 + 越南语 + 葡萄牙语 + 米动手环2 + 小米手环4 + 您即将在您的的小米手环4上安装 %s 固件。 +\n +\n请确保您先安装 .fw 文件,再安装 .res 文件。您的手环在安装 .fw 文件后将会重启。 +\n +\n备注:若您的 .res 文件版本和之前安装的相同,则没有必要安装 .res 文件。 +\n +\n风险自担! + 运动期间的心率警报 + 下限 + 上限 \ 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 76dcb65bc..c86d9c54f 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -13,6 +13,7 @@ System Default + Català Čeština Deutsch English @@ -31,6 +32,7 @@ default + ca cs de en @@ -148,6 +150,18 @@ @string/p_dateformat_datetime + + dd.MM.yyyy + dd/MM/yyyy + MM/dd/yyyy + + + + dd.MM.yyyy + dd/MM/yyyy + MM/dd/yyyy + + @string/mi2_dnd_off @string/mi2_dnd_automatic @@ -223,6 +237,8 @@ @string/menuitem_more @string/menuitem_status @string/heart_rate + @string/menuitem_timer + @string/menuitem_nfc @@ -232,6 +248,8 @@ @string/p_menuitem_more @string/p_menuitem_status @string/p_heart_rate + @string/p_menuitem_timer + @string/p_menuitem_nfc @@ -350,21 +368,47 @@ - @string/automatic - @string/simplified_chinese - @string/traditional_chinese - @string/english - @string/spanish - @string/russian + @string/automatic + @string/simplified_chinese + @string/traditional_chinese + @string/english + @string/spanish + @string/german + @string/italian + @string/french + @string/turkish + @string/russian - -1 - 0 - 1 - 2 - 3 - 4 + auto + zh_CH + zh_TW + en_US + es_ES + de_DE + it_IT + fr_FR + tr_TR + ru_RU + + + + @string/automatic + @string/simplified_chinese + @string/traditional_chinese + @string/english + @string/spanish + @string/russian + + + + auto + zh_CH + zh_TW + en_US + es_ES + ru_RU @@ -373,11 +417,19 @@ @string/traditional_chinese @string/english @string/spanish - @string/russian @string/german @string/italian @string/french + @string/portuguese + @string/dutch @string/polish + @string/turkish + @string/russian + @string/ukrainian + @string/arabic + @string/indonesian + @string/thai + @string/vietnamese @string/japanese @string/korean @@ -388,11 +440,19 @@ zh_TW en_US es_ES - ru_RU de_DE it_IT fr_FR + pt_PT + nl_NL pl_PL + tr_TR + ru_RU + uk_UA + ar_SA + id_ID + th_TH + vi_VN ja_JP ko_KO diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5172281d4..259d1c9a2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,13 +53,15 @@ Blacklisted Calendars FW/App installer - You are about to install the %s firmware instead of the one currently on your Mi Band. + You are about to install the %s. You are about to install the %s firmware on your Amazfit Bip.\n\nPlease make sure to install the .fw file, then the .res file, and finally the .gps file. Your watch will reboot after installing the .fw file.\n\nNote: You do not have to install .res and .gps if these files are exactly the same as the ones previously installed.\n\nPROCEED AT YOUR OWN RISK! - You are about to install the %s firmware on your Amazfit Cor.\n\nPlease make sure to install the .fw file, and after that the .res file. Your watch will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK! - You are about to install the %s firmware on your Mi Band 3.\n\nPlease make sure to install the .fw file, and after that the .res file. Your watch will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nUNTESTED, MAY BRICK YOUR DEVICE, PROCEED AT YOUR OWN RISK! + You are about to install the %s firmware on your Amazfit Cor.\n\nPlease make sure to install the .fw file, and after that the .res file. Your band will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK! + You are about to install the %s firmware on your Amazfit Cor 2.\n\nPlease make sure to install the .fw file, and after that the .res file. Your band will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK!\n\nCOMPLETELY UNTESTED, PROBABLY YOU NEED TO FLASH A BEATS_W FIRMWARE IF YOUR DEVICE NAME is \"Amazfit Band 2\!" + You are about to install the %s firmware on your Mi Band 3.\n\nPlease make sure to install the .fw file, and after that the .res file. Your band will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK! + You are about to install the %s firmware on your Mi Band 4.\n\nPlease make sure to install the .fw file, and after that the .res file. Your band will reboot after installing the .fw file.\n\nNote: You do not have to install .res if it is exactly the same as the one previously installed.\n\nPROCEED AT YOUR OWN RISK! You are about to install the %1$s and %2$s firmware, instead of the ones currently on your Mi Band. This firmware has been tested and is known to be compatible with Gadgetbridge. - "This firmware is untested and may not be compatible with Gadgetbridge.\n\nYou are DISCOURAGED from flashing it onto your Mi Band!" + "This firmware is untested and may not be compatible with Gadgetbridge.\n\nYou are DISCOURAGED from flashing it!" If you still want to proceed and things continue to work properly afterwards, please tell the Gadgetbridge developers to whitelist the %s firmware version. Settings @@ -69,7 +71,6 @@ Reconnect automatically Preferred Audioplayer Default - Enable left/right swipe in the charts activity Date and Time Sync time Sync time to Gadgetbridge device when connecting, and when time or time zone changes on Android device @@ -219,6 +220,15 @@ Beep twice Vibrate and beep once + + Device specific settings + Auth Key + Change the auth key to a common key on all your Android devices from which you would like to connect from. The previous default key for all devices is 0123456789@ABCDE + + Heart rate alarm during sports activity + Low limit + High limit + Auto export Auto export enabled @@ -405,6 +415,7 @@ Steps today, target: %1$s Lack of steps: %1$d Overstep: %1$d + Average: %1$s Do not ACK activity data transfer If the activity data are not acked to the band, they will not be cleared. Useful if GB is used together with other apps. Will keep activity data on the Mi Band even after synchronization. Useful if GB is used together with other apps. @@ -480,6 +491,14 @@ Polish Korean Japanese + Dutch + Turkish + Ukrainian + Arabic + Indonesian + Thai + Vietnamese + Portuguese About to transfer data since %1$s Waiting for reconnect About you @@ -487,9 +506,20 @@ Gender Height in cm Weight in kg + + + Charts Settings + Enable left/right swipe in the charts activity + Show averages in the charts Chart settings Max heart rate Min heart rate + Charts Range + Charts range is set to a Month + Charts range is set to a Week + Steps per month + Sleep per month + Authenticating Authentication required Zzz @@ -606,8 +636,10 @@ Mi Band Mi Band 2 Mi Band 3 + Mi Band 4 Amazfit Bip Amazfit Cor + Amazfit Cor 2 Vibratissimo LiveView HPlus @@ -626,6 +658,8 @@ Roidmi 3 Casio GB-6900 Mi Scale 2 + BFH-16 + Mijia Smart Clock Choose export location Gadgetbridge notifications @@ -642,6 +676,7 @@ Alipay Music More + NFC Minutes: Hours: Seconds: diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml index 570783715..a9170a503 100644 --- a/app/src/main/res/values/values.xml +++ b/app/src/main/res/values/values.xml @@ -32,6 +32,7 @@ notifications music more + nfc off on diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 7fe4929a7..bb136ae81 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,12 +1,53 @@ + + Mi Band 1/2: Crash when updating firmware while phone is set to Spanish + Mi Band 4: Enable music info support (displays now on the band) + Mi Band 4: Support setting date format (for built-in watchfaces) + Amazfit Cor 2: Try to fix empty menu on device + + + Mi Band 4: Support flashing watchfaces, res and firmware (fonts untested) + + + Mi Band 4: Initial support (WARNING: INITIAL SETUP NEEDS MI FIT WITH ACCOUNT AND ROOT, NOT A RECOMMENDED DEVICE FOR GADGETBRIDGE) + + + Mi Band 1: Fix crash when entering per-device settings + Mi Band 3: Allow setting date format in per-device settings + ZeTime: Fix timestmaps + Fix a crash when flashing an non-whitelisted firmware while using Gadgetbridge in Spanish + + + Mi Band 1/2/3/Bip/Cor: Migrate many settings to per-device settings (new settings icon in device card in main activity) + Mi Band 3: Fix setting menu items with 2.4 firmware and add support for the new timer menu + Amazfit Bip/Cor, Casio: Add support for muting incoming calls + ZeTime: Remove endless recursion in ZeTime settings + Recognize FairEmail notifications as generic email notifications + + + Mi Band 3: Recognize "Xiaomi Band 3" + Amazfit Bip: Add German, Italian, French and Turkish to language settings + + + BFH-16: Initial support + Mi Band 2/3/Bip/Cor: Generate random per-device security keys when pairing, allow manual override to still support multiple android devices connecting to the same device + Mi Band 3: Add Indonesian, Thai, Arabic, Vietnamese, Portuguese, Dutch, Turkish and Ukrainian to language settings + Mi Band 3: Support flashing latest Japanese-Korean font + Amazfit Cor 2: Initial experimental support (untested) + Pebble: Add pebblekit extension for reopening last app + Casio: Bugfixes and improvements + Lookup contacts also in work profile + Fix searching in application name when blacklisting + Remove misleading title from database management activity when no legacy database is available + - Make voip call support optional (disabled by default) - Amazfit Bip: GPX export corrections - ZeTime: Fix setting alarms - ZeTime: Fix wrong activity timestamps - ZeTime: Set HR alarm limits when changed, not only on connect - ZeTime: Sync preferences from the watch to Gadgetbridge settings + Make voip call support optional (disabled by default) + Amazfit Bip: GPX export corrections + ZeTime: Fix setting alarms + ZeTime: Fix wrong activity timestamps + ZeTime: Set HR alarm limits when changed, not only on connect + ZeTime: Sync preferences from the watch to Gadgetbridge settings Fix a crash in charts due to a broken German translation diff --git a/app/src/main/res/xml/devicesettings_amazfitbip.xml b/app/src/main/res/xml/devicesettings_amazfitbip.xml new file mode 100644 index 000000000..d2133e176 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_amazfitbip.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_amazfitcor.xml b/app/src/main/res/xml/devicesettings_amazfitcor.xml new file mode 100644 index 000000000..87b0f2a11 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_amazfitcor.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_dateformat.xml b/app/src/main/res/xml/devicesettings_dateformat.xml new file mode 100644 index 000000000..ce29ed05e --- /dev/null +++ b/app/src/main/res/xml/devicesettings_dateformat.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_disconnectnotification.xml b/app/src/main/res/xml/devicesettings_disconnectnotification.xml new file mode 100644 index 000000000..4ac7855e6 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_disconnectnotification.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_donotdisturb_withauto.xml b/app/src/main/res/xml/devicesettings_donotdisturb_withauto.xml new file mode 100644 index 000000000..13881caf7 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_donotdisturb_withauto.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_fake_timeoffset.xml b/app/src/main/res/xml/devicesettings_fake_timeoffset.xml new file mode 100644 index 000000000..7f603d6f5 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_fake_timeoffset.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_liftwrist_display.xml b/app/src/main/res/xml/devicesettings_liftwrist_display.xml new file mode 100644 index 000000000..c703c8eac --- /dev/null +++ b/app/src/main/res/xml/devicesettings_liftwrist_display.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_lowlatency_fwupdate.xml b/app/src/main/res/xml/devicesettings_lowlatency_fwupdate.xml new file mode 100644 index 000000000..93375f42d --- /dev/null +++ b/app/src/main/res/xml/devicesettings_lowlatency_fwupdate.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_miband2.xml b/app/src/main/res/xml/devicesettings_miband2.xml new file mode 100644 index 000000000..6a4c7cec5 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_miband2.xml @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_miband3.xml b/app/src/main/res/xml/devicesettings_miband3.xml new file mode 100644 index 000000000..7ff8378e8 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_miband3.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_nightmode.xml b/app/src/main/res/xml/devicesettings_nightmode.xml new file mode 100644 index 000000000..e59577e91 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_nightmode.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_pairingkey.xml b/app/src/main/res/xml/devicesettings_pairingkey.xml new file mode 100644 index 000000000..422a5c619 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_pairingkey.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/xml/devicesettings_rotatewrist_cycleinfo.xml b/app/src/main/res/xml/devicesettings_rotatewrist_cycleinfo.xml new file mode 100644 index 000000000..71b46fa7b --- /dev/null +++ b/app/src/main/res/xml/devicesettings_rotatewrist_cycleinfo.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_swipeunlock.xml b/app/src/main/res/xml/devicesettings_swipeunlock.xml new file mode 100644 index 000000000..d02b9d402 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_swipeunlock.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 5a8b262f1..f0859df44 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -106,44 +106,6 @@ android:summary="%s" android:title="@string/prefs_title_heartrate_measurement_interval" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -495,10 +426,5 @@ android:title="@string/pref_title_keep_data_on_device" android:summary="@string/pref_summary_keep_data_on_device" android:defaultValue="false" /> - \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 68243a67e..7eb6e91b9 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -157,6 +157,7 @@ android:maxLength="3" android:title="@string/activity_prefs_activetime_minutes" /> + @@ -174,13 +175,26 @@ android:maxLength="3" android:defaultValue="10" android:title="@string/activity_prefs_chart_min_heart_rate" /> - - + + + + + @@ -271,143 +285,7 @@ android:icon="@drawable/ic_device_miband" android:key="pref_key_miband" android:title="@string/preferences_miband_settings" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + 1); - try (InputStream in2 = getContents(ActivityDetailsParserTest.class.getClassLoader().getResource("ActivityDetailsDump1.txt"))) { + try (InputStream in2 = getContents(HuamiActivityDetailsParserTest.class.getClassLoader().getResource("ActivityDetailsDump1.txt"))) { binString = FileUtils.readAll(in2, 1024 * 1024); assertTrue(binString.length > 1); } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ActivityDetailsParserTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/HuamiActivityDetailsParserTest.java similarity index 90% rename from app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ActivityDetailsParserTest.java rename to app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/HuamiActivityDetailsParserTest.java index 2730dc464..841f40338 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/ActivityDetailsParserTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/HuamiActivityDetailsParserTest.java @@ -17,15 +17,15 @@ import nodomain.freeyourgadget.gadgetbridge.export.GPXExporter; import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint; import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.ActivityDetailsParser; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiActivityDetailsParser; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -public class ActivityDetailsParserTest extends TestBase { - private static final URL DETAILS_1 = ActivityDetailsParserTest.class.getClassLoader().getResource("ActivityDetailsDump1.txt"); +public class HuamiActivityDetailsParserTest extends TestBase { + private static final URL DETAILS_1 = HuamiActivityDetailsParserTest.class.getClassLoader().getResource("ActivityDetailsDump1.txt"); private static final long MAX_DETAILS = 1024 * 1024; private static Date baseTime; @@ -38,7 +38,7 @@ public class ActivityDetailsParserTest extends TestBase { public void testActivityDetails() throws Exception { BipActivitySummary summary = createSummary(); - ActivityDetailsParser parser = new ActivityDetailsParser(summary); + HuamiActivityDetailsParser parser = new HuamiActivityDetailsParser(summary); parser.setSkipCounterByte(true); try (InputStream in = getContents(DETAILS_1)) { ActivityTrack track = parser.parse(FileUtils.readAll(in, MAX_DETAILS)); @@ -79,7 +79,7 @@ public class ActivityDetailsParserTest extends TestBase { summary.setBaseLatitude(baseLati); summary.setBaseAltitude(baseAlti); - ActivityDetailsParser parser = new ActivityDetailsParser(summary); + HuamiActivityDetailsParser parser = new HuamiActivityDetailsParser(summary); parser.setSkipCounterByte(true); try (InputStream in = getContents(DETAILS_1)) { ActivityTrack track = parser.parse(FileUtils.readAll(in, MAX_DETAILS)); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/TestBase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/TestBase.java index 636f1077c..ce9d975a8 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/TestBase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/TestBase.java @@ -33,7 +33,7 @@ import static org.junit.Assert.assertNotNull; * directory. */ @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 19) +@Config(sdk = 19) // need sdk 19 because "WITHOUT ROWID" is not supported in robolectric/sqlite4java public abstract class TestBase { protected static File logFilesDir; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java index 9aca15ed7..b149f3995 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/Tryout.java @@ -33,7 +33,7 @@ public class Tryout extends TestBase { @Test public void testCalendarBytes() { GregorianCalendar calendar = MiBandDateConverter.createCalendar(); - byte[] bytes = MiBandDateConverter.calendarToRawBytes(calendar); + byte[] bytes = MiBandDateConverter.calendarToRawBytes(calendar,"fake"); LOG.info("Calender: " + DateTimeUtils.formatDateTime(calendar.getTime())); Logging.logBytes(LOG, bytes); } diff --git a/build.gradle b/build.gradle index ef1b8035a..a7772d912 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0' + classpath 'com.android.tools.build:gradle:3.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/fastlane/metadata/android/en-US/changelogs/148.txt b/fastlane/metadata/android/en-US/changelogs/148.txt new file mode 100644 index 000000000..f64539061 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/148.txt @@ -0,0 +1,10 @@ +* BFH-16: Initial support +* Mi Band 2/3/Bip/Cor: Generate random per-device security keys when pairing, allow manual override to still support multiple android devices connecting to the same device +* Mi Band 3: Add Indonesian, Thai, Arabic, Vietnamese, Portuguese, Dutch, Turkish and Ukrainian to language settings +* Mi Band 3: Support flashing latest Japanese-Korean font +* Amazfit Cor 2: Initial experimental support (untested) +* Pebble: Add pebblekit extension for reopening last app +* Casio: Bugfixes and improvements +* Lookup contacts also in work profile +* Fix searching in application name when blacklisting +* Remove misleading title from database management activity when no legacy database is available diff --git a/fastlane/metadata/android/en-US/changelogs/149.txt b/fastlane/metadata/android/en-US/changelogs/149.txt new file mode 100644 index 000000000..c98a000d4 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/149.txt @@ -0,0 +1,2 @@ +* Mi Band 3: Recognize "Xiaomi Band 3" +* Amazfit Bip: Add German, Italian, French and Turkish to language settings diff --git a/fastlane/metadata/android/en-US/changelogs/150.txt b/fastlane/metadata/android/en-US/changelogs/150.txt new file mode 100644 index 000000000..8ff035b94 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/150.txt @@ -0,0 +1,5 @@ +* Mi Band 1/2/3/Bip/Cor: Migrate many settings to per-device settings (new settings icon in device card in main activity) +* Mi Band 3: Fix setting menu items with 2.4 firmware and add support for the new timer menu +* Amazfit Bip/Cor, Casio: Add support for muting incoming calls +* ZeTime: Remove endless recursion in ZeTime settings +* Recognize FairEmail notifications as generic email notifications diff --git a/fastlane/metadata/android/en-US/changelogs/151.txt b/fastlane/metadata/android/en-US/changelogs/151.txt new file mode 100644 index 000000000..54730f2b0 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/151.txt @@ -0,0 +1,4 @@ +* Mi Band 1: Fix crash when entering per-device settings +* Mi Band 3: Allow setting date format in per-device settings +* ZeTime: Fix timestmaps +* Fix a crash when flashing an non-whitelisted firmware while using Gadgetbridge in Spanish diff --git a/fastlane/metadata/android/en-US/changelogs/152.txt b/fastlane/metadata/android/en-US/changelogs/152.txt new file mode 100644 index 000000000..6fc4ccbd6 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/152.txt @@ -0,0 +1 @@ +* Mi Band 4: Initial support (WARNING: INITIAL SETUP NEEDS MI FIT WITH ACCOUNT AND ROOT, NOT A RECOMMENDED DEVICE FOR GADGETBRIDGE) diff --git a/fastlane/metadata/android/en-US/changelogs/153.txt b/fastlane/metadata/android/en-US/changelogs/153.txt new file mode 100644 index 000000000..bfd64ad46 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/153.txt @@ -0,0 +1 @@ +* Mi Band 4: Support flashing watchfaces, res and firmware (.ft untested) diff --git a/fastlane/metadata/android/en-US/changelogs/154.txt b/fastlane/metadata/android/en-US/changelogs/154.txt new file mode 100644 index 000000000..c78886996 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/154.txt @@ -0,0 +1,5 @@ +#### Version 0.35.2 +* Mi Band 1/2: Crash when updating firmware while phone is set to Spanish +* Mi Band 4: Enable music info support (displays now on the band) +* Mi Band 4: Support setting date format (for built-in watchfaces) +* Amazfit Cor 2: Try to fix empty menu on device diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png new file mode 100644 index 000000000..4bfd41fb8 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1-MainScreen.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png new file mode 100644 index 000000000..4b30f2e57 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2-ActivityAndSleep.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png new file mode 100644 index 000000000..e11cc01d0 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3-SleepPerWeek.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png new file mode 100644 index 000000000..452a931a1 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4-StepsPerWeek.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png new file mode 100644 index 000000000..8a4e30673 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5-SpeedZones.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6-LiveActivity.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6-LiveActivity.png new file mode 100644 index 000000000..bfcc71aa2 Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6-LiveActivity.png differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9ac47aa1c..a9d649271 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Thu Apr 18 19:22:52 CEST 2019 +#Thu May 23 21:50:12 CEST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME