From 5b016e25776ff52c872d549b58179b12d21fd9bb Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 29 Jan 2016 17:45:35 +0100 Subject: [PATCH 001/274] WIP, read the miband user information for the time being. --- .../devices/pebble/PebbleProtocol.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) 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 58667332..612ff073 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 @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.util.Base64; import android.util.Pair; @@ -12,6 +14,7 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -29,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificati import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; @@ -680,7 +684,34 @@ public class PebbleProtocol extends GBDeviceProtocol { byte command; command = BLOBDB_INSERT; if (activate) { - blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}; + + ByteBuffer buf = ByteBuffer.allocate(9); + buf.order(ByteOrder.LITTLE_ENDIAN); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + + Integer heightMm = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_HEIGHT_CM, "175")) * 10; + buf.putShort(heightMm.shortValue()); + Integer weigthDag = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_WEIGHT_KG, "70")) * 100; + buf.putShort(weigthDag.shortValue()); + buf.put((byte)0x01); //activate tracking + buf.put((byte)0x01); //activity Insights + buf.put((byte)0x01); //sleep Insights + int userYear = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_YEAR_OF_BIRTH, "0")); + int age = 25; + if (userYear > 1900) { + age = Calendar.getInstance().get(Calendar.YEAR) - userYear; + if (age <= 0) { + age = 25; + } + } + buf.put((byte)age); + + int gender = ("male".equals(prefs.getString(MiBandConst.PREF_USER_GENDER, null)) ? 1 : 0); + buf.put((byte)gender); + //blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}; + + blob = buf.array(); } else { blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } From cc64bcf03c27e127134a4d3cfe15b17021178cf6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 31 Jan 2016 00:56:12 +0100 Subject: [PATCH 002/274] updated Korean and French from transifex (thanks!) --- app/src/main/res/values-fr/strings.xml | 162 ++++++++++++++----------- app/src/main/res/values-ko/strings.xml | 4 + 2 files changed, 93 insertions(+), 73 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9e56849d..88e12c03 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -2,107 +2,116 @@ Gadgetbridge Gadgetbridge - Paramètre - Débugger + Paramètres + Déboguer Quitter Synchroniser Moniteur de sommeil (ALPHA) Trouver l\'appareil Prendre une capture d\'écran Déconnexion - Débugger + Déboguer Gestionnaire d\'application Supprimer + Liste noire de notifications - Vous êtes sur le point d\'installer le firmware %s à la place de celui qui est actuellement sur votre Mi Band. - Ce firmware a été testé et est connu pour être compatible avec Gadgetbridge. - Ce firmware n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge. \n \n Il n\'est pas conseillé de flasher votre Mi Band. + Installateur d\'applications/micrologiciel + Vous êtes sur le point d\'installer le micrologiciel %s à la place de celui qui est 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 \n Il n\'est pas conseillé de flasher votre Mi Band. + Si vous désirez continuer et que tout fonctionne correctement par la suite, SVP informez-en les développeurs de Gadgetbridge pour demander l\'ajout de ce micrologiciel à leur liste: %s - Paramètre + Paramètres Paramètres généraux - Connecter votre appareil quand le Bluetooth est mise en marche + Connecter votre appareil quand le Bluetooth est mis en marche Lecteur audio préféré + Par défaut Date et heure - Synchroniser l\'horloge - Synchroniser l\'horloge lors de la connexion et quand l\'heure ou lorsque le fuseau horaire change sur Android + Synchroniser l\'heure + Synchroniser l\'heure à l\'appareil lors de la connexion et lorsque l\'heure ou le fuseau horaire changent sur Android Notifications Répétitions Appels téléphoniques - SMS - K9-Email - Message Pebble + Textos + K9-Mail + Messages Pebble Support pour les applications qui envoient des notifications au Pebble via Intent. Peut être utilisé pour les conversations. Support des notififactions génériques - Même lorsque l\'écran est allumé - Toujours - Quand l\'écran est éteint - Jamais + ... y compris lorsque l\'écran est allumé + toujours + quand l\'écran est éteint + jamais + Apps bloquées + Modèles de réponses + Suffixe commun Options développeur Adresse Mi Band Paramètres Pebble + Permettre l\'accès d\'applications tierces Android Activer le support expérimental pour les applications Android utilisant PebbleKit Protocole des notifications en vigueur - Cette option force l\'utilisation du dernier protocole de notification qui dépend de la verrsion du firmware. ACTIVER LA UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! - Activer les fonctionnalités non testé - Activer les fonctionnalités non testés. ACTIVER UNIQUEMENT SI VOUS SAVEZ CE QE VOUS FAITES! - Non connecté - En train de se connecter - Connecté - État inconnu + Cette option force l\'utilisation du plus récent protocole de notification qui dépend de la version du micrologiciel. ACTIVEZ-LA UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! + Activer les fonctionnalités non-testées + Activer les fonctionnalités non-testées. ACTIVEZ UNIQUEMENT SI VOUS SAVEZ CE QUE VOUS FAITES! + Tentatives de reconnexion + non connecté + connexion en cours + connecté + état inconnu HW: %1$s FW: %2$s FW: %1$s (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é - Taper sur l\'appareil pour le connecter au gestionnaire d\'application - Tapper sur l\'appareil pour le connecter - Ne peut être connecter. L’adresse bluetooth est invalide? + Le Bluetooth n\'est pas supporté. + Le Bluetooth est désactivé. + tappez sur l\'appareil pour le connecter au gestionnaire d\'application + tappez sur l\'appareil pour le connecter + Connexion impossible. L’adresse Bluetooth est-elle invalide? Gadgetbridge est en fonctionnement Installation du binaire %1$d/%2$d - Echec d\'installation + échec d\'installation! Installation réalisé - VOUS ÊTES EN TRAIN D\'INSTALLER UN FIRMWARE, PROCEDEZ À VOS PROPRES RISQUES. CE FIRMWARE EST POUR LA VERSION HW: %s + VOUS TENTEZ D\'INSTALLER UN MICROLOGICIEL, PROCÉDEZ À VOS PROPRES RISQUES.\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/A + N.D. Initialisé %1$s par %2$s - Décourir les appareils - Arreter de scanner - Démarrer le scan - Connecter un nouvelle appareil + Découvrir les appareils + Arrêter le balayage + Démarrer le balayage + Connecter un nouvel appareil %1$s (%2$s) Coupler l\'appareil - Utiliser l\'appareillage bluetouth d\'android pour coupler l\'appareil + Utiliser le couplement Bluetooth d\'Android pour coupler l\'appareil Coupler votre Mi Band Coupler avec %s... - Aucune adresse mac fournis, ne peut être couplé - Paramètres spécifique à l\'appareil + Aucune adresse mac fournie, ne peut être couplé + Paramètres spécifiques à l\'appareil Paramètres Mi Band Homme Femme Autre Gauche Droite - Aucune donnée utilisateur valide fournis, utilisation des données fictives pour le moment + Aucune donnée utilisateur valide fournie, utilisation des données fictives pour le moment Quand votre Mi Band vibre et clignote, appuyez dessus plusieurs fois d\'affilée. Installer - Rendre votre appareil découvrable. Actuellement les appareils connectés ne seront pas découvert. + Rendez votre appareil découvrable. Les appareils déjà connectés ne seront pas découverts. Note: Image de l\'appareil - A propos de vous + À propos de vous Nom/Pseudo Année de naissance Genre Taille en cm Poids en kg - Nombre de vibration + Nombre de vibrations Moniteur de sommeil - Ecrire le fichier de logs (besoin de redemarrer) + Écrire les fichiers journaux (redémarrage requis) Initialisation Récupération des données d\'activité De %1$s à %2$s @@ -113,20 +122,20 @@ Moyen Long Goute d\'eau - Cycle + Sonnette Réveil Vibration - Notification SMS + Notification Texto Paramètres des vibrations - Notification génériques + Notification générique Notification Pebble - Notification des emails K9 - Notification des appels entrant + Notification K9 Mail + Notification d\'appels entrants Trouver l\'appareil perdu - Annuler pour arreter les vibrations + Annuler pour arrêter les vibrations Votre activité - Configurer les réveils - Configurer les réveils + Configurer les alarmes + Configurer les alarmes Détails des alarmes Dim Lun @@ -136,54 +145,61 @@ Ven Sam Réveil intelligent - Il y avais une erreur lors du paramétrage des alarmes, s\'il vous plaît essayer à nouveau! - Alarmes envoyer à l\'appareil + 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? - À propos du transférer %1$s de données à partir de %2$s + Sur le point de transférer %1$s de données à partir de %2$s Objectif de pas par jour - Erreur d’exécution %1$s\' + 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 firmware donnée: il ne correspond pas à la version du matériel de votre Pebble. - S\'il vous plait patientez pendant la détermination du status de l\'installation... - Gadget batterie Vide! + Impossible d\'installer le micrologiciel spécifié: il ne correspond pas à la version du matériel de votre Pebble. + S\'il vous plait patientez 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 Pas de la semaine Votre activité et sommeil - Mise à jour du Firmware... + Mise à jour du micrologiciel... Le fichier ne peut pas être installé, l\'appareil n\'est pas prêt. - Firmware Mi Band %1$s + Micrologiciel Mi Band %1$s Version compatible - Version non testé! + Version non-testée! Connexion à l\'appareil: %1$s - Firmware Pebble %1$s - Révision correcte du matériel - Version Hardware incorrecte! + Micrologiciel Pebble %1$s + Version du matériel correcte + Version du matériel incorrecte! %1$s (%2$s) - Problème avec le transfert du firmware. Ne redémarrez pas votre Mi band! - Problème avec le transfert de métadonnées du firmware - Installation complète du firmware - Installation complète du firmware, redémarrage de l\'appareil - Échec lors de l\'écriture du firmware + 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\'écriture du micrologiciel Pas Activité en direct Nombre de pas aujourd\'hui, objectif: %1$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. + Ne pas confirmer le transfert de données d\'activités + Historique de pas + Pas/minute actuel Nombre total de pas + Historique de pas/minute Démarrer votre activité Activité Sommeil léger Sommeil profond Non porté Non connecté. + Toutes alarmes désactivées Conserver les activités sur l\'appareil - Firmware non compatible - Ce firmware n\'est pas compatible avec l\'appareil + Micrologiciel non compatible + Ce micrologiciel n\'est pas compatible avec l\'appareil Alarmes à réserver pour événements futurs - Attente de reconnexion + en attente de reconnexion + Réinstaller diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index cbbcfaf6..e3317713 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -44,6 +44,7 @@ 화면이 꺼져 있을 때 하지 않음 블랙리스트 앱 + 일반적인 접미사 개발자 옵션 Mi Band 주소 Pebble 설정 @@ -197,4 +198,7 @@ 기기에 활동 데이터 유지 호환되지 않는 펌웨어 이 펌웨어는 기기와 호환되지 않습니다 + 다가오는 이벤트를 위해 예약할 알람 + 재접속을 기다리는 중 + 재설치 From 60c7e9f6f68d32aec9b050c6d9daa24ab9db7b71 Mon Sep 17 00:00:00 2001 From: Chris Perelstein Date: Mon, 1 Feb 2016 22:05:49 -0500 Subject: [PATCH 003/274] Fix misspelling in exception notification. --- .../freeyourgadget/gadgetbridge/externalevents/K9Receiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index 1949b1ac..27b7132c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -62,7 +62,7 @@ public class K9Receiver extends BroadcastReceiver { e.printStackTrace(); notificationSpec.sender = "Gadgetbridge"; notificationSpec.subject = "Permission Error?"; - notificationSpec.body = "Please reinstall Gadgerbridge to enable K-9 Mail notifications"; + notificationSpec.body = "Please reinstall Gadgetbridge to enable K-9 Mail notifications"; } try { From 94c8633bad83aa7393473bbf600888a5d301446c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 2 Feb 2016 14:32:19 +0100 Subject: [PATCH 004/274] Move the generic user info outside the miband preferences activity. They still have to be renamed. --- .../activities/SettingsActivity.java | 9 ++++++ .../miband/MiBandPreferencesActivity.java | 4 --- app/src/main/res/values-de/strings.xml | 10 +++--- app/src/main/res/values-es/strings.xml | 10 +++--- app/src/main/res/values-fr/strings.xml | 10 +++--- app/src/main/res/values-it/strings.xml | 10 +++--- app/src/main/res/values-ja/strings.xml | 10 +++--- app/src/main/res/values-ko/strings.xml | 10 +++--- app/src/main/res/values-pl/strings.xml | 10 +++--- app/src/main/res/values-ru/strings.xml | 10 +++--- app/src/main/res/values-tr/strings.xml | 10 +++--- app/src/main/res/values-uk/strings.xml | 10 +++--- app/src/main/res/values-vi/strings.xml | 10 +++--- app/src/main/res/values/strings.xml | 12 ++++--- app/src/main/res/xml/miband_preferences.xml | 29 ++--------------- app/src/main/res/xml/preferences.xml | 31 +++++++++++++++++++ 16 files changed, 104 insertions(+), 91 deletions(-) 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 5150f082..7ab5d03f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -13,6 +13,11 @@ import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_GENDER; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_HEIGHT_CM; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_WEIGHT_KG; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_YEAR_OF_BIRTH; + public class SettingsActivity extends AbstractSettingsActivity { @Override protected void onCreate(Bundle savedInstanceState) { @@ -127,6 +132,10 @@ public class SettingsActivity extends AbstractSettingsActivity { "canned_reply_14", "canned_reply_15", "canned_reply_16", + PREF_USER_YEAR_OF_BIRTH, + PREF_USER_GENDER, + PREF_USER_HEIGHT_CM, + PREF_USER_WEIGHT_KG, }; } 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 62a10526..6f469e34 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 @@ -53,10 +53,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { protected String[] getPreferenceKeysWithSummary() { return new String[]{ PREF_USER_ALIAS, - PREF_USER_YEAR_OF_BIRTH, - PREF_USER_GENDER, - PREF_USER_HEIGHT_CM, - PREF_USER_WEIGHT_KG, PREF_MIBAND_WEARSIDE, PREF_MIBAND_ADDRESS, PREF_MIBAND_FITNESS_GOAL, diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5b57bb31..8426034e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -102,12 +102,12 @@ Mach Dein Gerät auffindbar. Derzeit verbundene Geräte sind in der Regel nicht auffindbar. Tipp: Bild des Geräts - Über Dich + Über Dich Name/Alias - Geburtsjahr - Geschlecht - Größe in cm - Gewicht in kg + Geburtsjahr + Geschlecht + Größe in cm + Gewicht in kg Anzahl der Vibrationen Schlafmonitor Log-Dateien schreiben (Neustart erforderlich) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index ec3a8c8a..9fe44650 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -103,12 +103,12 @@ Haz visible tu dispositivo. No es probable que se detecten los dispositivos que ya están conectados. Nota: Imagen del dispositivo - Sobre ti + Sobre ti Nombre/Apodo - Año de nacimiento - Sexo - Altura en cm - Peso en kg + Año de nacimiento + Sexo + Altura en cm + Peso en kg Número de vibraciones Monitor de sueño Guardar logs (requiere reiniciar) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 88e12c03..de149c54 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -103,12 +103,12 @@ Rendez votre appareil découvrable. Les appareils déjà connectés ne seront pas découverts. Note: Image de l\'appareil - À propos de vous + À propos de vous Nom/Pseudo - Année de naissance - Genre - Taille en cm - Poids en kg + Année de naissance + Genre + Taille en cm + Poids en kg Nombre de vibrations Moniteur de sommeil Écrire les fichiers journaux (redémarrage requis) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index aff24692..79f253d8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -95,12 +95,12 @@ Imposta il tuo dispositivo perchè sia rilevabile. I dispositivi attualmente connessi non saranno probabilmente rilevati. Nota: Immagine dispositivo - Informazioni sull\'utilizzatore + Informazioni sull\'utilizzatore Nome / Soprannome - Anno di nascita - Genere - Altezza in cm - Peso in kg + Anno di nascita + Genere + Altezza in cm + Peso in kg Numero vibrazioni Monitoraggio del sonno Salva il log su file (richiede riavvio) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 653dae86..dda3ea21 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -103,12 +103,12 @@ お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。 注: デバイスイメージ - あなたについて + あなたについて 名前/別名 - 誕生年 - 性別 - 身長(cm) - 体重(kg) + 誕生年 + 性別 + 身長(cm) + 体重(kg) バイブレーション回数 睡眠観測 ログファイルを出力 (再起動が必要) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e3317713..258105d3 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -102,12 +102,12 @@ 기기를 발견 가능하도록 설정하세요. 현재 연결된 기기들은 발견될 수 없습니다. 알림: 기기 이미지 - 당신에 대해 + 당신에 대해 이름/별명 - 출생년도 - 성별 - 키 (cm) - 몸무게 (kg) + 출생년도 + 성별 + 키 (cm) + 몸무게 (kg) 진동 횟수 수면 측정계 기록 파일 작성 (재시작 필요) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index e909702f..9d21e263 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -102,12 +102,12 @@ Uwidocznij swoje urządzenie. Aktualnie połączone urządzenia prawdopodobnie nie będą znalezione. Uwaga Obraz urządzenia - O tobie + O tobie Nazwisko/Pseudonim - Data urodzenia - Płeć - Wzrost w cm - Waga w kg + Data urodzenia + Płeć + Wzrost w cm + Waga w kg Liczba wibracji Monitor snu Zapisuj logi (wymaga restartu) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3eed8f85..6efeeb00 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -103,12 +103,12 @@ Подключённые в настоящее время устройства, скорее всего, не будут обнаружены. Заметка: Изображение устройства - Ваши данные + Ваши данные Имя/псевдоним - Год рождения - Пол - Рост в см - Вес в кг + Год рождения + Пол + Рост в см + Вес в кг Количество вибраций Анализ сна Записывать файлы журнала (нужен перезапуск) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 98e0a2f8..3e58a5bf 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -93,12 +93,12 @@ Cihazınızı keşfedilebilir yapın. Bağlı durumdaki cihazlar tekrar keşfedilmeyebilir. Not: Cihaz resmi - Hakkınızda + Hakkınızda İsim - Doğum yılı - Cinsiyet - Boy(cm) - Ağırlık (kg) + Doğum yılı + Cinsiyet + Boy(cm) + Ağırlık (kg) Titreşim adedi Uyku Monitörü diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3af9e0f2..fe50d4f5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -101,12 +101,12 @@ Під\'єднані на даний момент пристрої, скоріш за все не будуть виявлені. Замітка: Зображення пристрою - Ваші дані + Ваші дані Ім\'я/нік - Рік народження - Стать - Зріст в см - Вага в кг + Рік народження + Стать + Зріст в см + Вага в кг Кількість вібрацій Аналіз сну Записувати файли звіту (потрібен перезапуск) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 9a6a7738..56b39a6e 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -81,12 +81,12 @@ Cài đặt Ghi chú: Ảnh thiết bị - Thông tin về bạn + Thông tin về bạn Tên/Bí danh - Năm sinh - Giới tính - Chiều cao bằng cm - Trọng lượng bằng kg + Năm sinh + Giới tính + Chiều cao bằng cm + Trọng lượng bằng kg Trình giám sát giấc ngủ Ghi tập tin nhật ký (cần khởi động lại) đang khởi chạy diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78915517..0304d36c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,12 +117,7 @@ Make your device discoverable. Currently connected devices will likely not be discovered. Note: Device Image - About You Name/Alias - Year of Birth - Gender - Height in cm - Weight in kg Vibration Count Sleep Monitor @@ -218,4 +213,11 @@ Alarms to reserve for upcoming events waiting for reconnect Reinstall + + About You + Year of Birth + Gender + Height in cm + Weight in kg + diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 4b73c989..65997e95 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -1,39 +1,14 @@ + + android:title="@string/activity_prefs_about_you"> - - - - - - - - - + + + + + + + + + + + + From baf5eee72fd5cc57f5f939b554203f58ce097fb2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 2 Feb 2016 17:33:24 +0100 Subject: [PATCH 005/274] Refactored the User Activity-tracking related preferences. Created a new device-independent class ActivityUser to hold the data Moved the constants from the miband constant class to the ActivityUser class Removed the miband-specific in favor of common-prefixed preferences (with upgrade support for legacy values) Changed the way the gender is stored to an integer value Removed the hardcoded default values for user data in favor of static fields of the ActivityUser class --- .../gadgetbridge/GBApplication.java | 43 ++++++++++ .../activities/SettingsActivity.java | 8 +- .../devices/miband/MiBandConst.java | 4 - .../devices/miband/MiBandCoordinator.java | 21 ++--- .../miband/MiBandPreferencesActivity.java | 4 - .../gadgetbridge/devices/miband/UserInfo.java | 3 +- .../gadgetbridge/model/ActivityUser.java | 78 +++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 26 ++----- app/src/main/res/values/arrays.xml | 12 +-- 9 files changed, 146 insertions(+), 53 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 05c1ac7a..9807b9cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBConstants; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -45,6 +46,9 @@ public class GBApplication extends Application { private static final Lock dbLock = new ReentrantLock(); private static DeviceService deviceService; private static SharedPreferences sharedPrefs; + private static final String PREFS_VERSION = "shared_preferences_version"; + //if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version + private static final int CURRENT_PREFS_VERSION = 1; private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); public static final String ACTION_QUIT @@ -84,6 +88,10 @@ public class GBApplication extends Application { // slf4j may be implicitly initialized before we properly configured it. setupLogging(); + if (getPrefsFileVersion() != CURRENT_PREFS_VERSION) { + migratePrefs(getPrefsFileVersion()); + } + setupExceptionHandler(); // For debugging problems with the logback configuration // LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -242,6 +250,41 @@ public class GBApplication extends Application { return result; } + private int getPrefsFileVersion() { + return sharedPrefs.getInt(PREFS_VERSION, 0); //0 is legacy + } + + private void migratePrefs(int oldVersion) { + switch (oldVersion) { + case 0: + SharedPreferences.Editor editor = sharedPrefs.edit(); + String legacyGender = sharedPrefs.getString("mi_user_gender", null); + String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null); + String legacyWeigth = 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.putInt(ActivityUser.PREF_USER_GENDER, gender); + editor.remove("mi_user_gender"); + } + if(legacyHeight != null) { + editor.putString(ActivityUser.PREF_USER_HEIGHT_CM, legacyHeight); + editor.remove("mi_user_height_cm"); + } + if(legacyWeigth != null) { + editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeigth); + 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.putInt(PREFS_VERSION, CURRENT_PREFS_VERSION); + editor.commit(); + break; + } + } + public static LimitedQueue getIDSenderLookup() { return mIDSenderLookup; } 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 7ab5d03f..2c37c914 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -13,10 +13,10 @@ import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_GENDER; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_HEIGHT_CM; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_WEIGHT_KG; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_YEAR_OF_BIRTH; +import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER; +import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM; +import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_WEIGHT_KG; +import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_YEAR_OF_BIRTH; public class SettingsActivity extends AbstractSettingsActivity { @Override 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 20061674..6f3aa77f 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 @@ -9,10 +9,6 @@ public final class MiBandConst { private static final Logger LOG = LoggerFactory.getLogger(MiBandConst.class); public static final String PREF_USER_ALIAS = "mi_user_alias"; - public static final String PREF_USER_YEAR_OF_BIRTH = "mi_user_year_of_birth"; - public static final String PREF_USER_GENDER = "mi_user_gender"; - public static final String PREF_USER_HEIGHT_CM = "mi_user_height_cm"; - public static final String PREF_USER_WEIGHT_KG = "mi_user_weight_kg"; public static final String PREF_MIBAND_WEARSIDE = "mi_wearside"; public static final String PREF_MIBAND_ADDRESS = "development_miaddr"; // FIXME: should be prefixed mi_ public static final String PREF_MIBAND_ALARMS = "mi_alarms"; 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 7b3af1b8..868cda13 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 @@ -9,8 +9,6 @@ import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Calendar; - import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; @@ -18,6 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class MiBandCoordinator extends AbstractDeviceCoordinator { @@ -107,22 +106,16 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { * @throws IllegalArgumentException when the user info can not be created */ public static UserInfo getConfiguredUserInfo(String miBandAddress) throws IllegalArgumentException { + ActivityUser activityUser = new ActivityUser(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - int userYear = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_YEAR_OF_BIRTH, "0")); - int age = 25; - if (userYear > 1900) { - age = Calendar.getInstance().get(Calendar.YEAR) - userYear; - if (age <= 0) { - age = 25; - } - } + UserInfo info = UserInfo.create( miBandAddress, prefs.getString(MiBandConst.PREF_USER_ALIAS, null), - ("male".equals(prefs.getString(MiBandConst.PREF_USER_GENDER, null)) ? 1 : 0), - age, - Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_HEIGHT_CM, "175")), - Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_WEIGHT_KG, "70")), + activityUser.getActivityUserGender(), + activityUser.getActivityUserAge(), + activityUser.getActivityUserHeightCm(), + activityUser.getActivityUserWeightKg(), 0 ); return info; 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 6f469e34..3cb8343f 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 @@ -20,10 +20,6 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_WEARSIDE; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_GENDER; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_HEIGHT_CM; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_WEIGHT_KG; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_YEAR_OF_BIRTH; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_PROFILE; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefKey; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java index 48439da7..9aa098bc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; @@ -23,7 +24,7 @@ public class UserInfo { * @param btAddress the address of the MI Band to connect to. */ public static UserInfo getDefault(String btAddress) { - return new UserInfo(btAddress, "1550050550", 0, 25, 175, 70, 0); + return new UserInfo(btAddress, "1550050550", ActivityUser.defaultUserGender, ActivityUser.defaultUserAge, ActivityUser.defaultUserHeightCm, ActivityUser.defaultUserWeightKg, 0); } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java new file mode 100644 index 00000000..46e14ea6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -0,0 +1,78 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import java.util.Calendar; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; + +/** + * Class holding the common user information needed by most activity trackers + */ +public class ActivityUser { + + private Integer activityUserGender; + private Integer activityUserYearOfBirth; + private Integer activityUserHeightCm; + private Integer activityUserWeightKg; + + public static final int defaultUserGender = 0; + public static final int defaultUserYearOfBirth = 0; + public static final int defaultUserAge = 0; + public static final int defaultUserHeightCm = 175; + public static final int defaultUserWeightKg = 70; + + public static final String PREF_USER_YEAR_OF_BIRTH = "activity_user_year_of_birth"; + public static final String PREF_USER_GENDER = "activity_user_gender"; + public static final String PREF_USER_HEIGHT_CM = "activity_user_height_cm"; + public static final String PREF_USER_WEIGHT_KG = "activity_user_weight_kg"; + + public int getActivityUserWeightKg() { + if(activityUserWeightKg == null) { + fetchPreferences(); + } + return activityUserWeightKg; + } + + public int getActivityUserGender() { + if(activityUserGender == null) { + fetchPreferences(); + } + return activityUserGender; + } + + public int getActivityUserYearOfBirth() { + if(activityUserYearOfBirth == null) { + fetchPreferences(); + } + return activityUserYearOfBirth; + } + + public int getActivityUserHeightCm() { + if(activityUserHeightCm == null) { + fetchPreferences(); + } + return activityUserHeightCm; + } + + public int getActivityUserAge() { + int userYear = getActivityUserYearOfBirth(); + int age = 25; + if (userYear > 1900) { + age = Calendar.getInstance().get(Calendar.YEAR) - userYear; + if (age <= 0) { + age = 25; + } + } + return age; + } + + private void fetchPreferences() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + activityUserGender = prefs.getInt(PREF_USER_GENDER, defaultUserGender); + activityUserHeightCm = Integer.parseInt(prefs.getString(PREF_USER_HEIGHT_CM, Integer.toString(defaultUserHeightCm))); + activityUserWeightKg = Integer.parseInt(prefs.getString(PREF_USER_WEIGHT_KG, Integer.toString(defaultUserWeightKg))); + activityUserYearOfBirth = Integer.parseInt(prefs.getString(PREF_USER_YEAR_OF_BIRTH, Integer.toString(defaultUserYearOfBirth))); + } +} 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 612ff073..8276fca8 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 @@ -1,7 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.util.Base64; import android.util.Pair; @@ -14,7 +12,6 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -32,10 +29,10 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificati import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; @@ -688,27 +685,16 @@ public class PebbleProtocol extends GBDeviceProtocol { ByteBuffer buf = ByteBuffer.allocate(9); buf.order(ByteOrder.LITTLE_ENDIAN); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - - Integer heightMm = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_HEIGHT_CM, "175")) * 10; + ActivityUser activityUser = new ActivityUser(); + Integer heightMm = activityUser.getActivityUserHeightCm() * 10; buf.putShort(heightMm.shortValue()); - Integer weigthDag = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_WEIGHT_KG, "70")) * 100; + Integer weigthDag = activityUser.getActivityUserWeightKg() * 100; buf.putShort(weigthDag.shortValue()); buf.put((byte)0x01); //activate tracking buf.put((byte)0x01); //activity Insights buf.put((byte)0x01); //sleep Insights - int userYear = Integer.parseInt(prefs.getString(MiBandConst.PREF_USER_YEAR_OF_BIRTH, "0")); - int age = 25; - if (userYear > 1900) { - age = Calendar.getInstance().get(Calendar.YEAR) - userYear; - if (age <= 0) { - age = 25; - } - } - buf.put((byte)age); - - int gender = ("male".equals(prefs.getString(MiBandConst.PREF_USER_GENDER, null)) ? 1 : 0); - buf.put((byte)gender); + buf.put((byte)activityUser.getActivityUserAge()); + buf.put((byte)activityUser.getActivityUserGender()); //blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}; blob = buf.array(); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index c1857213..cc31c187 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -21,14 +21,14 @@ - @string/male - @string/female - @string/other + @string/male + @string/female + @string/other - male - female - other + 1 + 0 + 2 @string/left From 493fcfc853df3d3a0dfdc274ec6508e47cafd537 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 3 Feb 2016 20:23:56 +0100 Subject: [PATCH 006/274] Pebble: improve datalog output --- .../service/devices/pebble/PebbleProtocol.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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 8276fca8..58476f9b 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 @@ -690,11 +690,11 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putShort(heightMm.shortValue()); Integer weigthDag = activityUser.getActivityUserWeightKg() * 100; buf.putShort(weigthDag.shortValue()); - buf.put((byte)0x01); //activate tracking - buf.put((byte)0x01); //activity Insights - buf.put((byte)0x01); //sleep Insights - buf.put((byte)activityUser.getActivityUserAge()); - buf.put((byte)activityUser.getActivityUserGender()); + buf.put((byte) 0x01); //activate tracking + buf.put((byte) 0x01); //activity Insights + buf.put((byte) 0x01); //sleep Insights + buf.put((byte) activityUser.getActivityUserAge()); + buf.put((byte) activityUser.getActivityUserGender()); //blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}; blob = buf.array(); @@ -1785,7 +1785,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.order(ByteOrder.LITTLE_ENDIAN); int items_left = buf.getInt(); int crc = buf.getInt(); - LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 9)); + LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); break; case DATALOG_OPENSESSION: buf.order(ByteOrder.BIG_ENDIAN); @@ -1797,7 +1797,10 @@ public class PebbleProtocol extends GBDeviceProtocol { int log_tag = buf.getInt(); byte item_type = buf.get(); short item_size = buf.get(); - LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", item_type=" + item_type + ", item_size=" + item_size); + LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", item_size=" + item_size); + break; + case DATALOG_CLOSE: + LOG.info("DATALOG_CLOSE. id=" + (id & 0xff)); break; default: LOG.info("unknown DATALOG command: " + (command & 0xff)); From 85bad9abf5104e706c26a5d32622fdf10bc4321b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 3 Feb 2016 23:27:35 +0100 Subject: [PATCH 007/274] Pebble: store information about datalog sessions (uuid, item type, length, tag) ... and log them if data comes in from a known id. Also request open sessions on connect. And last but not least hex dump data which might be from Health (the tags that I never see on Aplite but always on Basalt) --- .../devices/pebble/DatalogSession.java | 19 ++++++++ .../devices/pebble/PebbleIoThread.java | 1 + .../devices/pebble/PebbleProtocol.java | 45 ++++++++++++++++--- 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java new file mode 100644 index 00000000..30ac869c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -0,0 +1,19 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import java.util.UUID; + +class DatalogSession { + final byte id; + final int tag; + final UUID uuid; + final byte item_type; + final short item_size; + + DatalogSession(byte id, UUID uuid, int tag, byte item_type, short item_size) { + this.id = id; + this.tag = tag; + this.uuid = uuid; + this.item_type = item_type; + this.item_size = item_size; + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 49165df4..69b16412 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 @@ -468,6 +468,7 @@ public class PebbleIoThread extends GBDeviceIoThread { LOG.info("syncing time"); write(mPebbleProtocol.encodeSetTime()); } + write(mPebbleProtocol.encodeReportDataLogSessions()); gbDevice.setState(GBDevice.State.INITIALIZED); return false; } else if (deviceEvent instanceof GBDeviceEventAppManagement) { 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 58476f9b..444313d9 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 @@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.GB; public class PebbleProtocol extends GBDeviceProtocol { @@ -351,6 +352,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_MISFIT = UUID.fromString("0b73b76a-cd65-4dc2-9585-aaa213320858"); private static final UUID UUID_PEBBLE_TIMESTYLE = UUID.fromString("4368ffa4-f0fb-4823-90be-f754b076bdaa"); private static final UUID UUID_PEBSTYLE = UUID.fromString("da05e84d-e2a2-4020-a2dc-9cdcf265fcdd"); + private static final UUID UUID_ZERO = new UUID(0, 0); private static final Map mAppMessageHandlers = new HashMap<>(); @@ -364,6 +366,8 @@ public class PebbleProtocol extends GBDeviceProtocol { } + private final HashMap mDatalogSessions = new HashMap<>(); + private static byte[] encodeSimpleMessage(short endpoint, byte command) { ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_SIMPLEMESSAGE); buf.order(ByteOrder.BIG_ENDIAN); @@ -374,7 +378,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return buf.array(); } - private static byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) { + private byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) { // Calculate length first int length = LENGTH_PREFIX + 1; if (parts != null) { @@ -704,6 +708,10 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb("activityPreferences", command, BLOBDB_HEALTH, blob); } + public byte[] encodeReportDataLogSessions() { + return encodeSimpleMessage(ENDPOINT_DATALOG, DATALOG_REPORTSESSIONS); + } + private byte[] encodeBlobDBClear(byte database) { final short LENGTH_BLOBDB_CLEAR = 4; ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_BLOBDB_CLEAR); @@ -1645,11 +1653,11 @@ public class PebbleProtocol extends GBDeviceProtocol { // FIXME: this does not belong here, but we want at least check if there is no chance at all to send out the SMS later before we report success String phoneNumber = (String) GBApplication.getIDSenderLookup().lookup(id); //if (phoneNumber != null) { - devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY; - devEvtNotificationControl.reply = new String(reply); - caption = "SENT"; - icon_id = PebbleIconID.RESULT_SENT; - failed = false; + devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY; + devEvtNotificationControl.reply = new String(reply); + caption = "SENT"; + icon_id = PebbleIconID.RESULT_SENT; + failed = false; //} } } @@ -1782,10 +1790,29 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring"); return null; case DATALOG_SENDDATA: + boolean doHexdump = false; buf.order(ByteOrder.LITTLE_ENDIAN); int items_left = buf.getInt(); int crc = buf.getInt(); + DatalogSession datalogSession = mDatalogSessions.get(id); LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); + if (datalogSession != null) { + String taginfo = ""; + if (datalogSession.uuid.equals(UUID_ZERO)) { + if (datalogSession.tag >= 78 && datalogSession.tag <= 80) { + taginfo = "(analytics?)"; + } else if (datalogSession.tag >= 81 && datalogSession.tag <= 83) { + taginfo = "(health?)"; + doHexdump = true; + } else { + taginfo = "(unknown)"; + } + } + LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + taginfo + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); + } + if (doHexdump) { + LOG.info(GB.hexdump(buf.array(), 10, length - 10)); + } break; case DATALOG_OPENSESSION: buf.order(ByteOrder.BIG_ENDIAN); @@ -1798,9 +1825,15 @@ public class PebbleProtocol extends GBDeviceProtocol { byte item_type = buf.get(); short item_size = buf.get(); LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", item_size=" + item_size); + if (!mDatalogSessions.containsKey(id)) { + mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); + } break; case DATALOG_CLOSE: LOG.info("DATALOG_CLOSE. id=" + (id & 0xff)); + if (mDatalogSessions.containsKey(id)) { + mDatalogSessions.remove(id); + } break; default: LOG.info("unknown DATALOG command: " + (command & 0xff)); From 59d6553c5467b6f1277c460151e8a421621643e9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 6 Feb 2016 19:35:49 +0100 Subject: [PATCH 008/274] Pebble: fix stupid bug that broke active reconnection --- .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 69b16412..ab7e2306 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 @@ -362,7 +362,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); - while (reconnectAttempts-- > 0 && !mQuit) { + while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) { LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")"); mIsConnected = connect(gbDevice.getAddress()); } From 3db88574fa03d5b8426f2f6077073846b0d8a0b1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 6 Feb 2016 19:41:21 +0100 Subject: [PATCH 009/274] bump version, update CHANGELOG.md (not yet released) --- CHANGELOG.md | 3 +++ app/build.gradle | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7213e752..ceee46b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version 0.7.4 (next) +* Pebble: Fix regression with broken active reconnect since 0.7.0 + ####Version 0.7.3 * Pebble: Report connection state to PebbleKit companion apps via content provider. NOTE: Makes Gadgetbridge mutual exclusive with the original Pebble app. * Ignore generic notification when from SMSSecure when SMS Notifications are on diff --git a/app/build.gradle b/app/build.gradle index ea5621ac..f1b8c31a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.7.3" - versionCode 39 + versionName "0.7.4" + versionCode 40 } buildTypes { release { From a451b37cebbf9c12ee68dd45c250da6e55bf2e9a Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sat, 6 Feb 2016 19:48:03 +0100 Subject: [PATCH 010/274] Added refactoring of user details to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ceee46b4..5a507875 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ###Changelog ####Version 0.7.4 (next) +* Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. * Pebble: Fix regression with broken active reconnect since 0.7.0 ####Version 0.7.3 From ba9e00d2e448f645cf4e0852b170fcb0acde48ec Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sat, 6 Feb 2016 21:23:21 +0100 Subject: [PATCH 011/274] Add strings for activate and deactivate pebble Health --- .../activities/AppManagerActivity.java | 17 +++++++++++------ app/src/main/res/menu/appmanager_context.xml | 7 +++++++ app/src/main/res/values/strings.xml | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 172f5285..d87f7241 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -157,24 +157,26 @@ public class AppManagerActivity extends Activity { AdapterView.AdapterContextMenuInfo acmi = (AdapterView.AdapterContextMenuInfo) menuInfo; selectedApp = appList.get(acmi.position); - if (!selectedApp.isInCache() && !PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) { + if (!selectedApp.isInCache()) { menu.removeItem(R.id.appmanager_app_reinstall); } + if (!PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) { + menu.removeItem(R.id.appmanager_health_activate); + menu.removeItem(R.id.appmanager_health_deactivate); + } else if (PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) { + menu.removeItem(R.id.appmanager_app_delete); + } menu.setHeaderTitle(selectedApp.getName()); } @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.appmanager_health_deactivate: case R.id.appmanager_app_delete: GBApplication.deviceService().onAppDelete(selectedApp.getUUID()); return true; case R.id.appmanager_app_reinstall: - if (PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) { - GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); - return true; - } - File cachePath; try { cachePath = new File(FileUtils.getExternalFilesDir().getPath() + "/pbw-cache/" + selectedApp.getUUID() + ".pbw"); @@ -184,6 +186,9 @@ public class AppManagerActivity extends Activity { } GBApplication.deviceService().onInstallApp(Uri.fromFile(cachePath)); return true; + case R.id.appmanager_health_activate: + GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); + return true; default: return super.onContextItemSelected(item); } diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index 60f1653d..a139eb1a 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -6,4 +6,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0304d36c..81a7d2db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -219,5 +219,7 @@ Gender Height in cm Weight in kg + Activate + Deactivate From 03ad7f5a24ce8c3cdadf93a868295f4be37adead Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sat, 6 Feb 2016 21:38:55 +0100 Subject: [PATCH 012/274] Do not enable insights on the watch. Add notice about health activation to changelog. --- CHANGELOG.md | 2 ++ .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a507875..6a73973d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ####Version 0.7.4 (next) * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. * Pebble: Fix regression with broken active reconnect since 0.7.0 +* Pebble: Support activation and deactivation of Pebble Health. Activation uses the User details as seen above. Insigths are NOT activated. +** Please be aware that deactivation does NOT delete the data stored on the watch (but it seems to stop the tracking), and we do not know how to switch to metric length units. ####Version 0.7.3 * Pebble: Report connection state to PebbleKit companion apps via content provider. NOTE: Makes Gadgetbridge mutual exclusive with the original Pebble app. 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 444313d9..6bd4bf3b 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 @@ -695,12 +695,10 @@ public class PebbleProtocol extends GBDeviceProtocol { Integer weigthDag = activityUser.getActivityUserWeightKg() * 100; buf.putShort(weigthDag.shortValue()); buf.put((byte) 0x01); //activate tracking - buf.put((byte) 0x01); //activity Insights - buf.put((byte) 0x01); //sleep Insights + buf.put((byte) 0x00); //activity Insights + buf.put((byte) 0x00); //sleep Insights buf.put((byte) activityUser.getActivityUserAge()); buf.put((byte) activityUser.getActivityUserGender()); - //blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}; - blob = buf.array(); } else { blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; From 299b850a6733d36db98fb6ac935dcb5b97039fc1 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 6 Feb 2016 21:48:33 +0100 Subject: [PATCH 013/274] its release time --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a73973d..4e565572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ ###Changelog -####Version 0.7.4 (next) +####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. * Pebble: Fix regression with broken active reconnect since 0.7.0 * Pebble: Support activation and deactivation of Pebble Health. Activation uses the User details as seen above. Insigths are NOT activated. -** Please be aware that deactivation does NOT delete the data stored on the watch (but it seems to stop the tracking), and we do not know how to switch to metric length units. + Please be aware that deactivation does NOT delete the data stored on the watch (but it seems to stop the tracking), and we do not know how to switch to metric length units. ####Version 0.7.3 * Pebble: Report connection state to PebbleKit companion apps via content provider. NOTE: Makes Gadgetbridge mutual exclusive with the original Pebble app. From 2f8207abf98361578f418124098b965ab87968d4 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 7 Feb 2016 09:27:51 +0100 Subject: [PATCH 014/274] Initial support for reading pebble health steps/activity data. --- CHANGELOG.md | 3 + .../gadgetbridge/devices/SampleProvider.java | 1 + .../devices/pebble/HealthSampleProvider.java | 31 ++++++ .../devices/pebble/PebbleCoordinator.java | 2 +- .../devices/pebble/DatalogHandler.java | 24 ++++ .../devices/pebble/DatalogHandlerHealth.java | 104 ++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 34 ++++-- 7 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e565572..c5970c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version (next) +* Pebble: Support reading Pebble Health steps/activity data + ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. * Pebble: Fix regression with broken active reconnect since 0.7.0 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index 5ce64b90..ad99cfea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -5,6 +5,7 @@ public interface SampleProvider { byte PROVIDER_PEBBLE_MORPHEUZ = 1; byte PROVIDER_PEBBLE_GADGETBRIDGE = 2; byte PROVIDER_PEBBLE_MISFIT = 3; + byte PROVIDER_PEBBLE_HEALTH = 4; byte PROVIDER_UNKNOWN = 100; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java new file mode 100644 index 00000000..5da0e230 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java @@ -0,0 +1,31 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.pebble; + +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class HealthSampleProvider implements SampleProvider { + + protected final float movementDivisor = 8000f; + + @Override + public int normalizeType(byte rawType) { + return ActivityKind.TYPE_UNKNOWN; + } + + @Override + public byte toRawActivityKind(int activityKind) { + return ActivityKind.TYPE_UNKNOWN; + } + + + @Override + public float normalizeIntensity(short rawIntensity) { + return rawIntensity / movementDivisor; + } + + + @Override + public byte getID() { + return SampleProvider.PROVIDER_PEBBLE_HEALTH; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 80450f72..917e19e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -49,7 +49,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); if (sharedPrefs.getBoolean("pebble_force_untested", false)) { //return new PebbleGadgetBridgeSampleProvider(); - return new MisfitSampleProvider(); + return new HealthSampleProvider(); } else { return new MorpheuzSampleProvider(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java new file mode 100644 index 00000000..f4f251a5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java @@ -0,0 +1,24 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import java.nio.ByteBuffer; + +public class DatalogHandler { + protected final PebbleProtocol mPebbleProtocol; + protected final int mTag; + + DatalogHandler(int tag, PebbleProtocol pebbleProtocol) { + mTag = tag; + mPebbleProtocol = pebbleProtocol; + } + + public int getTag() { + return mTag; + } + + public String getTagInfo() { return null; } + + public boolean handleMessage(ByteBuffer datalogMessage, int length) { + return true;//ack the datalog transmission + } + +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java new file mode 100644 index 00000000..58e37129 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java @@ -0,0 +1,104 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + + +import android.database.sqlite.SQLiteDatabase; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.Date; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class DatalogHandlerHealth extends DatalogHandler { + + private final int preambleLength = 10; + private final int packetLength = 99; + + private static final Logger LOG = LoggerFactory.getLogger(DatalogHandlerHealth.class); + + public DatalogHandlerHealth(int tag, PebbleProtocol pebbleProtocol) { + super(tag, pebbleProtocol); + } + + @Override + public String getTagInfo() { + return "(health)"; + } + + @Override + public boolean handleMessage(ByteBuffer datalogMessage, int length) { + LOG.info(GB.hexdump(datalogMessage.array(), preambleLength, length-preambleLength)); + + int unknownPacketPreamble, timestamp; + byte unknownC, recordLength, recordNum; + short unknownA; + int beginOfPacketPosition, beginOfSamplesPosition; + + byte steps, orientation; //possibly + short intensity; // possibly + + if (0 == ((length - preambleLength) % packetLength)) { // one datalog message may contain several packets + for (int packet = 0; packet < ((length - preambleLength) / packetLength); packet++) { + beginOfPacketPosition = preambleLength + packet*packetLength; + datalogMessage.position(beginOfPacketPosition); + unknownPacketPreamble = datalogMessage.getInt(); + unknownA = datalogMessage.getShort(); + timestamp = datalogMessage.getInt(); + unknownC = datalogMessage.get(); + recordLength = datalogMessage.get(); + recordNum = datalogMessage.get(); + + beginOfSamplesPosition = datalogMessage.position(); + DBHandler dbHandler = null; + try { + dbHandler = GBApplication.acquireDB(); + try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples + + ActivitySample[] samples = new ActivitySample[recordNum]; + SampleProvider sampleProvider = new HealthSampleProvider(); + + for (int j = 0; j < recordNum; j++) { + datalogMessage.position(beginOfSamplesPosition + j*recordLength); + steps = datalogMessage.get(); + orientation = datalogMessage.get(); + if (j<(recordNum-1)) { + //TODO:apparently last minute data do not contain intensity. I guess we are reading it wrong but this approach is our best bet ATM + intensity = datalogMessage.getShort(); + } else { + intensity = 0; + } + samples[j] = new GBActivitySample( + sampleProvider, + timestamp, + intensity, + (short) (steps & 0xff), + (byte) ActivityKind.TYPE_ACTIVITY); + timestamp += 60; + } + + dbHandler.addGBActivitySamples(samples); + } + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + return false;//NACK, so that we get the data again + }finally { + if (dbHandler != null) { + dbHandler.release(); + } + } + + } + } + return true;//ACK by default + } +} \ No newline at end of file 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 6bd4bf3b..17bcf549 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 @@ -366,6 +366,12 @@ public class PebbleProtocol extends GBDeviceProtocol { } + private static final Map mDatalogHandlers = new HashMap<>(); + + { + mDatalogHandlers.put(81,new DatalogHandlerHealth(81, PebbleProtocol.this)); + } + private final HashMap mDatalogSessions = new HashMap<>(); private static byte[] encodeSimpleMessage(short endpoint, byte command) { @@ -1781,6 +1787,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } private GBDeviceEventSendBytes decodeDatalog(ByteBuffer buf, short length) { + boolean ack = true; byte command = buf.get(); byte id = buf.get(); switch (command) { @@ -1797,13 +1804,19 @@ public class PebbleProtocol extends GBDeviceProtocol { if (datalogSession != null) { String taginfo = ""; if (datalogSession.uuid.equals(UUID_ZERO)) { - if (datalogSession.tag >= 78 && datalogSession.tag <= 80) { - taginfo = "(analytics?)"; - } else if (datalogSession.tag >= 81 && datalogSession.tag <= 83) { - taginfo = "(health?)"; - doHexdump = true; + DatalogHandler datalogHandler = mDatalogHandlers.get(datalogSession.tag); + if (datalogHandler != null) { + taginfo = datalogHandler.getTagInfo(); + ack = datalogHandler.handleMessage(buf, length); } else { - taginfo = "(unknown)"; + if (datalogSession.tag >= 78 && datalogSession.tag <= 80) { + taginfo = "(analytics?)"; + } else if (datalogSession.tag == 83) { + taginfo = "(health?)"; + doHexdump = true; + } else { + taginfo = "(unknown)"; + } } } LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + taginfo + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); @@ -1837,9 +1850,14 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("unknown DATALOG command: " + (command & 0xff)); break; } - LOG.info("sending ACK (0x85)"); GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - sendBytes.encodedBytes = encodeDatalog(id, DATALOG_ACK); + if(ack) { + LOG.info("sending ACK (0x85)"); + sendBytes.encodedBytes = encodeDatalog(id, DATALOG_ACK); + } else { + LOG.info("sending NACK (0x86)"); + sendBytes.encodedBytes = encodeDatalog(id, DATALOG_NACK); + } return sendBytes; } From 10b5c571bb220e7fff96e7605261698a6d004cd7 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 7 Feb 2016 16:44:16 +0100 Subject: [PATCH 015/274] Use Kilometers as distance unit --- .../service/devices/pebble/PebbleIoThread.java | 1 + .../service/devices/pebble/PebbleProtocol.java | 8 ++++++++ 2 files changed, 9 insertions(+) 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 ab7e2306..3cda3144 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 @@ -573,6 +573,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (uri.equals(Uri.parse("fake://health"))) { write(mPebbleProtocol.encodeActivateHealth(true)); + write(mPebbleProtocol.encodeSaneDistanceUnit()); return; } 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 17bcf549..b888e75e 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 @@ -712,6 +712,14 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb("activityPreferences", command, BLOBDB_HEALTH, blob); } + public byte[] encodeSaneDistanceUnit() { + byte[] blob; + byte command; + command = BLOBDB_INSERT; + blob = new byte[]{0x00}; + return encodeBlobdb("unitsDistance", command, BLOBDB_HEALTH, blob); + } + public byte[] encodeReportDataLogSessions() { return encodeSimpleMessage(ENDPOINT_DATALOG, DATALOG_REPORTSESSIONS); } From 0c4e606e74ee6b77961063199a9b91506c295a62 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 7 Feb 2016 21:59:14 +0100 Subject: [PATCH 016/274] Pebble: rename BLOBDB_HEALTH to BLOBDB_PREFERENCES and encodeSaneDistanceUnit to encodeSetSaneDistanceUnit Also allow to set insane units in the method --- .../devices/pebble/PebbleIoThread.java | 2 +- .../devices/pebble/PebbleProtocol.java | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) 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 3cda3144..f2b31488 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 @@ -573,7 +573,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (uri.equals(Uri.parse("fake://health"))) { write(mPebbleProtocol.encodeActivateHealth(true)); - write(mPebbleProtocol.encodeSaneDistanceUnit()); + write(mPebbleProtocol.encodeSetSaneDistanceUnit(true)); return; } 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 b888e75e..ceee63cc 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 @@ -83,7 +83,7 @@ public class PebbleProtocol extends GBDeviceProtocol { static final byte BLOBDB_APP = 2; static final byte BLOBDB_REMINDER = 3; static final byte BLOBDB_NOTIFICATION = 4; - static final byte BLOBDB_HEALTH = 7; // might also be some generic registry database + static final byte BLOBDB_PREFERENCES = 7; static final byte BLOBDB_SUCCESS = 1; static final byte BLOBDB_GENERALFAILURE = 2; static final byte BLOBDB_INVALIDOPERATION = 3; @@ -369,7 +369,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final Map mDatalogHandlers = new HashMap<>(); { - mDatalogHandlers.put(81,new DatalogHandlerHealth(81, PebbleProtocol.this)); + mDatalogHandlers.put(81, new DatalogHandlerHealth(81, PebbleProtocol.this)); } private final HashMap mDatalogSessions = new HashMap<>(); @@ -709,15 +709,17 @@ public class PebbleProtocol extends GBDeviceProtocol { } else { blob = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } - return encodeBlobdb("activityPreferences", command, BLOBDB_HEALTH, blob); + return encodeBlobdb("activityPreferences", command, BLOBDB_PREFERENCES, blob); } - public byte[] encodeSaneDistanceUnit() { - byte[] blob; - byte command; - command = BLOBDB_INSERT; - blob = new byte[]{0x00}; - return encodeBlobdb("unitsDistance", command, BLOBDB_HEALTH, blob); + public byte[] encodeSetSaneDistanceUnit(boolean sane) { + byte value; + if (sane) { + value = 0x00; + } else { + value = 0x01; + } + return encodeBlobdb("unitsDistance", BLOBDB_INSERT, BLOBDB_PREFERENCES, new byte[]{value}); } public byte[] encodeReportDataLogSessions() { @@ -1859,7 +1861,7 @@ public class PebbleProtocol extends GBDeviceProtocol { break; } GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); - if(ack) { + if (ack) { LOG.info("sending ACK (0x85)"); sendBytes.encodedBytes = encodeDatalog(id, DATALOG_ACK); } else { From dd9864015dbf7b2a9cc207e11471f77d7e66036e Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Mon, 8 Feb 2016 06:32:36 +0100 Subject: [PATCH 017/274] Fix #221 - Cast pair.first as integer This commit fixes the following compilation error: ``` :app:compileDebugJavaWithJavac /home/bob/dev/Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java:26: error: incomparable types: Object and int if (pair.first == id) { ^ Note: Some input files use or override a deprecated API. Note: Recompile with -Xlint:deprecation for details. Note: Some input files use unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 error :app:compileDebugJavaWithJavac FAILED FAILURE: Build failed with an exception. ``` --- .../nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java index 42bc1566..eb8ef7aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java @@ -23,7 +23,7 @@ public class LimitedQueue { public void remove(int id) { for (Iterator iter = list.iterator(); iter.hasNext(); ) { Pair pair = iter.next(); - if (pair.first == id) { + if ((Integer) pair.first == id) { iter.remove(); } } From 5b539d52526e5741fdffde1f7fa2e8f3e800a491 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Mon, 8 Feb 2016 22:32:16 +0100 Subject: [PATCH 018/274] [travis] Test against JDK 7 and JDK 8 Bug #221 was due to a different behaviour of JDK8 and JDK7. To prevent this in the future, travis should test with both. --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 22d9469a..a73f784f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: android +jdk: + - oraclejdk8 + - oraclejdk7 android: components: # Uncomment the lines below if you want to From b01a5178139f2d9f26e74e716692971b9b5749ab Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 8 Feb 2016 23:33:05 +0100 Subject: [PATCH 019/274] Pebble: fix hexdump for health datalog --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ceee63cc..1ab21c4c 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 @@ -1832,7 +1832,7 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + taginfo + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); } if (doHexdump) { - LOG.info(GB.hexdump(buf.array(), 10, length - 10)); + LOG.info(GB.hexdump(buf.array(), buf.position(), length - buf.position())); } break; case DATALOG_OPENSESSION: From 12a5b53f006e85cbf15f19549086247357028c01 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 9 Feb 2016 00:49:42 +0100 Subject: [PATCH 020/274] Pebble: Merge DatalogHandler and DataLog session Also: - pass the length of the payload and not of the whole datalog buffer to handleMessage(), simplifying DatalogSessionHealth::handleMessage() --- .../devices/pebble/DatalogHandler.java | 24 ------------ .../devices/pebble/DatalogSession.java | 10 +++++ ...rHealth.java => DatalogSessionHealth.java} | 24 +++++------- .../devices/pebble/PebbleProtocol.java | 38 ++++--------------- 4 files changed, 28 insertions(+), 68 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/{DatalogHandlerHealth.java => DatalogSessionHealth.java} (84%) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java deleted file mode 100644 index f4f251a5..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; - -import java.nio.ByteBuffer; - -public class DatalogHandler { - protected final PebbleProtocol mPebbleProtocol; - protected final int mTag; - - DatalogHandler(int tag, PebbleProtocol pebbleProtocol) { - mTag = tag; - mPebbleProtocol = pebbleProtocol; - } - - public int getTag() { - return mTag; - } - - public String getTagInfo() { return null; } - - public boolean handleMessage(ByteBuffer datalogMessage, int length) { - return true;//ack the datalog transmission - } - -} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index 30ac869c..b17e7259 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; +import java.nio.ByteBuffer; import java.util.UUID; class DatalogSession { @@ -8,6 +9,7 @@ class DatalogSession { final UUID uuid; final byte item_type; final short item_size; + String taginfo = "(unknown)"; DatalogSession(byte id, UUID uuid, int tag, byte item_type, short item_size) { this.id = id; @@ -16,4 +18,12 @@ class DatalogSession { this.item_type = item_type; this.item_size = item_size; } + + boolean handleMessage (ByteBuffer buf, int length) { + return true; + } + + String getTaginfo() { + return taginfo; + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java similarity index 84% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java index 58e37129..1def3cdb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogHandlerHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.util.Date; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; @@ -19,25 +20,21 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DatalogHandlerHealth extends DatalogHandler { +public class DatalogSessionHealth extends DatalogSession { - private final int preambleLength = 10; + private final int preambleLength = 10; // FIXME: this is 14 but if would break the code if corrected private final int packetLength = 99; - private static final Logger LOG = LoggerFactory.getLogger(DatalogHandlerHealth.class); + private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealth.class); - public DatalogHandlerHealth(int tag, PebbleProtocol pebbleProtocol) { - super(tag, pebbleProtocol); - } - - @Override - public String getTagInfo() { - return "(health)"; + public DatalogSessionHealth(byte id, UUID uuid, int tag, byte item_type, short item_size) { + super(id, uuid, tag, item_type, item_size); + taginfo = "(health)"; } @Override public boolean handleMessage(ByteBuffer datalogMessage, int length) { - LOG.info(GB.hexdump(datalogMessage.array(), preambleLength, length-preambleLength)); + LOG.info(GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); int unknownPacketPreamble, timestamp; byte unknownC, recordLength, recordNum; @@ -47,11 +44,10 @@ public class DatalogHandlerHealth extends DatalogHandler { byte steps, orientation; //possibly short intensity; // possibly - if (0 == ((length - preambleLength) % packetLength)) { // one datalog message may contain several packets - for (int packet = 0; packet < ((length - preambleLength) / packetLength); packet++) { + if (0 == (length % packetLength)) { // one datalog message may contain several packets + for (int packet = 0; packet < (length / packetLength); packet++) { beginOfPacketPosition = preambleLength + packet*packetLength; datalogMessage.position(beginOfPacketPosition); - unknownPacketPreamble = datalogMessage.getInt(); unknownA = datalogMessage.getShort(); timestamp = datalogMessage.getInt(); unknownC = datalogMessage.get(); 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 1ab21c4c..41cfe311 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 @@ -37,7 +37,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; -import nodomain.freeyourgadget.gadgetbridge.util.GB; public class PebbleProtocol extends GBDeviceProtocol { @@ -366,12 +365,6 @@ public class PebbleProtocol extends GBDeviceProtocol { } - private static final Map mDatalogHandlers = new HashMap<>(); - - { - mDatalogHandlers.put(81, new DatalogHandlerHealth(81, PebbleProtocol.this)); - } - private final HashMap mDatalogSessions = new HashMap<>(); private static byte[] encodeSimpleMessage(short endpoint, byte command) { @@ -1805,34 +1798,14 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG TIMEOUT. id=" + (id & 0xff) + " - ignoring"); return null; case DATALOG_SENDDATA: - boolean doHexdump = false; buf.order(ByteOrder.LITTLE_ENDIAN); int items_left = buf.getInt(); int crc = buf.getInt(); DatalogSession datalogSession = mDatalogSessions.get(id); LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); if (datalogSession != null) { - String taginfo = ""; - if (datalogSession.uuid.equals(UUID_ZERO)) { - DatalogHandler datalogHandler = mDatalogHandlers.get(datalogSession.tag); - if (datalogHandler != null) { - taginfo = datalogHandler.getTagInfo(); - ack = datalogHandler.handleMessage(buf, length); - } else { - if (datalogSession.tag >= 78 && datalogSession.tag <= 80) { - taginfo = "(analytics?)"; - } else if (datalogSession.tag == 83) { - taginfo = "(health?)"; - doHexdump = true; - } else { - taginfo = "(unknown)"; - } - } - } - LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + taginfo + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); - } - if (doHexdump) { - LOG.info(GB.hexdump(buf.array(), buf.position(), length - buf.position())); + LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); + ack = datalogSession.handleMessage(buf, length - 10); } break; case DATALOG_OPENSESSION: @@ -1847,7 +1820,12 @@ public class PebbleProtocol extends GBDeviceProtocol { short item_size = buf.get(); LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", item_size=" + item_size); if (!mDatalogSessions.containsKey(id)) { - mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); + if (uuid.equals(UUID_ZERO) && log_tag == 81) { + mDatalogSessions.put(id, new DatalogSessionHealth(id, uuid, log_tag, item_type, item_size)); + } + else { + mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); + } } break; case DATALOG_CLOSE: From 93db073538c85a022cc70b446e895d4642621508 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 9 Feb 2016 00:56:16 +0100 Subject: [PATCH 021/274] Pebble: try to fix health code, might be broken, cant test --- .../service/devices/pebble/DatalogSessionHealth.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java index 1def3cdb..97bf7375 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java @@ -22,7 +22,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DatalogSessionHealth extends DatalogSession { - private final int preambleLength = 10; // FIXME: this is 14 but if would break the code if corrected + private final int preambleLength = 14; private final int packetLength = 99; private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealth.class); From d62946df63f98cfe22fa2a35081fa08440ec883e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 9 Feb 2016 01:24:22 +0100 Subject: [PATCH 022/274] Pebble: some minor code cleanups regarding health --- .../devices/pebble/DatalogSession.java | 10 ++++----- .../devices/pebble/DatalogSessionHealth.java | 22 ++++++++----------- .../devices/pebble/PebbleProtocol.java | 7 +++--- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index b17e7259..00f67721 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -7,16 +7,16 @@ class DatalogSession { final byte id; final int tag; final UUID uuid; - final byte item_type; - final short item_size; + final byte itemType; + final short itemSize; String taginfo = "(unknown)"; - DatalogSession(byte id, UUID uuid, int tag, byte item_type, short item_size) { + DatalogSession(byte id, UUID uuid, int tag, byte itemType, short itemSize) { this.id = id; this.tag = tag; this.uuid = uuid; - this.item_type = item_type; - this.item_size = item_size; + this.itemType = itemType; + this.itemSize = itemSize; } boolean handleMessage (ByteBuffer buf, int length) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java index 97bf7375..12d036a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java @@ -2,13 +2,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.database.sqlite.SQLiteDatabase; -import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; -import java.util.Date; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -22,9 +20,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; public class DatalogSessionHealth extends DatalogSession { - private final int preambleLength = 14; - private final int packetLength = 99; - private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealth.class); public DatalogSessionHealth(byte id, UUID uuid, int tag, byte item_type, short item_size) { @@ -36,7 +31,7 @@ public class DatalogSessionHealth extends DatalogSession { public boolean handleMessage(ByteBuffer datalogMessage, int length) { LOG.info(GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); - int unknownPacketPreamble, timestamp; + int timestamp; byte unknownC, recordLength, recordNum; short unknownA; int beginOfPacketPosition, beginOfSamplesPosition; @@ -44,9 +39,10 @@ public class DatalogSessionHealth extends DatalogSession { byte steps, orientation; //possibly short intensity; // possibly - if (0 == (length % packetLength)) { // one datalog message may contain several packets - for (int packet = 0; packet < (length / packetLength); packet++) { - beginOfPacketPosition = preambleLength + packet*packetLength; + int initialPosition = datalogMessage.position(); + if (0 == (length % itemSize)) { // one datalog message may contain several packets + for (int packet = 0; packet < (length / itemSize); packet++) { + beginOfPacketPosition = initialPosition + packet * itemSize; datalogMessage.position(beginOfPacketPosition); unknownA = datalogMessage.getShort(); timestamp = datalogMessage.getInt(); @@ -64,10 +60,10 @@ public class DatalogSessionHealth extends DatalogSession { SampleProvider sampleProvider = new HealthSampleProvider(); for (int j = 0; j < recordNum; j++) { - datalogMessage.position(beginOfSamplesPosition + j*recordLength); + datalogMessage.position(beginOfSamplesPosition + j * recordLength); steps = datalogMessage.get(); orientation = datalogMessage.get(); - if (j<(recordNum-1)) { + if (j < (recordNum - 1)) { //TODO:apparently last minute data do not contain intensity. I guess we are reading it wrong but this approach is our best bet ATM intensity = datalogMessage.getShort(); } else { @@ -85,9 +81,9 @@ public class DatalogSessionHealth extends DatalogSession { dbHandler.addGBActivitySamples(samples); } } catch (Exception ex) { - LOG.debug(ex.getMessage()); + LOG.debug(ex.getMessage()); return false;//NACK, so that we get the data again - }finally { + } finally { if (dbHandler != null) { dbHandler.release(); } 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 41cfe311..bb898856 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 @@ -1804,7 +1804,7 @@ public class PebbleProtocol extends GBDeviceProtocol { DatalogSession datalogSession = mDatalogSessions.get(id); LOG.info("DATALOG SENDDATA. id=" + (id & 0xff) + ", items_left=" + items_left + ", total length=" + (length - 10)); if (datalogSession != null) { - LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", item_size=" + datalogSession.item_size + ", item_type=" + datalogSession.item_type); + LOG.info("DATALOG UUID=" + datalogSession.uuid + ", tag=" + datalogSession.tag + datalogSession.getTaginfo() + ", itemSize=" + datalogSession.itemSize + ", itemType=" + datalogSession.itemType); ack = datalogSession.handleMessage(buf, length - 10); } break; @@ -1818,12 +1818,11 @@ public class PebbleProtocol extends GBDeviceProtocol { int log_tag = buf.getInt(); byte item_type = buf.get(); short item_size = buf.get(); - LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", item_size=" + item_size); + LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size); if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { mDatalogSessions.put(id, new DatalogSessionHealth(id, uuid, log_tag, item_type, item_size)); - } - else { + } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); } } From 20c4e49fe14ca729cd7729537553a9f20584a69c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 9 Feb 2016 17:52:21 +0100 Subject: [PATCH 023/274] Refactoring of the Pebble Health steps data receiver. Added logic to deal with pebble health sleep data. Added database helper to change the type of a range of samples (needed for sleep data). Fixes to the Pebble Health sample provider. --- .../database/ActivityDatabaseHandler.java | 16 +++ .../gadgetbridge/database/DBHandler.java | 3 + .../devices/pebble/HealthSampleProvider.java | 25 +++- .../devices/pebble/DatalogSessionHealth.java | 96 --------------- .../pebble/DatalogSessionHealthSleep.java | 91 ++++++++++++++ .../pebble/DatalogSessionHealthSteps.java | 115 ++++++++++++++++++ .../devices/pebble/PebbleProtocol.java | 4 +- 7 files changed, 251 insertions(+), 99 deletions(-) delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index d0263b57..32bbc828 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -242,4 +242,20 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl builder.append(')'); return builder.toString(); } + + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, byte kind, SampleProvider provider) { + try (SQLiteDatabase db = this.getReadableDatabase()) { + String sql = "UPDATE " + TABLE_GBACTIVITYSAMPLES + " SET " + KEY_TYPE +"= ? WHERE " + + KEY_PROVIDER + " = ? AND " + + KEY_TIMESTAMP + " >= ? AND "+ KEY_TIMESTAMP + " < ? ;"; //do not use BETWEEN because the range is inclusive in that case! + + SQLiteStatement statement = db.compileStatement(sql); + statement.bindLong(1, kind); + statement.bindLong(2, provider.getID()); + statement.bindLong(3, timestampFrom); + statement.bindLong(4, timestampTo); + statement.execute(); + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 54e33829..52c4ba10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -29,4 +29,7 @@ public interface DBHandler { void addGBActivitySamples(ActivitySample[] activitySamples); SQLiteDatabase getWritableDatabase(); + + void changeStoredSamplesType(int timestampFrom, int timestampTo, byte kind, SampleProvider provider); + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java index 5da0e230..c557bbad 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/HealthSampleProvider.java @@ -4,17 +4,38 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class HealthSampleProvider implements SampleProvider { + public static final byte TYPE_DEEP_SLEEP = 5; + public static final byte TYPE_LIGHT_SLEEP = 4; + public static final byte TYPE_ACTIVITY = -1; protected final float movementDivisor = 8000f; @Override public int normalizeType(byte rawType) { - return ActivityKind.TYPE_UNKNOWN; + switch (rawType) { + case TYPE_DEEP_SLEEP: + return ActivityKind.TYPE_DEEP_SLEEP; + case TYPE_LIGHT_SLEEP: + return ActivityKind.TYPE_LIGHT_SLEEP; + case TYPE_ACTIVITY: + default: + return ActivityKind.TYPE_UNKNOWN; + } } @Override public byte toRawActivityKind(int activityKind) { - return ActivityKind.TYPE_UNKNOWN; + switch (activityKind) { + case ActivityKind.TYPE_ACTIVITY: + return TYPE_ACTIVITY; + case ActivityKind.TYPE_DEEP_SLEEP: + return TYPE_DEEP_SLEEP; + case ActivityKind.TYPE_LIGHT_SLEEP: + return TYPE_LIGHT_SLEEP; + case ActivityKind.TYPE_UNKNOWN: // fall through + default: + return TYPE_ACTIVITY; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java deleted file mode 100644 index 12d036a8..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealth.java +++ /dev/null @@ -1,96 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; - - -import android.database.sqlite.SQLiteDatabase; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.util.UUID; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.util.GB; - -public class DatalogSessionHealth extends DatalogSession { - - private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealth.class); - - public DatalogSessionHealth(byte id, UUID uuid, int tag, byte item_type, short item_size) { - super(id, uuid, tag, item_type, item_size); - taginfo = "(health)"; - } - - @Override - public boolean handleMessage(ByteBuffer datalogMessage, int length) { - LOG.info(GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); - - int timestamp; - byte unknownC, recordLength, recordNum; - short unknownA; - int beginOfPacketPosition, beginOfSamplesPosition; - - byte steps, orientation; //possibly - short intensity; // possibly - - int initialPosition = datalogMessage.position(); - if (0 == (length % itemSize)) { // one datalog message may contain several packets - for (int packet = 0; packet < (length / itemSize); packet++) { - beginOfPacketPosition = initialPosition + packet * itemSize; - datalogMessage.position(beginOfPacketPosition); - unknownA = datalogMessage.getShort(); - timestamp = datalogMessage.getInt(); - unknownC = datalogMessage.get(); - recordLength = datalogMessage.get(); - recordNum = datalogMessage.get(); - - beginOfSamplesPosition = datalogMessage.position(); - DBHandler dbHandler = null; - try { - dbHandler = GBApplication.acquireDB(); - try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples - - ActivitySample[] samples = new ActivitySample[recordNum]; - SampleProvider sampleProvider = new HealthSampleProvider(); - - for (int j = 0; j < recordNum; j++) { - datalogMessage.position(beginOfSamplesPosition + j * recordLength); - steps = datalogMessage.get(); - orientation = datalogMessage.get(); - if (j < (recordNum - 1)) { - //TODO:apparently last minute data do not contain intensity. I guess we are reading it wrong but this approach is our best bet ATM - intensity = datalogMessage.getShort(); - } else { - intensity = 0; - } - samples[j] = new GBActivitySample( - sampleProvider, - timestamp, - intensity, - (short) (steps & 0xff), - (byte) ActivityKind.TYPE_ACTIVITY); - timestamp += 60; - } - - dbHandler.addGBActivitySamples(samples); - } - } catch (Exception ex) { - LOG.debug(ex.getMessage()); - return false;//NACK, so that we get the data again - } finally { - if (dbHandler != null) { - dbHandler.release(); - } - } - - } - } - return true;//ACK by default - } -} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java new file mode 100644 index 00000000..55351024 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -0,0 +1,91 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.database.sqlite.SQLiteDatabase; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +class DatalogSessionHealthSleep extends DatalogSession { + + private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSleep.class); + + public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) { + super(id, uuid, tag, item_type, item_size); + taginfo = "(health - sleep)"; + } + + @Override + public boolean handleMessage(ByteBuffer datalogMessage, int length) { + LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); + + int initialPosition = datalogMessage.position(); + int beginOfRecordPosition; + short recordVersion; //probably + + if (0 != (length % itemSize)) + return false;//malformed message? + + int recordCount = length / itemSize; + SleepRecord[] sleepRecords = new SleepRecord[recordCount]; + + for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { + beginOfRecordPosition = initialPosition + recordIdx * itemSize; + datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record + recordVersion = datalogMessage.getShort(); + if (recordVersion!=1) + return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it + + sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(), + datalogMessage.getInt(), + datalogMessage.getInt(), + datalogMessage.getInt()); + + } + + store(sleepRecords); + return true;//ACK by default + } + + private void store(SleepRecord[] sleepRecords) { + DBHandler dbHandler = null; + SampleProvider sampleProvider = new HealthSampleProvider(); + GB.toast("We don't know how to store deep sleep from the pebble yet.", Toast.LENGTH_LONG, GB.INFO); + try { + dbHandler = GBApplication.acquireDB(); + for (SleepRecord sleepRecord: sleepRecords) { + dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart,sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP),sampleProvider); + } + }catch (Exception ex) { + LOG.debug(ex.getMessage()); + } finally { + if (dbHandler != null) { + dbHandler.release(); + } + } + } + + private class SleepRecord { + int offsetUTC; //probably + int bedTimeStart; + int bedTimeEnd; + int deepSleepSeconds; + + public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { + this.offsetUTC = offsetUTC; + this.bedTimeStart = bedTimeStart; + this.bedTimeEnd = bedTimeEnd; + this.deepSleepSeconds = deepSleepSeconds; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java new file mode 100644 index 00000000..6ce8bd09 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -0,0 +1,115 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + + +import android.database.sqlite.SQLiteDatabase; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.HealthSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class DatalogSessionHealthSteps extends DatalogSession { + + private static final Logger LOG = LoggerFactory.getLogger(DatalogSessionHealthSteps.class); + + public DatalogSessionHealthSteps(byte id, UUID uuid, int tag, byte item_type, short item_size) { + super(id, uuid, tag, item_type, item_size); + taginfo = "(health - steps)"; + } + + @Override + public boolean handleMessage(ByteBuffer datalogMessage, int length) { + LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); + + int timestamp; + byte recordLength, recordNum; + short recordVersion; //probably + int beginOfPacketPosition, beginOfRecordPosition; + + int initialPosition = datalogMessage.position(); + if (0 != (length % itemSize)) + return false;//malformed message? + + int packetCount = length / itemSize; + + for (int packetIdx = 0; packetIdx < packetCount; packetIdx++) { + beginOfPacketPosition = initialPosition + packetIdx * itemSize; + datalogMessage.position(beginOfPacketPosition);//we may not consume all the records of a packet + + recordVersion = datalogMessage.getShort(); + + if (recordVersion != 5) + return false; //we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it + + timestamp = datalogMessage.getInt(); + datalogMessage.get(); //unknown, throw away + recordLength = datalogMessage.get(); + recordNum = datalogMessage.get(); + + beginOfRecordPosition = datalogMessage.position(); + StepsRecord[] stepsRecords = new StepsRecord[recordNum]; + + for (int recordIdx = 0; recordIdx < recordNum; recordIdx++) { + datalogMessage.position(beginOfRecordPosition + recordIdx * recordLength); //we may not consume all the bytes of a record + stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get(), datalogMessage.get(), datalogMessage.getShort(), datalogMessage.get(), datalogMessage.get()); + timestamp += 60; + } + + store(stepsRecords); + } + return true;//ACK by default + } + + private void store(StepsRecord[] stepsRecords) { + + DBHandler dbHandler = null; + SampleProvider sampleProvider = new HealthSampleProvider(); + + ActivitySample[] samples = new ActivitySample[stepsRecords.length]; + for (int j = 0; j < stepsRecords.length; j++) { + StepsRecord stepsRecord = stepsRecords[j]; + samples[j] = new GBActivitySample( + sampleProvider, + stepsRecord.timestamp, + stepsRecord.intensity, + (short) (stepsRecord.steps & 0xff), + sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY)); + } + + try { + dbHandler = GBApplication.acquireDB(); + dbHandler.addGBActivitySamples(samples); + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } finally { + if (dbHandler != null) { + dbHandler.release(); + } + } + } + + private class StepsRecord { + int timestamp; + byte steps; + byte orientation; + short intensity; + + public StepsRecord(int timestamp, byte steps, byte orientation, short intensity, byte throwAway1, byte throwAway2) { + this.timestamp = timestamp; + this.steps = steps; + this.orientation = orientation; + this.intensity = intensity; + } + } + +} \ No newline at end of file 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 bb898856..5b6ee85d 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 @@ -1821,7 +1821,9 @@ public class PebbleProtocol extends GBDeviceProtocol { LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size); if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { - mDatalogSessions.put(id, new DatalogSessionHealth(id, uuid, log_tag, item_type, item_size)); + mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size)); + } else if (uuid.equals(UUID_ZERO) && log_tag == 83) { + mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size)); } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); } From 743677870042ee5edd4ee769e6758451e649c0db Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 11 Feb 2016 12:49:01 +0100 Subject: [PATCH 024/274] Pebble: fix for recent morpheuz versions (maybe breaks old versions) --- .../service/devices/pebble/AppMessageHandlerMorpheuz.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 305a4abe..07bdfe1a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -113,12 +113,12 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { break; case KEY_TO: smartalarm_to = (int) pair.second; - LOG.info("got from: " + smartalarm_to / 60 + ":" + smartalarm_to % 60); + LOG.info("got to: " + smartalarm_to / 60 + ":" + smartalarm_to % 60); ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT | AppMessageHandlerMorpheuz.CTRL_DO_NEXT; break; case KEY_VERSION: LOG.info("got version: " + ((float) ((int) pair.second) / 10.0f)); - ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE | AppMessageHandlerMorpheuz.CTRL_SET_LAST_SENT; + ctrl_message = AppMessageHandlerMorpheuz.CTRL_VERSION_DONE; break; case KEY_BASE: // fix timestamp From 8294921de746d9ea0e23afd7b509c52d4a51d05a Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 11 Feb 2016 19:14:40 +0100 Subject: [PATCH 025/274] Do not ack the sleep data until we can actually store them Added helper method to fetch the latest timestamp stored in the DB, needed for the aforementioned feature. Update changelog This closes #188 \o/ --- CHANGELOG.md | 2 +- .../database/ActivityDatabaseHandler.java | 16 ++++++++++++++-- .../gadgetbridge/database/DBHandler.java | 2 ++ .../pebble/DatalogSessionHealthSleep.java | 18 ++++++++++-------- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5970c9e..bf9cd135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ###Changelog ####Version (next) -* Pebble: Support reading Pebble Health steps/activity data +* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 32bbc828..1d8b54b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -246,9 +246,9 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl @Override public void changeStoredSamplesType(int timestampFrom, int timestampTo, byte kind, SampleProvider provider) { try (SQLiteDatabase db = this.getReadableDatabase()) { - String sql = "UPDATE " + TABLE_GBACTIVITYSAMPLES + " SET " + KEY_TYPE +"= ? WHERE " + String sql = "UPDATE " + TABLE_GBACTIVITYSAMPLES + " SET " + KEY_TYPE + "= ? WHERE " + KEY_PROVIDER + " = ? AND " - + KEY_TIMESTAMP + " >= ? AND "+ KEY_TIMESTAMP + " < ? ;"; //do not use BETWEEN because the range is inclusive in that case! + + KEY_TIMESTAMP + " >= ? AND " + KEY_TIMESTAMP + " < ? ;"; //do not use BETWEEN because the range is inclusive in that case! SQLiteStatement statement = db.compileStatement(sql); statement.bindLong(1, kind); @@ -258,4 +258,16 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl statement.execute(); } } + + @Override + public int fetchLatestTimestamp(SampleProvider provider) { + try (SQLiteDatabase db = this.getReadableDatabase()) { + try (Cursor cursor = db.query(TABLE_GBACTIVITYSAMPLES, new String[]{KEY_TIMESTAMP}, KEY_PROVIDER + "=" + String.valueOf(provider.getID()), null, null, null, KEY_TIMESTAMP + " DESC", "1")) { + if (cursor.moveToFirst()) { + return cursor.getInt(0); + } + } + } + return -1; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 52c4ba10..6e7398e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -32,4 +32,6 @@ public interface DBHandler { void changeStoredSamplesType(int timestampFrom, int timestampTo, byte kind, SampleProvider provider); + int fetchLatestTimestamp(SampleProvider provider); + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 55351024..a2ec192a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -43,36 +43,38 @@ class DatalogSessionHealthSleep extends DatalogSession { beginOfRecordPosition = initialPosition + recordIdx * itemSize; datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record recordVersion = datalogMessage.getShort(); - if (recordVersion!=1) + if (recordVersion != 1) return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); - } - store(sleepRecords); - return true;//ACK by default + return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. } - private void store(SleepRecord[] sleepRecords) { + private boolean store(SleepRecord[] sleepRecords) { DBHandler dbHandler = null; SampleProvider sampleProvider = new HealthSampleProvider(); GB.toast("We don't know how to store deep sleep from the pebble yet.", Toast.LENGTH_LONG, GB.INFO); try { dbHandler = GBApplication.acquireDB(); - for (SleepRecord sleepRecord: sleepRecords) { - dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart,sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP),sampleProvider); + int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider); + for (SleepRecord sleepRecord : sleepRecords) { + if (latestTimestamp < sleepRecord.bedTimeEnd) + return false; + dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); } - }catch (Exception ex) { + } catch (Exception ex) { LOG.debug(ex.getMessage()); } finally { if (dbHandler != null) { dbHandler.release(); } } + return true; } private class SleepRecord { From c86365ee2eff03e0f30c180b34896fe2f77c69b6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 13 Feb 2016 00:09:35 +0100 Subject: [PATCH 026/274] Some more Mi Band pairing improvements #180 - listen to notifications early -- the band then actually tells us that authentication is required - check for this after sending user info - add authentication states to GBDevice - workaround for event problems in pairing activity (delivered although already unregistered) - BtLEQueue now deals with gatt events coming *before* connectGatt() actually returned (namely the connection event) --- CHANGELOG.md | 2 +- .../devices/miband/MiBandPairingActivity.java | 11 +++- .../gadgetbridge/impl/GBDevice.java | 6 ++ .../gadgetbridge/service/btle/BtLEQueue.java | 6 +- .../CheckAuthenticationNeededAction.java | 27 +++++++++ .../service/devices/miband/MiBandSupport.java | 59 ++++++++++++++++--- app/src/main/res/values/strings.xml | 2 + 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9cd135..f07328eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ###Changelog ####Version (next) -* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. +* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen.* Mi Band: improvements to pairing ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. 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 c33a1909..9a2b9fac 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 @@ -159,6 +159,12 @@ public class MiBandPairingActivity extends Activity { } private void pairingFinished(boolean pairedSuccessfully) { + LOG.debug("pairingFinished: " + pairedSuccessfully); + if (!isPairing) { + // already gone? + return; + } + isPairing = false; LocalBroadcastManager.getInstance(this).unregisterReceiver(mPairingReceiver); unregisterReceiver(mBondingReceiver); @@ -188,12 +194,13 @@ public class MiBandPairingActivity extends Activity { bondingMacAddress = device.getAddress(); if (bondState == BluetoothDevice.BOND_BONDING) { - LOG.info("Bonding in progress: " + device.getAddress()); + GB.toast(this, "Bonding in progress: " + bondingMacAddress, Toast.LENGTH_LONG, GB.INFO); return; } + GB.toast(this, "Creating bond with" + bondingMacAddress, Toast.LENGTH_LONG, GB.INFO); if (!device.createBond()) { - GB.toast(this, "Unable to pair with " + device.getAddress(), Toast.LENGTH_LONG, GB.ERROR); + GB.toast(this, "Unable to pair with " + bondingMacAddress, Toast.LENGTH_LONG, GB.ERROR); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 38223004..9e832494 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -203,6 +203,10 @@ public class GBDevice implements Parcelable { return GBApplication.getContext().getString(R.string.connected); case INITIALIZING: return GBApplication.getContext().getString(R.string.initializing); + case AUTHENTICATION_REQUIRED: + return GBApplication.getContext().getString(R.string.authentication_required); + case AUTHENTICATING: + return GBApplication.getContext().getString(R.string.authenticating); case INITIALIZED: return GBApplication.getContext().getString(R.string.initialized); } @@ -333,6 +337,8 @@ public class GBDevice implements Parcelable { CONNECTING, CONNECTED, INITIALIZING, + AUTHENTICATION_REQUIRED, // some kind of pairing is required by the device + AUTHENTICATING, // some kind of pairing is requested by the device /** * Means that the device is connected AND all the necessary initialization steps * have been performed. At the very least, this means that basic information like 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 b5f3125d..8104cb46 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 @@ -271,8 +271,8 @@ public final class BtLEQueue { } private boolean checkCorrectGattInstance(BluetoothGatt gatt, String where) { - if (gatt != mBluetoothGatt) { - LOG.info("Ignoring event from wrong BluetoothGatt instance: " + where); + if (gatt != mBluetoothGatt && mBluetoothGatt != null) { + LOG.info("Ignoring event from wrong BluetoothGatt instance: " + where + "; " + gatt); return false; } return true; @@ -319,7 +319,7 @@ public final class BtLEQueue { setDeviceConnectionState(State.CONNECTED); // Attempts to discover services after successful connection. LOG.info("Attempting to start service discovery:" + - mBluetoothGatt.discoverServices()); + gatt.discoverServices()); break; case BluetoothProfile.STATE_DISCONNECTED: LOG.info("Disconnected from GATT server."); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java new file mode 100644 index 00000000..6f25b163 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java @@ -0,0 +1,27 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.bluetooth.BluetoothGatt; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction; + +public class CheckAuthenticationNeededAction extends PlainAction { + private final GBDevice mDevice; + + public CheckAuthenticationNeededAction(GBDevice device) { + super(); + mDevice = device; + } + + @Override + public boolean run(BluetoothGatt gatt) { + // the state is set in MiBandSupport.handleNotificationNotif() + switch (mDevice.getState()) { + case AUTHENTICATION_REQUIRED: // fall through + case AUTHENTICATING: + return false; // abort the whole thing + default: + return true; + } + } +} 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 a78b37f8..43d6c75d 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 @@ -31,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; @@ -94,12 +95,14 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { @Override protected TransactionBuilder initializeDevice(TransactionBuilder builder) { builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext())); - pair(builder) + enableNotifications(builder, true) + .pair(builder) .requestDeviceInfo(builder) .sendUserInfo(builder) + .checkAuthenticationNeeded(builder, getDevice()) .setWearLocation(builder) .setFitnessGoal(builder) - .enableNotifications(builder, true) + .enableFurtherNotifications(builder, true) .setCurrentTime(builder) .requestBatteryInfo(builder) .setInitialized(builder); @@ -107,6 +110,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return builder; } + private MiBandSupport checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) { + builder.add(new CheckAuthenticationNeededAction(device)); + return this; + } + /** * Last action of initialization sequence. Sets the device to initialized. * It is only invoked if all other actions were successfully run, so the device @@ -120,8 +128,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { // TODO: tear down the notifications on quit private MiBandSupport enableNotifications(TransactionBuilder builder, boolean enable) { - builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable) - .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable) + builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable); + return this; + } + + private MiBandSupport enableFurtherNotifications(TransactionBuilder builder, boolean enable) { + builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS), enable) .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable) .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_BATTERY), enable) .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); @@ -685,6 +697,26 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return; } switch (value[0]) { + case MiBandService.NOTIFY_AUTHENTICATION_FAILED: + // we get first FAILED, then NOTIFY_STATUS_MOTOR_AUTH (0x13) + // which means, we need to authenticate by tapping + getDevice().setState(State.AUTHENTICATION_REQUIRED); + getDevice().sendDeviceUpdateIntent(getContext()); + GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR); + break; + case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get + case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS: + LOG.info("Band successfully authenticated"); + // maybe we can perform the rest of the initialization from here + doInitialize(); + break; + + case MiBandService.NOTIFY_STATUS_MOTOR_AUTH: + LOG.info("Band needs authentication (MOTOR_AUTH)"); + getDevice().setState(State.AUTHENTICATING); + getDevice().sendDeviceUpdateIntent(getContext()); + break; + default: for (byte b : value) { LOG.warn("DATA: " + String.format("0x%2x", b)); @@ -692,6 +724,15 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + private void doInitialize() { + try { + TransactionBuilder builder = performInitialized("just initializing after authentication"); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to initialize device after authentication", ex); + } + } + private void handleDeviceInfo(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { mDeviceInfo = new DeviceInfo(value); @@ -763,10 +804,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } private void handleUserInfoResult(byte[] value, int status) { - // successfully transfered user info means we're initialized - if (status == BluetoothGatt.GATT_SUCCESS) { - setConnectionState(State.INITIALIZED); - } + // successfully transferred user info means we're initialized +// commented out, because we have SetDeviceStateAction which sets initialized +// state on every successful initialization. +// if (status == BluetoothGatt.GATT_SUCCESS) { +// setConnectionState(State.INITIALIZED); +// } } private void setConnectionState(State newState) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81a7d2db..cb75de4d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -221,5 +221,7 @@ Weight in kg Activate Deactivate + authenticating + authentication required From cc425838859d6b9ee3bed9dcda4c396bd327be80 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Wed, 17 Feb 2016 15:19:05 +0100 Subject: [PATCH 027/274] add missing newline --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07328eb..8725069d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ###Changelog ####Version (next) -* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen.* Mi Band: improvements to pairing +* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. +* Mi Band: improvements to pairing ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. From 70ae5a2a3a3efc9d7d1f0dc3e7f088938f11a303 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 18 Feb 2016 20:41:22 +0100 Subject: [PATCH 028/274] Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) --- CHANGELOG.md | 2 ++ .../activities/SettingsActivity.java | 1 + .../devices/pebble/PebbleCoordinator.java | 18 ++++++++++++------ app/src/main/res/values/arrays.xml | 12 ++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 6 ++++++ 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8725069d..465db271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ####Version (next) * Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. +* Pebble: Fix support for newer version of morpheuz (>=3.3?) +* Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) * Mi Band: improvements to pairing ####Version 0.7.4 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 2c37c914..acf84304 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -112,6 +112,7 @@ public class SettingsActivity extends AbstractSettingsActivity { "notification_mode_calls", "notification_mode_sms", "notification_mode_k9mail", + "pebble_activitytracker", "pebble_emu_addr", "pebble_emu_port", "pebble_reconnect_attempts", diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index 917e19e5..a76534d1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -45,13 +45,19 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider() { - // FIXME: make this configurable somewhere else. SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - if (sharedPrefs.getBoolean("pebble_force_untested", false)) { - //return new PebbleGadgetBridgeSampleProvider(); - return new HealthSampleProvider(); - } else { - return new MorpheuzSampleProvider(); + int activityTracker = Integer.parseInt(sharedPrefs.getString("pebble_activitytracker", Integer.toString(SampleProvider.PROVIDER_PEBBLE_HEALTH))); + switch (activityTracker) { + case SampleProvider.PROVIDER_PEBBLE_HEALTH: + return new HealthSampleProvider(); + case SampleProvider.PROVIDER_PEBBLE_MISFIT: + return new MisfitSampleProvider(); + case SampleProvider.PROVIDER_PEBBLE_MORPHEUZ: + return new MorpheuzSampleProvider(); + case SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE: + return new PebbleGadgetBridgeSampleProvider(); + default: + return new HealthSampleProvider(); } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index cc31c187..1f5cfa29 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -59,4 +59,16 @@ @string/p_alarm_clock + + Pebble Health + Misfit + Morpheuz + + + + 4 + 3 + 1 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb75de4d..14951692 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,6 +62,7 @@ Mi Band address Pebble Settings + Preferred Activitytracker Allow 3rd Party Android App Access Enable experimental support for Android Apps using PebbleKit Force Notification Protocol diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index aea48761..f547b01e 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -180,6 +180,12 @@ android:key="pebble_reconnect_attempts" android:maxLength="4" android:title="@string/pref_title_pebble_reconnect_attempts" /> + Date: Thu, 18 Feb 2016 23:35:55 +0100 Subject: [PATCH 029/274] Attempt at fixing a (re-) connection issue Sometimes reconnection lead only to "Connected" state, but not "Initialized". This probably happened when the device got disconnected earlier and then was automatically reconnected. The reconnection closed the previous connection, which caused the dispatch-thread to wake up and think the connection is actually establish. Then, when the first action is invoked, it would fail with an NPE because mBluetoothGatt passed to the action is actually null. --- .../freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 8104cb46..4af53e70 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 @@ -63,6 +63,7 @@ public final class BtLEQueue { Transaction transaction = mTransactions.take(); if (!isConnected()) { + LOG.debug("not connected, waiting for connection..."); // TODO: request connection and initialization from the outside and wait until finished internalGattCallback.reset(); @@ -168,15 +169,17 @@ public final class BtLEQueue { } private void setDeviceConnectionState(State newState) { + LOG.debug("new device connection state: " + newState); mGbDevice.setState(newState); mGbDevice.sendDeviceUpdateIntent(mContext); - if (mConnectionLatch != null) { + if (mConnectionLatch != null && newState == State.CONNECTED) { mConnectionLatch.countDown(); } } public void disconnect() { synchronized (mGattMonitor) { + LOG.debug("disconnect()"); BluetoothGatt gatt = mBluetoothGatt; if (gatt != null) { mBluetoothGatt = null; @@ -189,6 +192,7 @@ public final class BtLEQueue { } private void handleDisconnected(int status) { + LOG.debug("handleDisconnected: " + status); internalGattCallback.reset(); mTransactions.clear(); mAbortTransaction = true; From 7626667a0a4dbfd30b6446d09ff33c01a5a89f6e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 19 Feb 2016 23:48:08 +0100 Subject: [PATCH 030/274] try to blindly fix user preferences screen --- app/src/main/res/xml/preferences.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index f547b01e..53a526ee 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -130,7 +130,7 @@ @@ -138,19 +138,19 @@ android:defaultValue="male" android:entries="@array/gender" android:entryValues="@array/gender_values" - android:key="mi_user_gender" + android:key="activity_user_gender" android:title="@string/activity_prefs_gender" /> From c436c4c055d32125dd1f2b25fede5c2f4a5f3170 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 20 Feb 2016 22:20:02 +0100 Subject: [PATCH 031/274] Pebble: Fix wrong(previous) contact being displayed on the pebble. Fixes #228 --- CHANGELOG.md | 1 + .../gadgetbridge/externalevents/PhoneCallReceiver.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 465db271..e05f4495 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. * Pebble: Fix support for newer version of morpheuz (>=3.3?) * Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) +* Pebble: Fix wrong(previous) contact being displayed on the pebble * Mi Band: improvements to pairing ####Version 0.7.4 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 c1782917..26513a06 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -31,7 +31,6 @@ public class PhoneCallReceiver extends BroadcastReceiver { } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(stateStr)) { state = TelephonyManager.CALL_STATE_RINGING; } - onCallStateChanged(context, state, number); } } @@ -52,6 +51,7 @@ public class PhoneCallReceiver extends BroadcastReceiver { callCommand = ServiceCommand.CALL_START; } else { callCommand = ServiceCommand.CALL_OUTGOING; + mSavedNumber = number; } break; case TelephonyManager.CALL_STATE_IDLE: From b858e50804b724218d03887a7a061515d650d0de Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 21 Feb 2016 13:04:32 +0100 Subject: [PATCH 032/274] Use strings to store activity shared preferences. System has trouble with accessing integer in the preferences, so let's not use them. --- CHANGELOG.md | 1 + .../gadgetbridge/GBApplication.java | 23 +++++++++++++++---- .../gadgetbridge/model/ActivityUser.java | 2 +- app/src/main/res/xml/preferences.xml | 2 +- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e05f4495..dfadb833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) * Pebble: Fix wrong(previous) contact being displayed on the pebble * Mi Band: improvements to pairing +* Fix a problem related to shared preferences storage of activity settings. ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 9807b9cd..ee33a7de 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -48,7 +48,7 @@ public class GBApplication extends Application { private static SharedPreferences sharedPrefs; private static final String PREFS_VERSION = "shared_preferences_version"; //if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version - private static final int CURRENT_PREFS_VERSION = 1; + private static final int CURRENT_PREFS_VERSION = 2; private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); public static final String ACTION_QUIT @@ -251,20 +251,25 @@ public class GBApplication extends Application { } private int getPrefsFileVersion() { - return sharedPrefs.getInt(PREFS_VERSION, 0); //0 is legacy + try { + return Integer.parseInt(sharedPrefs.getString(PREFS_VERSION, "0")); //0 is legacy + } catch (Exception e) { + //in version 1 this was an int + return 1; + } } private void migratePrefs(int oldVersion) { + SharedPreferences.Editor editor = sharedPrefs.edit(); switch (oldVersion) { case 0: - SharedPreferences.Editor editor = sharedPrefs.edit(); String legacyGender = sharedPrefs.getString("mi_user_gender", null); String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null); String legacyWeigth = 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.putInt(ActivityUser.PREF_USER_GENDER, gender); + editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(gender)); editor.remove("mi_user_gender"); } if(legacyHeight != null) { @@ -279,10 +284,18 @@ public class GBApplication extends Application { editor.putString(ActivityUser.PREF_USER_YEAR_OF_BIRTH, legacyYOB); editor.remove("mi_user_year_of_birth"); } - editor.putInt(PREFS_VERSION, CURRENT_PREFS_VERSION); + editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); editor.commit(); break; + case 1: + Integer legacyGender_1 = sharedPrefs.getInt(ActivityUser.PREF_USER_GENDER, 2); + if(legacyGender_1 != null) { + editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(legacyGender_1)); + } + editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); + break; } + editor.commit(); } public static LimitedQueue getIDSenderLookup() { 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 46e14ea6..f2c87c49 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -70,7 +70,7 @@ public class ActivityUser { private void fetchPreferences() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - activityUserGender = prefs.getInt(PREF_USER_GENDER, defaultUserGender); + activityUserGender = Integer.parseInt(prefs.getString(PREF_USER_GENDER, Integer.toString(defaultUserGender))); activityUserHeightCm = Integer.parseInt(prefs.getString(PREF_USER_HEIGHT_CM, Integer.toString(defaultUserHeightCm))); activityUserWeightKg = Integer.parseInt(prefs.getString(PREF_USER_WEIGHT_KG, Integer.toString(defaultUserWeightKg))); activityUserYearOfBirth = Integer.parseInt(prefs.getString(PREF_USER_YEAR_OF_BIRTH, Integer.toString(defaultUserYearOfBirth))); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 53a526ee..cb2a8a6f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -135,7 +135,7 @@ android:title="@string/activity_prefs_year_birth" /> Date: Sun, 21 Feb 2016 15:26:24 +0100 Subject: [PATCH 033/274] Request permissions at runtime on Android 6. Closes #219 TODO: Tell the user why we request that and if he really needs it (if he does not have both a Mi Band and a Pebble she does not need all) --- .../activities/ControlCenter.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index db241ed9..2615f282 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; +import android.Manifest; +import android.annotation.TargetApi; import android.app.Activity; import android.app.ProgressDialog; import android.bluetooth.BluetoothDevice; @@ -9,8 +11,12 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.SwipeRefreshLayout; import android.view.ContextMenu; @@ -167,6 +173,10 @@ public class ControlCenter extends Activity { Intent enableIntent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); startActivity(enableIntent); } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + checkAndRequestPermissions(); + } + GBApplication.deviceService().start(); enableSwipeRefresh(selectedDevice); @@ -354,4 +364,37 @@ public class ControlCenter extends Activity { mGBDeviceAdapter.notifyDataSetChanged(); } + + @TargetApi(Build.VERSION_CODES.M) + private void checkAndRequestPermissions() { + List wantedPermissions = new ArrayList<>(); + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.BLUETOOTH); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.BLUETOOTH_ADMIN); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.READ_CONTACTS); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.CALL_PHONE); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.READ_PHONE_STATE); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.READ_SMS); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.SEND_SMS); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED) + wantedPermissions.add(Manifest.permission.READ_CALENDAR); + if (ContextCompat.checkSelfPermission(this, "com.fsck.k9.permission.READ_MESSAGES") == PackageManager.PERMISSION_DENIED) + wantedPermissions.add("com.fsck.k9.permission.READ_MESSAGES"); + + if (!wantedPermissions.isEmpty()) + ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0); + } + + } From db6f26fcd5f20b474f47f898d80a6d241f920032 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 21 Feb 2016 15:46:53 +0100 Subject: [PATCH 034/274] bump version, update CHANGELOG.md and README.md --- CHANGELOG.md | 1 + README.md | 5 ++--- app/build.gradle | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfadb833..5b54465e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Pebble: Fix wrong(previous) contact being displayed on the pebble * Mi Band: improvements to pairing * Fix a problem related to shared preferences storage of activity settings. +* Very basic support Android 6 runtime permission ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. diff --git a/README.md b/README.md index 187372dd..11ac5309 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,8 @@ need to create an account and transmit any of your data to the vendor's servers. * Install firwmare files (.pbz) [READ THE WIKI](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble-Firmware-updates) * Install language files (.pbl) * Take and share screenshots from the Pebble's screen -* PebbleKit support for 3rd Party Android Apps support (experimental) -* Morpheuz sleep data syncronization (experimental) -* Misfit steps data synchronization (experimental) +* PebbleKit support for 3rd Party Android Apps (experimental) +* Fetch activity data from Pebble Health, Misfit and Morpheuz (experimental) ## Notes about Firmware 3.x (Pebble Time, updated OG) diff --git a/app/build.gradle b/app/build.gradle index f1b8c31a..d2c03751 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.7.4" - versionCode 40 + versionName "0.8.0" + versionCode 41 } buildTypes { release { From 6eb35b955ed70428a59fa700335c359300ff5fd5 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 21 Feb 2016 16:46:48 +0100 Subject: [PATCH 035/274] Prevent race condition on android 6 (?) at the cost of losing the gender data (we cannot display a toast at this point unfortunately). --- .../freeyourgadget/gadgetbridge/GBApplication.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index ee33a7de..af43c296 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -285,13 +285,17 @@ public class GBApplication extends Application { editor.remove("mi_user_year_of_birth"); } editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); - editor.commit(); break; case 1: - Integer legacyGender_1 = sharedPrefs.getInt(ActivityUser.PREF_USER_GENDER, 2); - if(legacyGender_1 != null) { - editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(legacyGender_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; } From b5a726b777d785226a2257b6cf8d0e28d5886291 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 21 Feb 2016 17:21:04 +0100 Subject: [PATCH 036/274] Change layout of the alarms activity, fixes #216. --- .../res/layout/activity_alarm_details.xml | 141 +++++++----------- 1 file changed, 53 insertions(+), 88 deletions(-) diff --git a/app/src/main/res/layout/activity_alarm_details.xml b/app/src/main/res/layout/activity_alarm_details.xml index b50f7193..10a7f296 100644 --- a/app/src/main/res/layout/activity_alarm_details.xml +++ b/app/src/main/res/layout/activity_alarm_details.xml @@ -11,72 +11,56 @@ + android:layout_height="wrap_content" + android:layout_marginBottom="10dp"> - + android:id="@+id/alarm_cb_smart_wakeup"/> + + + + + + + - - - - - - - - - - - + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom"/> + android:gravity="center_horizontal|top"/> @@ -90,20 +74,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_tue" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom" /> + android:gravity="center_horizontal|top" /> @@ -117,20 +98,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_wed" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom"/> + android:gravity="center_horizontal|top" /> @@ -144,20 +122,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_thu" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom" /> + android:gravity="center_horizontal|top" /> @@ -171,20 +146,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_fri" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom"/> + android:gravity="center_horizontal|top" /> @@ -198,20 +170,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_sat" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal" + android:gravity="center_horizontal|bottom"/> + android:gravity="center_horizontal|top" /> @@ -225,20 +194,16 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/alarm_cb_sun" - android:layout_gravity="center_horizontal|bottom" - android:gravity="center_horizontal|bottom" - android:layout_weight="1" /> + android:layout_gravity="center_horizontal"/> + android:gravity="center_horizontal|top" /> From fee04a05ae0ca8701b57af90bad91f67b14c08af Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 21 Feb 2016 21:23:22 +0100 Subject: [PATCH 037/274] Updated for Mi Band connection fixes --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b54465e..c4fdea08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ * Pebble: Fix support for newer version of morpheuz (>=3.3?) * Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) * Pebble: Fix wrong(previous) contact being displayed on the pebble -* Mi Band: improvements to pairing +* Mi Band: improvements to pairing and connecting * Fix a problem related to shared preferences storage of activity settings. * Very basic support Android 6 runtime permission From 8de836efb82240f206f4f83daca9effd8de75ddd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 21 Feb 2016 22:09:02 +0100 Subject: [PATCH 038/274] Version 0.8.0 --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4fdea08..62225e98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,14 @@ ###Changelog -####Version (next) -* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it deems appropriate, there is no action to perform on the watch for this to happen. +####Version 0.8.0 +* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it seems appropriate, there is no action to perform on the watch for this to happen. * Pebble: Fix support for newer version of morpheuz (>=3.3?) * Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz) * Pebble: Fix wrong(previous) contact being displayed on the pebble * Mi Band: improvements to pairing and connecting -* Fix a problem related to shared preferences storage of activity settings. +* Fix a problem related to shared preferences storage of activity settings * Very basic support Android 6 runtime permission +* Fix layout of the alarms activity ####Version 0.7.4 * Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved. From defa97b882ace9521b6a1ee4b81a1d264f2aace0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 24 Feb 2016 23:53:30 +0100 Subject: [PATCH 039/274] Log the toast message immediately, not delayed in the main thread (this helps understanding logs) --- .../java/nodomain/freeyourgadget/gadgetbridge/util/GB.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 7571badc..c6f87cff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -202,18 +202,17 @@ public class GB { * @param ex optional exception to be logged */ public static void toast(final Context context, final String message, final int displayTime, final int severity, final Throwable ex) { + log(message, severity, ex); // log immediately, not delayed if (env().isLocalTest()) { return; } Looper mainLooper = Looper.getMainLooper(); if (Thread.currentThread() == mainLooper.getThread()) { - log(message, severity, ex); Toast.makeText(context, message, displayTime).show(); } else { Runnable runnable = new Runnable() { @Override public void run() { - log(message, severity, ex); Toast.makeText(context, message, displayTime).show(); } }; From 095ef56c14427de5ab836c15805bf6d5f0019636 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 25 Feb 2016 23:52:34 +0100 Subject: [PATCH 040/274] Initial support for activity data sync with Mi 1S #205 Looks like the activity type is somehow wrong though, or I'm sleeping all day ;-) --- .../operations/FetchActivityOperation.java | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) 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 21bc3928..9019013b 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 @@ -27,7 +27,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandSampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.impl.GBActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; @@ -52,11 +51,13 @@ public class FetchActivityOperation extends AbstractMiBandOperation { private final int activityMetadataLength = 11; - //temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 bytes) - private static final int activityDataHolderSize = 3 * 60 * 4; // 4h + //temporary buffer, size is a multiple of 60 because we want to store complete minutes (1 minute = 3 or 4 bytes) + private final int activityDataHolderSize; + private final boolean hasExtendedActivityData; private static class ActivityStruct { - private final byte[] activityDataHolder = new byte[activityDataHolderSize]; + private final byte[] activityDataHolder; + private final int activityDataHolderSize; //index of the buffer above private int activityDataHolderProgress = 0; //number of bytes we will get in a single data transfer, used as counter @@ -68,6 +69,11 @@ public class FetchActivityOperation extends AbstractMiBandOperation { //same as above, but remains untouched for the ack message private GregorianCalendar activityDataTimestampToAck = null; + ActivityStruct(int activityDataHolderSize) { + this.activityDataHolderSize = activityDataHolderSize; + activityDataHolder = new byte[activityDataHolderSize]; + } + public boolean hasRoomFor(byte[] value) { return activityDataRemainingBytes >= value.length; } @@ -127,10 +133,13 @@ public class FetchActivityOperation extends AbstractMiBandOperation { } } - private ActivityStruct activityStruct = new ActivityStruct(); + private ActivityStruct activityStruct; public FetchActivityOperation(MiBandSupport support) { super(support); + hasExtendedActivityData = support.getDeviceInfo().isMilli1S(); + activityDataHolderSize = getBytesPerMinuteOfActivityData() * 60 * 4; // 4h + activityStruct = new ActivityStruct(activityDataHolderSize); } @Override @@ -208,30 +217,34 @@ public class FetchActivityOperation extends AbstractMiBandOperation { // counter of all data held by the band int totalDataToRead = (value[7] & 0xff) | ((value[8] & 0xff) << 8); - totalDataToRead *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? 3 : 1; + totalDataToRead *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? getBytesPerMinuteOfActivityData() : 1; // counter of this data block int dataUntilNextHeader = (value[9] & 0xff) | ((value[10] & 0xff) << 8); - dataUntilNextHeader *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? 3 : 1; + dataUntilNextHeader *= (dataType == MiBandService.MODE_REGULAR_DATA_LEN_MINUTE) ? getBytesPerMinuteOfActivityData() : 1; - // there is a total of totalDataToRead that will come in chunks (3 bytes per minute if dataType == 1 (MiBandService.MODE_REGULAR_DATA_LEN_MINUTE)), + // there is a total of totalDataToRead that will come in chunks (3 or 4 bytes per minute if dataType == 1 (MiBandService.MODE_REGULAR_DATA_LEN_MINUTE)), // these chunks are usually 20 bytes long and grouped in blocks // after dataUntilNextHeader bytes we will get a new packet of 11 bytes that should be parsed // as we just did if (activityStruct.isFirstChunk() && dataUntilNextHeader != 0) { GB.toast(getContext().getString(R.string.user_feedback_miband_activity_data_transfer, - DateTimeUtils.formatDurationHoursMinutes((totalDataToRead / 3), TimeUnit.MINUTES), + DateTimeUtils.formatDurationHoursMinutes((totalDataToRead / getBytesPerMinuteOfActivityData()), TimeUnit.MINUTES), DateFormat.getDateTimeInstance().format(timestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); } - LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / 3) + " minute(s)"); - LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / 3) + " minute(s)"); + LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / getBytesPerMinuteOfActivityData()) + " minute(s)"); + LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / getBytesPerMinuteOfActivityData()) + " minute(s)"); LOG.info("TIMESTAMP: " + DateFormat.getDateTimeInstance().format(timestamp.getTime()) + " magic byte: " + dataUntilNextHeader); activityStruct.startNewBlock(timestamp, dataUntilNextHeader); } + private int getBytesPerMinuteOfActivityData() { + return hasExtendedActivityData ? 4 : 3; + } + /** * Method to store temporarily the activity data values got from the Mi Band. *

@@ -290,7 +303,8 @@ public class FetchActivityOperation extends AbstractMiBandOperation { LOG.debug("nothing to flush, struct is already null"); return; } - LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / 3); + int bpm = getBytesPerMinuteOfActivityData(); + LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / bpm); byte category, intensity, steps; DBHandler dbHandler = null; @@ -299,18 +313,19 @@ public class FetchActivityOperation extends AbstractMiBandOperation { int minutes = 0; try (SQLiteDatabase db = dbHandler.getWritableDatabase()) { // explicitly keep the db open while looping over the samples int timestampInSeconds = (int) (activityStruct.activityDataTimestampProgress.getTimeInMillis() / 1000); - if ((activityStruct.activityDataHolderProgress % 3) != 0) { - throw new IllegalStateException("Unexpected data, progress should be mutiple of 3: " + activityStruct.activityDataHolderProgress); + if ((activityStruct.activityDataHolderProgress % bpm) != 0) { + throw new IllegalStateException("Unexpected data, progress should be mutiple of " + bpm +": " + activityStruct.activityDataHolderProgress); } - int numSamples = activityStruct.activityDataHolderProgress/3; + int numSamples = activityStruct.activityDataHolderProgress / bpm; ActivitySample[] samples = new ActivitySample[numSamples]; SampleProvider sampleProvider = new MiBandSampleProvider(); int s = 0; - for (int i = 0; i < activityStruct.activityDataHolderProgress; i += 3) { + for (int i = 0; i < activityStruct.activityDataHolderProgress; i += bpm) { category = activityStruct.activityDataHolder[i]; intensity = activityStruct.activityDataHolder[i + 1]; steps = activityStruct.activityDataHolder[i + 2]; + byte unknown = activityStruct.activityDataHolder[i + 3]; samples[minutes] = new GBActivitySample( sampleProvider, From 0b568df8debca9ea49c774b96664c71481543fd3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 26 Feb 2016 00:04:33 +0100 Subject: [PATCH 041/274] Extra byte indeed appears to be heartrate value #205 --- .../devices/miband/operations/FetchActivityOperation.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 9019013b..6cefcef6 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 @@ -305,7 +305,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { } int bpm = getBytesPerMinuteOfActivityData(); LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / bpm); - byte category, intensity, steps; + byte category, intensity, steps, heartrate; DBHandler dbHandler = null; try { @@ -325,7 +325,9 @@ public class FetchActivityOperation extends AbstractMiBandOperation { category = activityStruct.activityDataHolder[i]; intensity = activityStruct.activityDataHolder[i + 1]; steps = activityStruct.activityDataHolder[i + 2]; - byte unknown = activityStruct.activityDataHolder[i + 3]; + if (hasExtendedActivityData) { + heartrate = activityStruct.activityDataHolder[i + 3]; + } samples[minutes] = new GBActivitySample( sampleProvider, From a10c6f3b9f6976d44b503b432ad3289622846a11 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 26 Feb 2016 00:30:57 +0100 Subject: [PATCH 042/274] Some initial heartrate support #205 (not visible to user yet) --- .../gadgetbridge/impl/GBActivitySample.java | 32 +++++++++++++++++-- .../gadgetbridge/model/ActivitySample.java | 2 ++ .../gadgetbridge/util/DateTimeUtils.java | 6 ++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java index 1cb7416d..f75ab4dc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java @@ -2,32 +2,42 @@ package nodomain.freeyourgadget.gadgetbridge.impl; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; public class GBActivitySample implements ActivitySample { private final int timestamp; private final SampleProvider provider; private final short intensity; private final short steps; + private final short heartrate; private final byte type; public GBActivitySample(SampleProvider provider, int timestamp, short intensity, short steps, byte type) { + this(provider, timestamp, intensity, steps, (short) 0, type); + } + + public GBActivitySample(SampleProvider provider, int timestamp, short intensity, short steps, short heartrate, byte type) { this.timestamp = timestamp; this.provider = provider; this.intensity = intensity; this.steps = steps; + this.heartrate = heartrate; this.type = type; validate(); } private void validate() { if (steps < 0) { - throw new IllegalArgumentException("steps must be > 0"); + throw new IllegalArgumentException("steps must be >= 0"); } if (intensity < 0) { - throw new IllegalArgumentException("intensity must be > 0"); + throw new IllegalArgumentException("intensity must be >= 0"); } if (timestamp < 0) { - throw new IllegalArgumentException("timestamp must be > 0"); + throw new IllegalArgumentException("timestamp must be >= 0"); + } + if (heartrate < 0) { + throw new IllegalArgumentException("heartrate must be >= 0"); } } @@ -65,4 +75,20 @@ public class GBActivitySample implements ActivitySample { public int getKind() { return getProvider().normalizeType(getRawKind()); } + + @Override + public short getHeartRate() { + return heartrate; + } + + @Override + public String toString() { + return "GBActivitySample{" + + "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(timestamp)) + + ", intensity=" + getIntensity() + + ", steps=" + getSteps() + + ", heartrate=" + getHeartRate() + + ", type=" + getKind() + + '}'; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java index 99a2d28e..6d332423 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java @@ -41,4 +41,6 @@ public interface ActivitySample { * Returns the number of steps performed during the period of this sample */ short getSteps(); + + short getHeartRate(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java index f7ac4e15..ef507876 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java @@ -49,4 +49,10 @@ public class DateTimeUtils { Date newDate = cal.getTime(); return newDate; } + + public static Date parseTimeStamp(int timestamp) { + GregorianCalendar cal = (GregorianCalendar) GregorianCalendar.getInstance(); + cal.setTimeInMillis(timestamp * 1000L); // make sure it's converted to long + return cal.getTime(); + } } From df741e957102bfe914cd513a9e6e7775c2d31362 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 26 Feb 2016 15:29:26 +0100 Subject: [PATCH 043/274] Install app on watch directly instead of telling the user to do so. --- CHANGELOG.md | 3 +++ .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62225e98..9fb5827d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version (next) +* Pebble: install (and start) freshly-installed apps on the watch instead of showing a Toast that tells the user to do so. (only applies to firmware 3.x) + ####Version 0.8.0 * Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it seems appropriate, there is no action to perform on the watch for this to happen. * Pebble: Fix support for newer version of morpheuz (>=3.3?) 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 f2b31488..e3ffaec4 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 @@ -620,7 +620,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (appId == 0) { // only install metadata - not the binaries write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion(), mPBWReader.getFlags(), mPBWReader.getIconId())); - GB.toast("To finish installation please start the watchapp on your Pebble", 5, GB.INFO); + write(mPebbleProtocol.encodeAppStart(app.getUUID(), true)); } else { // this came from an app fetch request, so do the real stuff mIsInstalling = true; From 0ef738067dec687a5fa2fc7b0ea904ed6b6b29da Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 26 Feb 2016 23:45:17 +0100 Subject: [PATCH 044/274] Some work in progress for heart rate graphs #178 Currently we get the heart rate when synchronizing activity data (i.e. not live) and we write it to the activity database so that we can show a nice graph. The value is currently always 0 though, because we can't enable recording hr, yet. --- .../charts/AbstractChartFragment.java | 59 ++++++++++++++++--- .../charts/ActivitySleepChartFragment.java | 2 +- .../activities/charts/SleepChartFragment.java | 5 +- .../database/ActivityDatabaseHandler.java | 22 +++++-- .../gadgetbridge/database/DBConstants.java | 1 + .../gadgetbridge/database/DBHandler.java | 2 +- .../database/schema/ActivityDBUpdate_6.java | 14 ++--- .../database/schema/ActivityDBUpdate_X.java | 31 ++++++++++ .../gadgetbridge/impl/GBActivitySample.java | 18 +++--- .../gadgetbridge/model/ActivitySample.java | 2 +- .../operations/FetchActivityOperation.java | 5 +- .../pebble/AppMessageHandlerGBPebble.java | 2 +- .../pebble/AppMessageHandlerMorpheuz.java | 2 +- .../res/layout-land/fragment_sleepchart.xml | 2 +- app/src/main/res/layout/fragment_charts.xml | 2 +- .../main/res/layout/fragment_sleepchart.xml | 2 +- app/src/main/res/values/colors.xml | 1 + 17 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 61afd279..03993b02 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -12,9 +12,14 @@ import android.view.View; import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.CombinedData; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,6 +28,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashSet; @@ -72,12 +78,16 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } }; private boolean mChartDirty = true; + private boolean supportsHeartrateChart = false; public boolean isChartDirty() { return mChartDirty; } public abstract String getTitle(); + public boolean supportsHeartrate() { + return supportsHeartrateChart; + } protected static final class ActivityConfig { public final int type; @@ -101,6 +111,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected int DESCRIPTION_COLOR; protected int CHART_TEXT_COLOR; protected int LEGEND_TEXT_COLOR; + protected int HEARTRATE_COLOR; protected int AK_ACTIVITY_COLOR; protected int AK_DEEP_SLEEP_COLOR; protected int AK_LIGHT_SLEEP_COLOR; @@ -134,6 +145,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { DESCRIPTION_COLOR = getResources().getColor(R.color.primarytext); CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext); LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext); + HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate); AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light); AK_DEEP_SLEEP_COLOR = getResources().getColor(R.color.chart_light_sleep_light); AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light); @@ -389,6 +401,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { int numEntries = samples.size(); List xLabels = new ArrayList<>(numEntries); List activityEntries = new ArrayList<>(numEntries); + boolean hr = supportsHeartrate(); + List heartrateEntries = hr ? new ArrayList(numEntries) : null; List colors = new ArrayList<>(numEntries); // this is kinda inefficient... for (int i = 0; i < numEntries; i++) { @@ -431,6 +445,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { colors.add(akActivity.color); } activityEntries.add(createBarEntry(value, i)); + if (hr) { + heartrateEntries.add(createLineEntry(sample.getCustomShortValue(), i)); + } String xLabel = ""; if (annotate) { @@ -463,13 +480,19 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { chart.getXAxis().setValues(xLabels); BarDataSet activitySet = createActivitySet(activityEntries, colors, "Activity"); - - ArrayList dataSets = new ArrayList<>(); - dataSets.add(activitySet); - // create a data object with the datasets - BarData data = new BarData(xLabels, dataSets); - data.setGroupSpace(0); + CombinedData combinedData = new CombinedData(xLabels); + List list = new ArrayList<>(); + list.add(activitySet); + BarData barData = new BarData(xLabels, list); + barData.setGroupSpace(0); + combinedData.setData(barData); + + if (hr) { + LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate"); + LineData lineData = new LineData(xLabels, heartrateSet); + combinedData.setData(lineData); + } chart.setDescription(""); // chart.setDescription(getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo)); @@ -477,7 +500,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { setupLegend(chart); - chart.setData(data); + chart.setData(combinedData); } } @@ -498,6 +521,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return new BarEntry(value, index); } + protected Entry createLineEntry(float value, int index) { + return new Entry(value, index); + } + protected BarDataSet createActivitySet(List values, List colors, String label) { BarDataSet set1 = new BarDataSet(values, label); set1.setColors(colors); @@ -515,6 +542,24 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return set1; } + protected LineDataSet createHeartrateSet(List values, String label) { + LineDataSet set1 = new LineDataSet(values, label); + set1.setColor(HEARTRATE_COLOR); +// set1.setColors(colors); +// set1.setDrawCubic(true); +// set1.setCubicIntensity(0.2f); +// //set1.setDrawFilled(true); +// set1.setDrawCircles(false); +// set1.setLineWidth(2f); +// set1.setCircleSize(5f); +// set1.setFillColor(ColorTemplate.getHoloBlue()); + set1.setDrawValues(false); +// set1.setHighLightColor(Color.rgb(128, 0, 255)); +// set1.setColor(Color.rgb(89, 178, 44)); + set1.setValueTextColor(CHART_TEXT_COLOR); + return set1; + } + protected BarDataSet createDeepSleepSet(List values, String label) { BarDataSet set1 = new BarDataSet(values, label); // set1.setDrawCubic(true); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java index b60927b4..b729123e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java @@ -78,7 +78,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment { YAxis yAxisRight = mChart.getAxisRight(); yAxisRight.setDrawGridLines(false); - yAxisRight.setEnabled(false); + yAxisRight.setEnabled(supportsHeartrate()); yAxisRight.setDrawLabels(false); yAxisRight.setDrawTopYLabelEntry(false); yAxisRight.setTextColor(CHART_TEXT_COLOR); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index f2c2f37f..17411ff6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -10,6 +10,7 @@ import android.view.ViewGroup; import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.BarLineChartBase; import com.github.mikephil.charting.charts.Chart; +import com.github.mikephil.charting.charts.CombinedChart; import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.components.YAxis; @@ -39,7 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; public class SleepChartFragment extends AbstractChartFragment { protected static final Logger LOG = LoggerFactory.getLogger(ActivitySleepChartFragment.class); - private BarLineChartBase mActivityChart; + private CombinedChart mActivityChart; private PieChart mSleepAmountChart; private int mSmartAlarmFrom = -1; @@ -99,7 +100,7 @@ public class SleepChartFragment extends AbstractChartFragment { Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_sleepchart, container, false); - mActivityChart = (BarLineChartBase) rootView.findViewById(R.id.sleepchart); + mActivityChart = (CombinedChart) rootView.findViewById(R.id.sleepchart); mSleepAmountChart = (PieChart) rootView.findViewById(R.id.sleepchart_pie_light_deep); setupActivityChart(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 1d8b54b4..2df3759d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -27,13 +27,14 @@ import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROV import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TYPE; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; -public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandler { +public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandler { private static final Logger LOG = LoggerFactory.getLogger(ActivityDatabaseHandler.class); - private static final int DATABASE_VERSION = 5; + private static final int DATABASE_VERSION = 6; public ActivityDatabaseHandler(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -101,6 +102,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl values.put(KEY_PROVIDER, sample.getProvider().getID()); values.put(KEY_INTENSITY, sample.getRawIntensity()); values.put(KEY_STEPS, sample.getSteps()); + values.put(KEY_CUSTOM_SHORT, sample.getCustomShortValue()); values.put(KEY_TYPE, sample.getRawKind()); db.insert(TABLE_GBACTIVITYSAMPLES, null, values); @@ -117,7 +119,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl * @param kind the raw activity kind of the sample */ @Override - public void addGBActivitySample(int timestamp, byte provider, short intensity, short steps, byte kind) { + public void addGBActivitySample(int timestamp, byte provider, short intensity, short steps, byte kind, short customShortValue) { if (intensity < 0) { LOG.error("negative intensity received, ignoring"); intensity = 0; @@ -127,6 +129,11 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl steps = 0; } + if (customShortValue < 0) { + LOG.error("negative short value received, ignoring"); + customShortValue = 0; + } + try (SQLiteDatabase db = this.getWritableDatabase()) { ContentValues values = new ContentValues(); values.put(KEY_TIMESTAMP, timestamp); @@ -134,6 +141,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl values.put(KEY_INTENSITY, intensity); values.put(KEY_STEPS, steps); values.put(KEY_TYPE, kind); + values.put(KEY_CUSTOM_SHORT, customShortValue); db.insert(TABLE_GBACTIVITYSAMPLES, null, values); } @@ -144,8 +152,8 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl try (SQLiteDatabase db = this.getWritableDatabase()) { String sql = "INSERT INTO " + TABLE_GBACTIVITYSAMPLES + " (" + KEY_TIMESTAMP + "," + - KEY_PROVIDER + "," + KEY_INTENSITY + "," + KEY_STEPS + "," + KEY_TYPE + ")" + - " VALUES (?,?,?,?,?);"; + KEY_PROVIDER + "," + KEY_INTENSITY + "," + KEY_STEPS + "," + KEY_TYPE + "," + KEY_CUSTOM_SHORT + ")" + + " VALUES (?,?,?,?,?,?);"; SQLiteStatement statement = db.compileStatement(sql); db.beginTransaction(); @@ -156,6 +164,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl statement.bindLong(3, activitySample.getRawIntensity()); statement.bindLong(4, activitySample.getSteps()); statement.bindLong(5, activitySample.getRawKind()); + statement.bindLong(6, activitySample.getCustomShortValue()); statement.execute(); } db.setTransactionSuccessful(); @@ -216,7 +225,8 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl cursor.getInt(cursor.getColumnIndex(KEY_TIMESTAMP)), cursor.getShort(cursor.getColumnIndex(KEY_INTENSITY)), cursor.getShort(cursor.getColumnIndex(KEY_STEPS)), - (byte) cursor.getShort(cursor.getColumnIndex(KEY_TYPE))); + (byte) cursor.getShort(cursor.getColumnIndex(KEY_TYPE)), + cursor.getShort(cursor.getColumnIndex(KEY_CUSTOM_SHORT))); samples.add(sample); } while (cursor.moveToNext()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java index 696f30d7..5d1a72b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBConstants.java @@ -10,5 +10,6 @@ public class DBConstants { public static final String KEY_PROVIDER = "provider"; public static final String KEY_INTENSITY = "intensity"; public static final String KEY_STEPS = "steps"; + public static final String KEY_CUSTOM_SHORT = "customShort"; public static final String KEY_TYPE = "type"; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 6e7398e9..7dfeb5c8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -24,7 +24,7 @@ public interface DBHandler { List getSleepSamples(int tsFrom, int tsTo, SampleProvider provider); - void addGBActivitySample(int timestamp, byte provider, short intensity, short steps, byte kind); + void addGBActivitySample(int timestamp, byte provider, short intensity, short steps, byte kind, short heartrate); void addGBActivitySamples(ActivitySample[] activitySamples); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java index f2303367..c805e352 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java @@ -5,27 +5,25 @@ import android.database.sqlite.SQLiteDatabase; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROVIDER; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_STEPS_PER_DAY; /** - * Adds a table "STEPS_PER_DAY". + * Adds a column "customShort" to the table "GBActivitySamples" */ public class ActivityDBUpdate_6 implements DBUpdateScript { @Override public void upgradeSchema(SQLiteDatabase db) { - String CREATE_STEPS_PER_DAY_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_STEPS_PER_DAY + " (" - + KEY_TIMESTAMP + " INT," - + KEY_PROVIDER + " TINYINT," - + KEY_STEPS + " MEDIUMINT," - + " PRIMARY KEY (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)" + DBHelper.getWithoutRowId(); - db.execSQL(CREATE_STEPS_PER_DAY_TABLE); + String ADD_COLUMN_CUSTOM_SHORT = "ALTER TABLE " + TABLE_GBACTIVITYSAMPLES + " ADD COLUMN " + + KEY_CUSTOM_SHORT + " INT;"; + db.execSQL(ADD_COLUMN_CUSTOM_SHORT); } @Override public void downgradeSchema(SQLiteDatabase db) { - DBHelper.dropTable(TABLE_STEPS_PER_DAY, db); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java new file mode 100644 index 00000000..e23d13e5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_X.java @@ -0,0 +1,31 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; + +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROVIDER; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_TIMESTAMP; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_STEPS_PER_DAY; + +/** + * Adds a table "STEPS_PER_DAY". + */ +public class ActivityDBUpdate_X implements DBUpdateScript { + @Override + public void upgradeSchema(SQLiteDatabase db) { + String CREATE_STEPS_PER_DAY_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_STEPS_PER_DAY + " (" + + KEY_TIMESTAMP + " INT," + + KEY_PROVIDER + " TINYINT," + + KEY_STEPS + " MEDIUMINT," + + " PRIMARY KEY (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)" + DBHelper.getWithoutRowId(); + db.execSQL(CREATE_STEPS_PER_DAY_TABLE); + } + + @Override + public void downgradeSchema(SQLiteDatabase db) { + DBHelper.dropTable(TABLE_STEPS_PER_DAY, db); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java index f75ab4dc..6ade2e63 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBActivitySample.java @@ -9,19 +9,19 @@ public class GBActivitySample implements ActivitySample { private final SampleProvider provider; private final short intensity; private final short steps; - private final short heartrate; private final byte type; + private final short customShortValue; public GBActivitySample(SampleProvider provider, int timestamp, short intensity, short steps, byte type) { - this(provider, timestamp, intensity, steps, (short) 0, type); + this(provider, timestamp, intensity, steps, type, (short) 0); } - public GBActivitySample(SampleProvider provider, int timestamp, short intensity, short steps, short heartrate, byte type) { + public GBActivitySample(SampleProvider provider, int timestamp, short intensity, short steps, byte type, short customShortValue) { this.timestamp = timestamp; this.provider = provider; this.intensity = intensity; this.steps = steps; - this.heartrate = heartrate; + this.customShortValue = customShortValue; this.type = type; validate(); } @@ -36,8 +36,8 @@ public class GBActivitySample implements ActivitySample { if (timestamp < 0) { throw new IllegalArgumentException("timestamp must be >= 0"); } - if (heartrate < 0) { - throw new IllegalArgumentException("heartrate must be >= 0"); + if (customShortValue < 0) { + throw new IllegalArgumentException("customShortValue must be >= 0"); } } @@ -77,8 +77,8 @@ public class GBActivitySample implements ActivitySample { } @Override - public short getHeartRate() { - return heartrate; + public short getCustomShortValue() { + return customShortValue; } @Override @@ -87,7 +87,7 @@ public class GBActivitySample implements ActivitySample { "timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(timestamp)) + ", intensity=" + getIntensity() + ", steps=" + getSteps() + - ", heartrate=" + getHeartRate() + + ", customShortValue=" + getCustomShortValue() + ", type=" + getKind() + '}'; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java index 6d332423..b515c5ef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java @@ -42,5 +42,5 @@ public interface ActivitySample { */ short getSteps(); - short getHeartRate(); + short getCustomShortValue(); } 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 6cefcef6..84f9c7cc 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 @@ -305,7 +305,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { } int bpm = getBytesPerMinuteOfActivityData(); LOG.debug("flushing activity data samples: " + activityStruct.activityDataHolderProgress / bpm); - byte category, intensity, steps, heartrate; + byte category, intensity, steps, heartrate = 0; DBHandler dbHandler = null; try { @@ -334,7 +334,8 @@ public class FetchActivityOperation extends AbstractMiBandOperation { timestampInSeconds, (short) (intensity & 0xff), (short) (steps & 0xff), - category); + category, + (short) (heartrate & 0xff)); // next minute minutes++; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java index 667763d6..10655018 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java @@ -55,7 +55,7 @@ public class AppMessageHandlerGBPebble extends AppMessageHandler { byte type = (byte) ((sample & 0xe000) >>> 13); byte intensity = (byte) ((sample & 0x1f80) >>> 7); byte steps = (byte) (sample & 0x007f); - db.addGBActivitySample(timestamp + offset_seconds, SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE, (short) (intensity & 0xff), (short) (steps & 0xff), type); + db.addGBActivitySample(timestamp + offset_seconds, SampleProvider.PROVIDER_PEBBLE_GADGETBRIDGE, (short) (intensity & 0xff), (short) (steps & 0xff), type, (short)0); offset_seconds += 60; } } catch (GBException e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index 07bdfe1a..4507fbc4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -93,7 +93,7 @@ public class AppMessageHandlerMorpheuz extends AppMessageHandler { DBHandler db = null; try { db = GBApplication.acquireDB(); - db.addGBActivitySample(recording_base_timestamp + index * 600, SampleProvider.PROVIDER_PEBBLE_MORPHEUZ, intensity, (byte) 0, type); + db.addGBActivitySample(recording_base_timestamp + index * 600, SampleProvider.PROVIDER_PEBBLE_MORPHEUZ, intensity, (byte) 0, type, (short)0); } catch (GBException e) { LOG.error("Error acquiring database", e); } finally { diff --git a/app/src/main/res/layout-land/fragment_sleepchart.xml b/app/src/main/res/layout-land/fragment_sleepchart.xml index 5b4e0b40..0761e1b6 100644 --- a/app/src/main/res/layout-land/fragment_sleepchart.xml +++ b/app/src/main/res/layout-land/fragment_sleepchart.xml @@ -11,7 +11,7 @@ android:layout_weight="40"> - - - #ff808080 #1f000000 + #b40000 #0071b7 #4c5aff From 9e636d66f602287b710705cea2bbf1d6dc883d78 Mon Sep 17 00:00:00 2001 From: Kasha Date: Mon, 28 Dec 2015 14:38:56 +0100 Subject: [PATCH 045/274] Initial heart rate support by KashaMalaga #178 (removed unrelated Android M fixes and squashed commits) --- .../activities/DebugActivity.java | 8 +++ .../gadgetbridge/devices/EventHandler.java | 2 + .../devices/miband/MiBandService.java | 16 ++++++ .../gadgetbridge/impl/GBDeviceService.java | 6 +++ .../gadgetbridge/model/DeviceService.java | 2 +- .../service/DeviceCommunicationService.java | 5 ++ .../service/ServiceDeviceSupport.java | 8 +++ .../service/devices/miband/MiBandSupport.java | 54 +++++++++++++++++-- .../service/devices/pebble/PebbleSupport.java | 5 ++ app/src/main/res/layout/activity_debug.xml | 15 ++++-- 10 files changed, 112 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index a4fbe772..c5d05183 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -53,6 +53,7 @@ public class DebugActivity extends Activity { private Button setMusicInfoButton; private Button setTimeButton; private Button rebootButton; + private Button HearRateButton; private Button exportDBButton; private Button importDBButton; private Button deleteDBButton; @@ -185,6 +186,13 @@ public class DebugActivity extends Activity { GBApplication.deviceService().onReboot(); } }); + HearRateButton = (Button) findViewById(R.id.HearRateButton); + HearRateButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + GBApplication.deviceService().onHearRateTest(); + } + }); setMusicInfoButton = (Button) findViewById(R.id.setMusicInfoButton); setMusicInfoButton.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 6d7bf853..1136f084 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -40,6 +40,8 @@ public interface EventHandler { void onReboot(); + void onHearRateTest(); + void onFindDevice(boolean start); void onScreenshotReq(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java index 349c0f48..7cf008bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java @@ -14,6 +14,8 @@ public class MiBandService { public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0")); + public static final UUID UUID_SERVICE_HEART_RATE = UUID.fromString(String.format(BASE_UUID, "180D")); + public static final UUID UUID_CHARACTERISTIC_DEVICE_INFO = UUID.fromString(String.format(BASE_UUID, "FF01")); public static final UUID UUID_CHARACTERISTIC_DEVICE_NAME = UUID.fromString(String.format(BASE_UUID, "FF02")); @@ -44,6 +46,11 @@ public class MiBandService { public static final UUID UUID_CHARACTERISTIC_PAIR = UUID.fromString(String.format(BASE_UUID, "FF0F")); + public static final UUID UUID_CHAR_HEART_RATE_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID, "2A39")); + public static final UUID UUID_CHAR_HEART_RATE_MEASUREMENT = UUID.fromString(String.format(BASE_UUID, "2A37")); + + + /* FURTHER UUIDS that were mixed with the other params below. The base UUID for these is unknown */ public static final String UUID_SERVICE_WEIGHT_SERVICE = "00001530-0000-3512-2118-0009af100700"; @@ -153,6 +160,12 @@ public class MiBandService { public static final byte COMMAND_SET_REALTIME_STEP = 0x10; + // Test HR + public static final byte COMMAND_SET_HR_SLEEP = 0x0 ; + public static final byte COMMAND_SET__HR_CONTINUOUS = 0x1 ; + public static final byte COMMAND_SET_HR_MANUAL = 0x2 ; + + /* FURTHER COMMANDS: unchecked therefore left commented @@ -213,6 +226,7 @@ public class MiBandService { static { MIBAND_DEBUG = new HashMap<>(); MIBAND_DEBUG.put(UUID_SERVICE_MIBAND_SERVICE, "MiBand Service"); + MIBAND_DEBUG.put(UUID_SERVICE_HEART_RATE, "MiBand HR Service"); MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_INFO, "Device Info"); MIBAND_DEBUG.put(UUID_CHARACTERISTIC_DEVICE_NAME, "Device Name"); @@ -229,6 +243,8 @@ public class MiBandService { MIBAND_DEBUG.put(UUID_CHARACTERISTIC_TEST, "Test"); MIBAND_DEBUG.put(UUID_CHARACTERISTIC_SENSOR_DATA, "Sensor Data"); MIBAND_DEBUG.put(UUID_CHARACTERISTIC_PAIR, "Pair"); + MIBAND_DEBUG.put(UUID_CHAR_HEART_RATE_CONTROL_POINT, "Heart Rate Control Point"); + MIBAND_DEBUG.put(UUID_CHAR_HEART_RATE_MEASUREMENT, "Heart Rate Measure"); } public static String lookup(UUID uuid, String fallback) { 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 cee7a5e2..54cc6c23 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -171,6 +171,12 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onHearRateTest() { + Intent intent = createIntent().setAction(ACTION_HEARTRATE_TEST); + invokeService(intent); + } + @Override public void onFindDevice(boolean start) { Intent intent = createIntent().setAction(ACTION_FIND_DEVICE) 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 af4a6d1b..bd4f33e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -26,13 +26,13 @@ public interface DeviceService extends EventHandler { String ACTION_DELETEAPP = PREFIX + ".action.deleteapp"; String ACTION_INSTALL = PREFIX + ".action.install"; String ACTION_REBOOT = PREFIX + ".action.reboot"; + String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test"; String ACTION_FETCH_ACTIVITY_DATA = PREFIX + ".action.fetch_activity_data"; String ACTION_DISCONNECT = PREFIX + ".action.disconnect"; String ACTION_FIND_DEVICE = PREFIX + ".action.find_device"; String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms"; String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps"; String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps"; - String EXTRA_DEVICE_ADDRESS = "device_address"; String EXTRA_NOTIFICATION_BODY = "notification_body"; String EXTRA_NOTIFICATION_FLAGS = "notification_flags"; 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 d85c1290..15e6f1c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -47,6 +47,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DI import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_HEARTRATE_TEST; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_INSTALL; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_NOTIFICATION; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_REBOOT; @@ -251,6 +252,10 @@ public class DeviceCommunicationService extends Service { mDeviceSupport.onReboot(); break; } + case ACTION_HEARTRATE_TEST: { + mDeviceSupport.onHearRateTest(); + break; + } case ACTION_FETCH_ACTIVITY_DATA: { mDeviceSupport.onFetchActivityData(); break; 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 b75b1c59..8faf48a9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -194,6 +194,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onReboot(); } + @Override + public void onHearRateTest() { + if (checkBusy("heartrate")) { + return; + } + delegate.onHearRateTest(); + } + @Override public void onFindDevice(boolean start) { if (checkBusy("find device")) { 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 43d6c75d..2e839967 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 @@ -89,6 +89,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS); addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE); addSupportedService(MiBandService.UUID_SERVICE_MIBAND_SERVICE); + addSupportedService(MiBandService.UUID_SERVICE_HEART_RATE); addSupportedService(GattService.UUID_SERVICE_IMMEDIATE_ALERT); } @@ -106,7 +107,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { .setCurrentTime(builder) .requestBatteryInfo(builder) .setInitialized(builder); - + // heartrate(builder) + // .requestHRInfo(builder); return builder; } @@ -199,6 +201,10 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } static final byte[] reboot = new byte[]{MiBandService.COMMAND_REBOOT}; + +// static final byte[] HeartMode = new byte[]{MiBandService.COMMAND_SET_HR_MANUAL}; + static final byte[] HeartMode = new byte[]{MiBandService.COMMAND_SET_HR_SLEEP}; + static final byte[] startRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 1}; static final byte[] stopRealTimeStepsNotifications = new byte[]{MiBandService.COMMAND_SET_REALTIME_STEPS_NOTIFICATION, 0}; @@ -245,6 +251,30 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return this; } + /* private MiBandSupport requestHRInfo(TransactionBuilder builder) { + LOG.debug("Requesting HR Info!"); + BluetoothGattCharacteristic HRInfo = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT); + builder.read(HRInfo); + BluetoothGattCharacteristic HR_Point = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + builder.read(HR_Point); + return this; + } + *//** + * Part of HR test. Do not call manually. + * + * @param transaction + * @return + *//* + private MiBandSupport heartrate(TransactionBuilder transaction) { + LOG.info("Attempting to read HR ..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT); + if (characteristic != null) { + transaction.write(characteristic, new byte[]{MiBandService.COMMAND_SET__HR_CONTINUOUS}); + } else { + LOG.info("Unable to read HR from MI device -- characteristic not available"); + } + return this; + }*/ /** * Part of device initialization process. Do not call manually. * @@ -493,7 +523,16 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { LOG.error("Unable to reboot MI", ex); } } - + @Override + public void onHearRateTest() { + try { + TransactionBuilder builder = performInitialized("HeartRateTest"); + builder.write(getCharacteristic(MiBandService.UUID_CHAR_HEART_RATE_CONTROL_POINT), HeartMode); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to read HearRate in MI1S", ex); + } + } @Override public void onFindDevice(boolean start) { isLocatingDevice = start; @@ -613,7 +652,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { handleNotificationNotif(characteristic.getValue()); } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); - } else { + } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { + handleRealtimeSteps(characteristic.getValue()); + } else if (MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { + logMessageContent(characteristic.getValue()); + } + else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); } @@ -631,8 +675,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { handleDeviceName(characteristic.getValue(), status); } else if (MiBandService.UUID_CHARACTERISTIC_BATTERY.equals(characteristicUUID)) { handleBatteryInfo(characteristic.getValue(), status); - } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { - handleRealtimeSteps(characteristic.getValue()); + } else if (MiBandService.UUID_CHAR_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { + logMessageContent(characteristic.getValue()); } else { LOG.info("Unhandled characteristic read: "+ characteristicUUID); logMessageContent(characteristic.getValue()); 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 590bbb1d..d045789c 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 @@ -37,6 +37,11 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { getDeviceIOThread().installApp(uri, 0); } + @Override + public void onHearRateTest() { + + } + @Override public synchronized PebbleIoThread getDeviceIOThread() { return (PebbleIoThread) super.getDeviceIOThread(); diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index 799b7ab5..9bc0997a 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -73,7 +73,7 @@ android:layout_column="0" android:layout_columnSpan="3" android:layout_gravity="fill_horizontal" - android:layout_row="9" + android:layout_row="10" android:text="create test notification" /> + +

+

Incoming configuration data:

+
+ +
+ diff --git a/app/src/main/assets/app_config/js/Uri.js b/app/src/main/assets/app_config/js/Uri.js new file mode 100644 index 00000000..a3fe4998 --- /dev/null +++ b/app/src/main/assets/app_config/js/Uri.js @@ -0,0 +1,458 @@ +/*! + * jsUri + * https://github.com/derek-watson/jsUri + * + * Copyright 2013, Derek Watson + * Released under the MIT license. + * + * Includes parseUri regular expressions + * http://blog.stevenlevithan.com/archives/parseuri + * Copyright 2007, Steven Levithan + * Released under the MIT license. + */ + + /*globals define, module */ + +(function(global) { + + var re = { + starts_with_slashes: /^\/+/, + ends_with_slashes: /\/+$/, + pluses: /\+/g, + query_separator: /[&;]/, + uri_parser: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*)(?::([^:@\/]*))?)?@)?(\[[0-9a-fA-F:.]+\]|[^:\/?#]*)(?::(\d+|(?=:)))?(:)?)((((?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ + }; + + /** + * Define forEach for older js environments + * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach#Compatibility + */ + if (!Array.prototype.forEach) { + Array.prototype.forEach = function(callback, thisArg) { + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + var O = Object(this); + var len = O.length >>> 0; + + if (typeof callback !== "function") { + throw new TypeError(callback + ' is not a function'); + } + + if (arguments.length > 1) { + T = thisArg; + } + + k = 0; + + while (k < len) { + var kValue; + if (k in O) { + kValue = O[k]; + callback.call(T, kValue, k, O); + } + k++; + } + }; + } + + /** + * unescape a query param value + * @param {string} s encoded value + * @return {string} decoded value + */ + function decode(s) { + if (s) { + s = s.toString().replace(re.pluses, '%20'); + s = decodeURIComponent(s); + } + return s; + } + + /** + * Breaks a uri string down into its individual parts + * @param {string} str uri + * @return {object} parts + */ + function parseUri(str) { + var parser = re.uri_parser; + var parserKeys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "isColonUri", "relative", "path", "directory", "file", "query", "anchor"]; + var m = parser.exec(str || ''); + var parts = {}; + + parserKeys.forEach(function(key, i) { + parts[key] = m[i] || ''; + }); + + return parts; + } + + /** + * Breaks a query string down into an array of key/value pairs + * @param {string} str query + * @return {array} array of arrays (key/value pairs) + */ + function parseQuery(str) { + var i, ps, p, n, k, v, l; + var pairs = []; + + if (typeof(str) === 'undefined' || str === null || str === '') { + return pairs; + } + + if (str.indexOf('?') === 0) { + str = str.substring(1); + } + + ps = str.toString().split(re.query_separator); + + for (i = 0, l = ps.length; i < l; i++) { + p = ps[i]; + n = p.indexOf('='); + + if (n !== 0) { + k = decode(p.substring(0, n)); + v = decode(p.substring(n + 1)); + pairs.push(n === -1 ? [p, null] : [k, v]); + } + + } + return pairs; + } + + /** + * Creates a new Uri object + * @constructor + * @param {string} str + */ + function Uri(str) { + this.uriParts = parseUri(str); + this.queryPairs = parseQuery(this.uriParts.query); + this.hasAuthorityPrefixUserPref = null; + } + + /** + * Define getter/setter methods + */ + ['protocol', 'userInfo', 'host', 'port', 'path', 'anchor'].forEach(function(key) { + Uri.prototype[key] = function(val) { + if (typeof val !== 'undefined') { + this.uriParts[key] = val; + } + return this.uriParts[key]; + }; + }); + + /** + * if there is no protocol, the leading // can be enabled or disabled + * @param {Boolean} val + * @return {Boolean} + */ + Uri.prototype.hasAuthorityPrefix = function(val) { + if (typeof val !== 'undefined') { + this.hasAuthorityPrefixUserPref = val; + } + + if (this.hasAuthorityPrefixUserPref === null) { + return (this.uriParts.source.indexOf('//') !== -1); + } else { + return this.hasAuthorityPrefixUserPref; + } + }; + + Uri.prototype.isColonUri = function (val) { + if (typeof val !== 'undefined') { + this.uriParts.isColonUri = !!val; + } else { + return !!this.uriParts.isColonUri; + } + }; + + /** + * Serializes the internal state of the query pairs + * @param {string} [val] set a new query string + * @return {string} query string + */ + Uri.prototype.query = function(val) { + var s = '', i, param, l; + + if (typeof val !== 'undefined') { + this.queryPairs = parseQuery(val); + } + + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (s.length > 0) { + s += '&'; + } + if (param[1] === null) { + s += param[0]; + } else { + s += param[0]; + s += '='; + if (typeof param[1] !== 'undefined') { + s += encodeURIComponent(param[1]); + } + } + } + return s.length > 0 ? '?' + s : s; + }; + + /** + * returns the first query param value found for the key + * @param {string} key query key + * @return {string} first value found for key + */ + Uri.prototype.getQueryParamValue = function (key) { + var param, i, l; + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (key === param[0]) { + return param[1]; + } + } + }; + + /** + * returns an array of query param values for the key + * @param {string} key query key + * @return {array} array of values + */ + Uri.prototype.getQueryParamValues = function (key) { + var arr = [], i, param, l; + for (i = 0, l = this.queryPairs.length; i < l; i++) { + param = this.queryPairs[i]; + if (key === param[0]) { + arr.push(param[1]); + } + } + return arr; + }; + + /** + * removes query parameters + * @param {string} key remove values for key + * @param {val} [val] remove a specific value, otherwise removes all + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.deleteQueryParam = function (key, val) { + var arr = [], i, param, keyMatchesFilter, valMatchesFilter, l; + + for (i = 0, l = this.queryPairs.length; i < l; i++) { + + param = this.queryPairs[i]; + keyMatchesFilter = decode(param[0]) === decode(key); + valMatchesFilter = param[1] === val; + + if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && (!keyMatchesFilter || !valMatchesFilter))) { + arr.push(param); + } + } + + this.queryPairs = arr; + + return this; + }; + + /** + * adds a query parameter + * @param {string} key add values for key + * @param {string} val value to add + * @param {integer} [index] specific index to add the value at + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.addQueryParam = function (key, val, index) { + if (arguments.length === 3 && index !== -1) { + index = Math.min(index, this.queryPairs.length); + this.queryPairs.splice(index, 0, [key, val]); + } else if (arguments.length > 0) { + this.queryPairs.push([key, val]); + } + return this; + }; + + /** + * test for the existence of a query parameter + * @param {string} key check values for key + * @return {Boolean} true if key exists, otherwise false + */ + Uri.prototype.hasQueryParam = function (key) { + var i, len = this.queryPairs.length; + for (i = 0; i < len; i++) { + if (this.queryPairs[i][0] == key) + return true; + } + return false; + }; + + /** + * replaces query param values + * @param {string} key key to replace value for + * @param {string} newVal new value + * @param {string} [oldVal] replace only one specific value (otherwise replaces all) + * @return {Uri} returns self for fluent chaining + */ + Uri.prototype.replaceQueryParam = function (key, newVal, oldVal) { + var index = -1, len = this.queryPairs.length, i, param; + + if (arguments.length === 3) { + for (i = 0; i < len; i++) { + param = this.queryPairs[i]; + if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) { + index = i; + break; + } + } + if (index >= 0) { + this.deleteQueryParam(key, decode(oldVal)).addQueryParam(key, newVal, index); + } + } else { + for (i = 0; i < len; i++) { + param = this.queryPairs[i]; + if (decode(param[0]) === decode(key)) { + index = i; + break; + } + } + this.deleteQueryParam(key); + this.addQueryParam(key, newVal, index); + } + return this; + }; + + /** + * Define fluent setter methods (setProtocol, setHasAuthorityPrefix, etc) + */ + ['protocol', 'hasAuthorityPrefix', 'isColonUri', 'userInfo', 'host', 'port', 'path', 'query', 'anchor'].forEach(function(key) { + var method = 'set' + key.charAt(0).toUpperCase() + key.slice(1); + Uri.prototype[method] = function(val) { + this[key](val); + return this; + }; + }); + + /** + * Scheme name, colon and doubleslash, as required + * @return {string} http:// or possibly just // + */ + Uri.prototype.scheme = function() { + var s = ''; + + if (this.protocol()) { + s += this.protocol(); + if (this.protocol().indexOf(':') !== this.protocol().length - 1) { + s += ':'; + } + s += '//'; + } else { + if (this.hasAuthorityPrefix() && this.host()) { + s += '//'; + } + } + + return s; + }; + + /** + * Same as Mozilla nsIURI.prePath + * @return {string} scheme://user:password@host:port + * @see https://developer.mozilla.org/en/nsIURI + */ + Uri.prototype.origin = function() { + var s = this.scheme(); + + if (this.userInfo() && this.host()) { + s += this.userInfo(); + if (this.userInfo().indexOf('@') !== this.userInfo().length - 1) { + s += '@'; + } + } + + if (this.host()) { + s += this.host(); + if (this.port() || (this.path() && this.path().substr(0, 1).match(/[0-9]/))) { + s += ':' + this.port(); + } + } + + return s; + }; + + /** + * Adds a trailing slash to the path + */ + Uri.prototype.addTrailingSlash = function() { + var path = this.path() || ''; + + if (path.substr(-1) !== '/') { + this.path(path + '/'); + } + + return this; + }; + + /** + * Serializes the internal state of the Uri object + * @return {string} + */ + Uri.prototype.toString = function() { + var path, s = this.origin(); + + if (this.isColonUri()) { + if (this.path()) { + s += ':'+this.path(); + } + } else if (this.path()) { + path = this.path(); + if (!(re.ends_with_slashes.test(s) || re.starts_with_slashes.test(path))) { + s += '/'; + } else { + if (s) { + s.replace(re.ends_with_slashes, '/'); + } + path = path.replace(re.starts_with_slashes, '/'); + } + s += path; + } else { + if (this.host() && (this.query().toString() || this.anchor())) { + s += '/'; + } + } + if (this.query().toString()) { + s += this.query().toString(); + } + + if (this.anchor()) { + if (this.anchor().indexOf('#') !== 0) { + s += '#'; + } + s += this.anchor(); + } + + return s; + }; + + /** + * Clone a Uri object + * @return {Uri} duplicate copy of the Uri + */ + Uri.prototype.clone = function() { + return new Uri(this.toString()); + }; + + /** + * export via AMD or CommonJS, otherwise leak a global + */ + if (typeof define === 'function' && define.amd) { + define(function() { + return Uri; + }); + } else if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = Uri; + } else { + global.Uri = Uri; + } +}(this)); diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js new file mode 100644 index 00000000..af68a965 --- /dev/null +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -0,0 +1,95 @@ +function loadScript(url, callback) { + // Adding the script tag to the head as suggested before + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = url; + + // Then bind the event to the callback function. + // There are several events for cross browser compatibility. + script.onreadystatechange = callback; + script.onload = callback; + + // Fire the loading + head.appendChild(script); +} + +function getURLVariable(variable, defaultValue) { + // Find all URL parameters + var query = location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + + // If the query variable parameter is found, decode it to use and return it for use + if (pair[0] === variable) { + return decodeURIComponent(pair[1]); + } + } + return defaultValue || false; +} + +function gbPebble() { + this.configurationURL = null; + this.configurationValues = null; + + this.addEventListener = function(e, f) { + if(e == 'showConfiguration') { + this.showConfiguration = f; + } + if(e == 'webviewclosed') { + this.parseconfig = f; + } + if(e == 'appmessage') { + this.appmessage = f; + } + } + + this.actuallyOpenURL = function() { + window.open(this.configurationURL.toString(), "config"); + } + + this.actuallySendData = function() { + GBjs.sendAppMessage(this.configurationValues); + } + + //needs to be called like this because of original Pebble function name + this.openURL = function(url) { + document.getElementById("config_url").innerHTML=url; + var UUID = GBjs.getAppUUID(); + this.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); + } + + this.getActiveWatchInfo = function() { + return JSON.parse(GBjs.getActiveWatchInfo()); + } + + this.sendAppMessage = function (dict, callback){ + this.configurationValues = JSON.stringify(dict); + document.getElementById("jsondata").innerHTML=this.configurationValues; + return callback; + } + + this.ready = function(e) { + GBjs.gbLog("ready called"); + } +} + +var Pebble = new gbPebble(); + +var jsConfigFile = GBjs.getAppConfigurationFile(); +if (jsConfigFile != null) { + loadScript(jsConfigFile, function() { + if (getURLVariable('config') == 'true') { + document.getElementById('step1').style.display="none"; + var json_string = unescape(getURLVariable('json')); + var t = new Object(); + t.response = json_string; + if (json_string != '') + Pebble.parseconfig(t); + } else { + document.getElementById('step2').style.display="none"; + Pebble.showConfiguration(); + } + }); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index d87f7241..0ff6333a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -189,6 +189,11 @@ public class AppManagerActivity extends Activity { case R.id.appmanager_health_activate: GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; + case R.id.appmanager_app_configure: + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); + startIntent.putExtra("app_uuid", selectedApp.getUUID()); + startActivity(startIntent); + return true; default: return super.onContextItemSelected(item); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java new file mode 100644 index 00000000..cb79cc7a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -0,0 +1,153 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.util.Pair; +import android.webkit.JavascriptInterface; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class ExternalPebbleJSActivity extends Activity { + + private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); + + //TODO: get device + private Uri uri; + private UUID appUuid; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + String queryString = ""; + uri = getIntent().getData(); + if (uri != null) { + //getting back with configuration data + appUuid = UUID.fromString(uri.getHost()); + queryString = uri.getEncodedQuery(); + } else { + appUuid = (UUID) getIntent().getSerializableExtra("app_uuid"); + } + + setContentView(R.layout.activity_external_pebble_js); + getActionBar().setDisplayHomeAsUpEnabled(true); + + WebView myWebView = (WebView) findViewById(R.id.configureWebview); + myWebView.clearCache(true); + WebSettings webSettings = myWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + //needed to access the DOM + webSettings.setDomStorageEnabled(true); + + JSInterface gbJSInterface = new JSInterface(); + myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); + + myWebView.loadUrl("file:///android_asset/app_config/configure.html?"+queryString); + } + + private JSONObject getAppConfigurationKeys() { + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, appUuid.toString() + ".json"); + if(configurationFile.exists()) { + String jsonstring = FileUtils.getStringFromFile(configurationFile); + JSONObject json = new JSONObject(jsonstring); + return json.getJSONObject("appKeys"); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + + private class JSInterface { + + public JSInterface () { + } + + @JavascriptInterface + public void gbLog(String msg) { + Log.d("WEBVIEW", msg); + } + + @JavascriptInterface + public void sendAppMessage(String msg) { + Log.d("from WEBVIEW", msg); + JSONObject knownKeys = getAppConfigurationKeys(); + ArrayList> pairs = new ArrayList<>(); + + try{ + JSONObject in = new JSONObject(msg); + String cur_key; + for (Iterator key = in.keys(); key.hasNext();) { + cur_key = key.next(); + int pebbleAppIndex = knownKeys.optInt(cur_key); + if (pebbleAppIndex != 0) { + //TODO: cast to integer (int32) / String? Is it needed? + pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); + } else { + GB.toast("Discarded key "+cur_key+", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + //TODO: send pairs to pebble. (encodeApplicationMessagePush(ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);) + } + + @JavascriptInterface + public String getActiveWatchInfo() { + //TODO: interact with GBDevice, see also todo at the beginning + JSONObject wi = new JSONObject(); + try { + wi.put("platform", "basalt"); + }catch (JSONException e) { + e.printStackTrace(); + } + //Json not supported apparently, we need to cast back and forth + return wi.toString(); + } + + @JavascriptInterface + public String getAppConfigurationFile() { + try { + File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); + File configurationFile = new File(destDir, appUuid.toString() + "_config.js"); + if(configurationFile.exists()) { + return "file:///" + configurationFile.getAbsolutePath(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + @JavascriptInterface + public String getAppUUID() { + return appUuid.toString(); + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 4071c83d..243b5deb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -173,6 +173,24 @@ public class PBWInstallHandler implements InstallHandler { } catch (JSONException e) { LOG.error(e.getMessage(), e); } + + String jsConfigFile = mPBWReader.getJsConfigurationFile(); + + if (jsConfigFile != null) { + outputFile = new File(destDir, app.getUUID().toString() + "_config.js"); + try { + writer = new BufferedWriter(new FileWriter(outputFile)); + } catch (IOException e) { + LOG.error("Failed to open output file: " + e.getMessage(), e); + return; + } + try { + writer.write(jsConfigFile); + writer.close(); + } catch (IOException e) { + LOG.error("Failed to write to output file: " + e.getMessage(), e); + } + } } public boolean isValid() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index e463dd44..ee65b567 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -57,6 +57,7 @@ public class PBWReader { private short mAppVersion; private int mIconId; private int mFlags; + private String jsConfigurationFile = null; private JSONObject mAppKeys = null; @@ -212,6 +213,18 @@ public class PBWReader { e.printStackTrace(); break; } + } else if (fileName.equals("pebble-js-app.js")) { + LOG.info("Found JS file: app supports configuration."); + long bytes = ze.getSize(); + if (bytes > 65536) // that should be too much + break; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + while ((count = zis.read(buffer)) != -1) { + baos.write(buffer, 0, count); + } + + jsConfigurationFile = baos.toString(); } else if (fileName.equals(platformDir + "pebble-app.bin")) { zis.read(buffer, 0, 108); byte[] tmp_buf = new byte[32]; @@ -327,4 +340,8 @@ public class PBWReader { public JSONObject getAppKeysJSON() { return mAppKeys; } -} + + public String getJsConfigurationFile() { + return jsConfigurationFile; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_external_pebble_js.xml b/app/src/main/res/layout/activity_external_pebble_js.xml new file mode 100644 index 00000000..551fb927 --- /dev/null +++ b/app/src/main/res/layout/activity_external_pebble_js.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/menu/appmanager_context.xml b/app/src/main/res/menu/appmanager_context.xml index a139eb1a..dd5d22c3 100644 --- a/app/src/main/res/menu/appmanager_context.xml +++ b/app/src/main/res/menu/appmanager_context.xml @@ -12,5 +12,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ab85988..fc9e2a29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -224,6 +224,7 @@ Deactivate authenticating authentication required + Configure Zzz Add widget From 63d938559ebfae6d5b9f7a072e48a0f73346f51d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 11:52:30 +0100 Subject: [PATCH 072/274] pass GBDevice down to ExternalPebbleJSActivity to determine the platform version (aplite,basalt,chalk) --- .../activities/AppManagerActivity.java | 10 ++++++++++ .../activities/ExternalPebbleJSActivity.java | 13 ++++++++++--- .../devices/pebble/PBWInstallHandler.java | 11 ++--------- .../service/devices/pebble/PebbleIoThread.java | 11 ++--------- .../gadgetbridge/util/PebbleUtils.java | 15 +++++++++++++++ build.gradle | 2 +- 6 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 0ff6333a..471bc684 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -30,6 +30,7 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.GBDeviceAppAdapter; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -72,6 +73,7 @@ public class AppManagerActivity extends Activity { private final List appList = new ArrayList<>(); private GBDeviceAppAdapter mGBDeviceAppAdapter; private GBDeviceApp selectedApp = null; + private GBDevice mGBDevice = null; private List getSystemApps() { List systemApps = new ArrayList<>(); @@ -116,6 +118,13 @@ public class AppManagerActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); + } else { + throw new IllegalArgumentException("Must provide a device when invoking this activity"); + } + sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); setContentView(R.layout.activity_appmanager); @@ -192,6 +201,7 @@ public class AppManagerActivity extends Activity { case R.id.appmanager_app_configure: Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); startActivity(startIntent); return true; default: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index cb79cc7a..9bc3577e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -26,19 +26,27 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class ExternalPebbleJSActivity extends Activity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); - //TODO: get device private Uri uri; private UUID appUuid; + private GBDevice mGBDevice = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); + } else { + throw new IllegalArgumentException("Must provide a device when invoking this activity"); + } + String queryString = ""; uri = getIntent().getData(); if (uri != null) { @@ -119,10 +127,9 @@ public class ExternalPebbleJSActivity extends Activity { @JavascriptInterface public String getActiveWatchInfo() { - //TODO: interact with GBDevice, see also todo at the beginning JSONObject wi = new JSONObject(); try { - wi.put("platform", "basalt"); + wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); }catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 243b5deb..9794ca8f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -23,6 +23,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class PBWInstallHandler implements InstallHandler { private static final Logger LOG = LoggerFactory.getLogger(PBWInstallHandler.class); @@ -49,15 +50,7 @@ public class PBWInstallHandler implements InstallHandler { return; } - String hwRev = device.getHardwareVersion(); - String platformName; - if (hwRev.startsWith("snowy")) { - platformName = "basalt"; - } else if (hwRev.startsWith("spalding")) { - platformName = "chalk"; - } else { - platformName = "aplite"; - } + String platformName = PebbleUtils.getPlatformName(device.getHardwareVersion()); try { mPBWReader = new PBWReader(mUri, mContext, platformName); 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 e3ffaec4..0463cffc 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 @@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); @@ -577,15 +578,7 @@ public class PebbleIoThread extends GBDeviceIoThread { return; } - String hwRev = gbDevice.getHardwareVersion(); - String platformName; - if (hwRev.startsWith("snowy")) { - platformName = "basalt"; - } else if (hwRev.startsWith("spalding")) { - platformName = "chalk"; - } else { - platformName = "aplite"; - } + String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); try { mPBWReader = new PBWReader(uri, getContext(), platformName); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java new file mode 100644 index 00000000..b925c3fb --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class PebbleUtils { + public static String getPlatformName(String hwRev) { + String platformName; + if (hwRev.startsWith("snowy")) { + platformName = "basalt"; + } else if (hwRev.startsWith("spalding")) { + platformName = "chalk"; + } else { + platformName = "aplite"; + } + return platformName; + } +} diff --git a/build.gradle b/build.gradle index 6d40ae25..cf58ce20 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.0-beta6' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 860ded102257ae299295634c17c7649f8ef1fb5d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 11:54:07 +0100 Subject: [PATCH 073/274] refromat code --- .../activities/ExternalPebbleJSActivity.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 9bc3577e..b04dce90 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -70,14 +69,14 @@ public class ExternalPebbleJSActivity extends Activity { JSInterface gbJSInterface = new JSInterface(); myWebView.addJavascriptInterface(gbJSInterface, "GBjs"); - myWebView.loadUrl("file:///android_asset/app_config/configure.html?"+queryString); + myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString); } private JSONObject getAppConfigurationKeys() { try { File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File configurationFile = new File(destDir, appUuid.toString() + ".json"); - if(configurationFile.exists()) { + if (configurationFile.exists()) { String jsonstring = FileUtils.getStringFromFile(configurationFile); JSONObject json = new JSONObject(jsonstring); return json.getJSONObject("appKeys"); @@ -92,7 +91,7 @@ public class ExternalPebbleJSActivity extends Activity { private class JSInterface { - public JSInterface () { + public JSInterface() { } @JavascriptInterface @@ -106,17 +105,17 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject knownKeys = getAppConfigurationKeys(); ArrayList> pairs = new ArrayList<>(); - try{ + try { JSONObject in = new JSONObject(msg); String cur_key; - for (Iterator key = in.keys(); key.hasNext();) { + for (Iterator key = in.keys(); key.hasNext(); ) { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { //TODO: cast to integer (int32) / String? Is it needed? pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); } else { - GB.toast("Discarded key "+cur_key+", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); + GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } } } catch (JSONException e) { @@ -130,7 +129,7 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject wi = new JSONObject(); try { wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); - }catch (JSONException e) { + } catch (JSONException e) { e.printStackTrace(); } //Json not supported apparently, we need to cast back and forth @@ -142,8 +141,8 @@ public class ExternalPebbleJSActivity extends Activity { try { File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File configurationFile = new File(destDir, appUuid.toString() + "_config.js"); - if(configurationFile.exists()) { - return "file:///" + configurationFile.getAbsolutePath(); + if (configurationFile.exists()) { + return "file:///" + configurationFile.getAbsolutePath(); } } catch (IOException e) { e.printStackTrace(); From fa924ff9d8b47f74235da6ee9ef94dc6235c4e4a Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 13:05:51 +0100 Subject: [PATCH 074/274] Pebble: fix crash when navigating back from configuration activity --- app/src/main/AndroidManifest.xml | 1 + .../activities/ExternalPebbleJSActivity.java | 23 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b9b8c99c..c04cea7e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,7 @@ android:label="@string/preferences_miband_settings" android:parentActivityName=".activities.SettingsActivity" /> diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index b04dce90..85b1dcdd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -3,8 +3,10 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.Activity; import android.net.Uri; import android.os.Bundle; +import android.support.v4.app.NavUtils; import android.util.Log; import android.util.Pair; +import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; @@ -31,7 +33,6 @@ public class ExternalPebbleJSActivity extends Activity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); - private Uri uri; private UUID appUuid; private GBDevice mGBDevice = null; @@ -47,7 +48,7 @@ public class ExternalPebbleJSActivity extends Activity { } String queryString = ""; - uri = getIntent().getData(); + Uri uri = getIntent().getData(); if (uri != null) { //getting back with configuration data appUuid = UUID.fromString(uri.getHost()); @@ -81,9 +82,7 @@ public class ExternalPebbleJSActivity extends Activity { JSONObject json = new JSONObject(jsonstring); return json.getJSONObject("appKeys"); } - } catch (IOException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (IOException | JSONException e) { e.printStackTrace(); } return null; @@ -113,7 +112,8 @@ public class ExternalPebbleJSActivity extends Activity { int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { //TODO: cast to integer (int32) / String? Is it needed? - pairs.add(new Pair<>(pebbleAppIndex, (Object) in.get(cur_key))); + pairs.add(new Pair<>(pebbleAppIndex, in.get(cur_key))); + LOG.info(in.get(cur_key).getClass().toString()); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } @@ -156,4 +156,15 @@ public class ExternalPebbleJSActivity extends Activity { } } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; + } + return super.onOptionsItemSelected(item); + } + + } From 2a7f9226a0ba1217e0930b0aeae61de5a7369cb5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:23:17 +0100 Subject: [PATCH 075/274] Pebble: send configuration to watch TODO: handle booleans --- .../activities/ExternalPebbleJSActivity.java | 15 ++++++------ .../gadgetbridge/devices/EventHandler.java | 4 ++++ .../gadgetbridge/impl/GBDeviceService.java | 9 +++++++ .../gadgetbridge/model/DeviceService.java | 3 ++- .../service/DeviceCommunicationService.java | 7 ++++++ .../service/ServiceDeviceSupport.java | 8 +++++++ .../service/devices/miband/MiBandSupport.java | 5 ++++ .../service/devices/pebble/PebbleSupport.java | 24 +++++++++++++++++++ 8 files changed, 66 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 85b1dcdd..6f05a0e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -5,7 +5,6 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.util.Log; -import android.util.Pair; import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; @@ -19,10 +18,10 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -100,28 +99,28 @@ public class ExternalPebbleJSActivity extends Activity { @JavascriptInterface public void sendAppMessage(String msg) { - Log.d("from WEBVIEW", msg); + LOG.debug("from WEBVIEW: ", msg); JSONObject knownKeys = getAppConfigurationKeys(); - ArrayList> pairs = new ArrayList<>(); try { JSONObject in = new JSONObject(msg); + JSONObject out = new JSONObject(); String cur_key; for (Iterator key = in.keys(); key.hasNext(); ) { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { - //TODO: cast to integer (int32) / String? Is it needed? - pairs.add(new Pair<>(pebbleAppIndex, in.get(cur_key))); - LOG.info(in.get(cur_key).getClass().toString()); + out.put(String.valueOf(pebbleAppIndex), in.get(cur_key)); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } } + LOG.info(out.toString()); + GBApplication.deviceService().onAppConfiguration(appUuid, out.toString()); + } catch (JSONException e) { e.printStackTrace(); } - //TODO: send pairs to pebble. (encodeApplicationMessagePush(ENDPOINT_APPLICATIONMESSAGE, uuid, pairs);) } @JavascriptInterface diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 0d3de370..8036d874 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.net.Uri; import android.support.annotation.Nullable; +import android.util.Pair; import java.util.ArrayList; import java.util.UUID; @@ -36,6 +37,8 @@ public interface EventHandler { void onAppDelete(UUID uuid); + void onAppConfiguration(UUID appUuid, String config); + void onFetchActivityData(); void onReboot(); @@ -45,4 +48,5 @@ public interface EventHandler { void onFindDevice(boolean start); void onScreenshotReq(); + } 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 027a54c3..9839ade2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.Nullable; +import android.util.Pair; import java.util.ArrayList; import java.util.UUID; @@ -159,6 +160,14 @@ public class GBDeviceService implements DeviceService { invokeService(intent); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + Intent intent = createIntent().setAction(ACTION_APP_CONFIGURE) + .putExtra(EXTRA_APP_UUID, uuid) + .putExtra(EXTRA_APP_CONFIG, config); + invokeService(intent); + } + @Override public void onFetchActivityData() { Intent intent = createIntent().setAction(ACTION_FETCH_ACTIVITY_DATA); 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 bd4f33e9..263e174b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -15,7 +15,6 @@ public interface DeviceService extends EventHandler { String ACTION_START = PREFIX + ".action.start"; String ACTION_CONNECT = PREFIX + ".action.connect"; String ACTION_NOTIFICATION = PREFIX + ".action.notification"; - String ACTION_NOTIFICATION_SMS = PREFIX + ".action.notification_sms"; String ACTION_CALLSTATE = PREFIX + ".action.callstate"; String ACTION_SETTIME = PREFIX + ".action.settime"; String ACTION_SETMUSICINFO = PREFIX + ".action.setmusicinfo"; @@ -24,6 +23,7 @@ public interface DeviceService extends EventHandler { String ACTION_REQUEST_SCREENSHOT = PREFIX + ".action.request_screenshot"; String ACTION_STARTAPP = PREFIX + ".action.startapp"; String ACTION_DELETEAPP = PREFIX + ".action.deleteapp"; + String ACTION_APP_CONFIGURE = PREFIX + ".action.app_configure"; String ACTION_INSTALL = PREFIX + ".action.install"; String ACTION_REBOOT = PREFIX + ".action.reboot"; String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test"; @@ -51,6 +51,7 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_TRACK = "music_track"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; + String EXTRA_APP_CONFIG = "app_configt"; String EXTRA_URI = "uri"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; 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 bae6f8b9..f3b8c58b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -40,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; @@ -60,6 +61,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SE import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_STARTAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALARMS; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; @@ -306,6 +308,11 @@ public class DeviceCommunicationService extends Service { mDeviceSupport.onAppDelete(uuid); break; } + case ACTION_APP_CONFIGURE: { + UUID uuid = (UUID) intent.getSerializableExtra(EXTRA_APP_UUID); + String config = intent.getStringExtra(EXTRA_APP_CONFIG); + mDeviceSupport.onAppConfiguration(uuid, config); + } case ACTION_INSTALL: Uri uri = intent.getParcelableExtra(EXTRA_URI); if (uri != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index 4ddeff3a..d13c90d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -178,6 +178,14 @@ public class ServiceDeviceSupport implements DeviceSupport { delegate.onAppDelete(uuid); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + if (checkBusy("app configuration")) { + return; + } + delegate.onAppConfiguration(uuid, config); + } + @Override public void onFetchActivityData() { if (checkBusy("fetch activity data")) { 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 63c3c022..f412e803 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 @@ -657,6 +657,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { // not supported } + @Override + public void onAppConfiguration(UUID uuid, String config) { + // not supported + } + @Override public void onScreenshotReq() { // not supported 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 0f0b3399..66560c0d 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,8 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; import android.net.Uri; +import android.util.Pair; + +import org.json.JSONException; +import org.json.JSONObject; import java.util.ArrayList; +import java.util.Iterator; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; @@ -37,6 +43,24 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { getDeviceIOThread().installApp(uri, 0); } + @Override + public void onAppConfiguration(UUID uuid, String config) { + try { + ArrayList> pairs = new ArrayList<>(); + + JSONObject json = new JSONObject(config); + Iterator keysIterator = json.keys(); + while (keysIterator.hasNext()) { + String keyStr = keysIterator.next(); + Object object = json.get(keyStr); + pairs.add(new Pair<>(Integer.parseInt(keyStr), object)); + } + getDeviceIOThread().write(((PebbleProtocol) getDeviceProtocol()).encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, uuid, pairs)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + @Override public void onHeartRateTest() { From 902ff39c0bd07b7d81d585213e8a9381030d9e61 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:25:44 +0100 Subject: [PATCH 076/274] start app when about to be configured --- .../gadgetbridge/activities/AppManagerActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 471bc684..38aee186 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -199,6 +199,8 @@ public class AppManagerActivity extends Activity { GBApplication.deviceService().onInstallApp(Uri.parse("fake://health")); return true; case R.id.appmanager_app_configure: + GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); From 864e0953d9c42e28bfe925e3c760442309b57583 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 14:29:46 +0100 Subject: [PATCH 077/274] only allow starting AppManager after device is initalized (else platform cant be determined) --- .../freeyourgadget/gadgetbridge/activities/ControlCenter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 408592c2..e4b04a6b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -131,7 +131,7 @@ public class ControlCenter extends Activity { @Override public void onItemClick(AdapterView parent, View v, int position, long id) { GBDevice gbDevice = deviceList.get(position); - if (gbDevice.isConnected()) { + if (gbDevice.isInitialized()) { DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice); Class primaryActivity = coordinator.getPrimaryActivity(); if (primaryActivity != null) { From bd7b34985bfb3bc6ea1863a68568ab5e3084557e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 15:47:00 +0100 Subject: [PATCH 078/274] reformat code and optimize imports --- .../gadgetbridge/activities/AppManagerActivity.java | 2 +- .../freeyourgadget/gadgetbridge/devices/EventHandler.java | 1 - .../freeyourgadget/gadgetbridge/impl/GBDeviceService.java | 1 - .../freeyourgadget/gadgetbridge/model/ActivityUser.java | 2 +- .../gadgetbridge/service/devices/pebble/PebbleIoThread.java | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 38aee186..080dba67 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -200,7 +200,7 @@ public class AppManagerActivity extends Activity { return true; case R.id.appmanager_app_configure: GBApplication.deviceService().onAppStart(selectedApp.getUUID(), true); - + Intent startIntent = new Intent(getApplicationContext(), ExternalPebbleJSActivity.class); startIntent.putExtra("app_uuid", selectedApp.getUUID()); startIntent.putExtra(GBDevice.EXTRA_DEVICE, mGBDevice); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 8036d874..adcf077d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.net.Uri; import android.support.annotation.Nullable; -import android.util.Pair; import java.util.ArrayList; import java.util.UUID; 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 9839ade2..22278693 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.Nullable; -import android.util.Pair; import java.util.ArrayList; import java.util.UUID; 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 cb2b1f48..c0a436a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -64,7 +64,7 @@ public class ActivityUser { * value is out of any logical bounds. */ public int getActivityUserSleepDuration() { - if(activityUserSleepDuration == null) { + if (activityUserSleepDuration == null) { fetchPreferences(); } if (activityUserSleepDuration < 1 || activityUserSleepDuration > 24) { 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 0463cffc..b5bfe222 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 @@ -578,7 +578,7 @@ public class PebbleIoThread extends GBDeviceIoThread { return; } - String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); + String platformName = PebbleUtils.getPlatformName(gbDevice.getHardwareVersion()); try { mPBWReader = new PBWReader(uri, getContext(), platformName); From 1e44bb03fb166aa632952c2e78f8d98f10e2f09c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:02:30 +0100 Subject: [PATCH 079/274] Pebble: convert Boolean to String for app configuration --- .../gadgetbridge/activities/ExternalPebbleJSActivity.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 6f05a0e6..61ee95e7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -110,7 +110,11 @@ public class ExternalPebbleJSActivity extends Activity { cur_key = key.next(); int pebbleAppIndex = knownKeys.optInt(cur_key); if (pebbleAppIndex != 0) { - out.put(String.valueOf(pebbleAppIndex), in.get(cur_key)); + Object obj = in.get(cur_key); + if (obj instanceof Boolean) { + obj = ((Boolean) obj) ? "true" : "false"; + } + out.put(String.valueOf(pebbleAppIndex), obj); } else { GB.toast("Discarded key " + cur_key + ", not found in the local configuration.", Toast.LENGTH_SHORT, GB.WARN); } From 3786e0b7f26f33aa2b258cdd5659adebb4426c67 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:04:17 +0100 Subject: [PATCH 080/274] fix typo --- .../freeyourgadget/gadgetbridge/model/DeviceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 263e174b..498cb29c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -51,7 +51,7 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_TRACK = "music_track"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; - String EXTRA_APP_CONFIG = "app_configt"; + String EXTRA_APP_CONFIG = "app_config"; String EXTRA_URI = "uri"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; From a96d042dce1168c3ff771e347fd5830f7915c93d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 16:07:59 +0100 Subject: [PATCH 081/274] try to make mister travis happy --- .../gadgetbridge/service/TestDeviceSupport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 77158b15..8722f00d 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -90,6 +90,11 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } + @Override + public void onAppConfiguration(UUID appUuid, String config) { + + } + @Override public void onFetchActivityData() { From aba21d3ab72474d0870ebe01196797d1ca1a31fd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 17:19:29 +0100 Subject: [PATCH 082/274] switch to gradle plugin 1.5.0 in an attempt to fix travis --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index cf58ce20..6d40ae25 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.0.0-beta6' + classpath 'com.android.tools.build:gradle:1.5.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From f616e4f571e218870d9fc57f88ec3ae982645351 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 3 Mar 2016 17:46:58 +0100 Subject: [PATCH 083/274] Pebble: skip .js file if too large instead of breaking installation (Hotfix) --- .../gadgetbridge/devices/pebble/PBWReader.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index ee65b567..e287b328 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -216,8 +216,10 @@ public class PBWReader { } else if (fileName.equals("pebble-js-app.js")) { LOG.info("Found JS file: app supports configuration."); long bytes = ze.getSize(); - if (bytes > 65536) // that should be too much - break; + if (bytes > 65536) { + LOG.info("size exceeding 64k, skipping"); + continue; + } ByteArrayOutputStream baos = new ByteArrayOutputStream(); while ((count = zis.read(buffer)) != -1) { From 3920b3f977895247ea49d3b722b764829c3c85b2 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 4 Mar 2016 17:43:43 +0100 Subject: [PATCH 084/274] Do not override the configured settings with our old stored values (but keep them around) --- .../service/devices/pebble/AppMessageHandlerPebStyle.java | 6 ++++++ .../devices/pebble/AppMessageHandlerTimeStylePebble.java | 3 +++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index c29460da..8b90c5a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -96,18 +96,24 @@ public class AppMessageHandlerPebStyle extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); ByteBuffer buf = ByteBuffer.allocate(encodeAck().length + encodePebStyleConfig().length); buf.put(encodeAck()); buf.put(encodePebStyleConfig()); sendBytes.encodedBytes = buf.array(); return new GBDeviceEvent[]{sendBytes}; + */ } @Override public GBDeviceEvent[] pushMessage() { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodePebStyleConfig(); return new GBDeviceEvent[]{sendBytes}; + */ } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index 11b8cf78..fb1583f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -100,8 +100,11 @@ public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { @Override public GBDeviceEvent[] handleMessage(ArrayList> pairs) { + return null; + /* GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); sendBytes.encodedBytes = encodeTimeStylePebbleConfig(); return new GBDeviceEvent[]{sendBytes}; + */ } } From 6d4b98719a64cab406d0be1fbd44284039b1bf7c Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 4 Mar 2016 17:44:42 +0100 Subject: [PATCH 085/274] Implement some further JS methods to make additional watchapps happy --- .../app_config/js/gadgetbridge_boilerplate.js | 45 ++++++++++++++++--- .../activities/ExternalPebbleJSActivity.java | 10 +++++ .../gadgetbridge/util/PebbleUtils.java | 12 +++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index af68a965..760a2ec8 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -34,6 +34,9 @@ function gbPebble() { this.configurationValues = null; this.addEventListener = function(e, f) { + if(e == 'ready') { + this.ready = f; + } if(e == 'showConfiguration') { this.showConfiguration = f; } @@ -45,6 +48,20 @@ function gbPebble() { } } + this.removeEventListener = function(e, f) { + if(e == 'ready') { + this.ready = null; + } + if(e == 'showConfiguration') { + this.showConfiguration = null; + } + if(e == 'webviewclosed') { + this.parseconfig = null; + } + if(e == 'appmessage') { + this.appmessage = null; + } + } this.actuallyOpenURL = function() { window.open(this.configurationURL.toString(), "config"); } @@ -64,15 +81,31 @@ function gbPebble() { return JSON.parse(GBjs.getActiveWatchInfo()); } - this.sendAppMessage = function (dict, callback){ - this.configurationValues = JSON.stringify(dict); - document.getElementById("jsondata").innerHTML=this.configurationValues; - return callback; + this.sendAppMessage = function (dict, callbackAck, callbackNack){ + try { + this.configurationValues = JSON.stringify(dict); + document.getElementById("jsondata").innerHTML=this.configurationValues; + return callbackAck; + } + catch (e) { + GBjs.gbLog("sendAppMessage failed"); + return callbackNack; + } } - this.ready = function(e) { - GBjs.gbLog("ready called"); + this.getAccountToken = function() { + return ''; } + + this.getWatchToken = function() { + return GBjs.getWatchToken(); + } + + this.showSimpleNotificationOnPebble = function(title, body) { + GBjs.gbLog("app wanted to show: " + title + " body: "+ body); + } + + } var Pebble = new gbPebble(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 61ee95e7..36e8101e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -131,7 +131,11 @@ public class ExternalPebbleJSActivity extends Activity { public String getActiveWatchInfo() { JSONObject wi = new JSONObject(); try { + wi.put("firmware",mGBDevice.getFirmwareVersion()); wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); + wi.put("model", PebbleUtils.getModel(mGBDevice.getHardwareVersion())); + //TODO: use real info + wi.put("language","en"); } catch (JSONException e) { e.printStackTrace(); } @@ -157,6 +161,12 @@ public class ExternalPebbleJSActivity extends Activity { public String getAppUUID() { return appUuid.toString(); } + + @JavascriptInterface + public String getWatchToken() { + //specification says: A string that is is guaranteed to be identical for each Pebble device for the same app across different mobile devices. The token is unique to your app and cannot be used to track Pebble devices across applications. see https://developer.pebble.com/docs/js/Pebble/ + return "gb"+appUuid.toString(); + } } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index b925c3fb..0e13e925 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -12,4 +12,16 @@ public class PebbleUtils { } return platformName; } + public static String getModel(String hwRev) { + //TODO: get real data? + String model; + if (hwRev.startsWith("snowy")) { + model = "pebble_time_black"; + } else if (hwRev.startsWith("spalding")) { + model = "pebble_time_round_black_20mm"; + } else { + model = "pebble_black"; + } + return model; + } } From 3b3458e19634b7aa34df65e333d1c1453a4ec0d4 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Mar 2016 23:07:41 +0100 Subject: [PATCH 086/274] Show the heart rate measurement tooltip a little longer --- .../freeyourgadget/gadgetbridge/activities/DebugActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index b964773a..757fdd6e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -190,7 +190,7 @@ public class DebugActivity extends Activity { HeartRateButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GB.toast("Measuring heart rate, please wait...", Toast.LENGTH_SHORT, GB.INFO); + GB.toast("Measuring heart rate, please wait...", Toast.LENGTH_LONG, GB.INFO); GBApplication.deviceService().onHeartRateTest(); } }); From dc162a9ac818528f4f3c216eb837ab467895f022 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Mar 2016 23:19:44 +0100 Subject: [PATCH 087/274] Only add column if it doesn't exist yet Column can exist if there down- and upgrades --- .../freeyourgadget/gadgetbridge/database/DBHelper.java | 8 ++++++++ .../gadgetbridge/database/schema/ActivityDBUpdate_6.java | 9 ++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index a5fed010..b9b679bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.database; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -70,6 +71,13 @@ public class DBHelper { db.execSQL(statement); } + public static boolean existsColumn(String tableName, String columnName, SQLiteDatabase db) { + try (Cursor res = db.rawQuery("PRAGMA table_info('" + tableName + "')", null)) { + int result = res.getColumnIndex(columnName); + return result != -1; + } + } + /** * WITHOUT ROWID is only available with sqlite 3.8.2, which is available * with Lollipop and later. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java index 483f556d..b6d157c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_6.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.database.schema; import android.database.sqlite.SQLiteDatabase; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; @@ -13,9 +14,11 @@ import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GB public class ActivityDBUpdate_6 implements DBUpdateScript { @Override public void upgradeSchema(SQLiteDatabase db) { - String ADD_COLUMN_CUSTOM_SHORT = "ALTER TABLE " + TABLE_GBACTIVITYSAMPLES + " ADD COLUMN " - + KEY_CUSTOM_SHORT + " INT;"; - db.execSQL(ADD_COLUMN_CUSTOM_SHORT); + if (!DBHelper.existsColumn(TABLE_GBACTIVITYSAMPLES, KEY_CUSTOM_SHORT, db)) { + String ADD_COLUMN_CUSTOM_SHORT = "ALTER TABLE " + TABLE_GBACTIVITYSAMPLES + " ADD COLUMN " + + KEY_CUSTOM_SHORT + " INT;"; + db.execSQL(ADD_COLUMN_CUSTOM_SHORT); + } } @Override From 97faf61c5a64f18b9ade431c431131dc14fe066e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 4 Mar 2016 23:37:42 +0100 Subject: [PATCH 088/274] Log db upgrade/downgrade requests --- .../gadgetbridge/database/ActivityDatabaseHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 97023cdd..13557906 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -52,6 +52,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + LOG.info("ActivityDatabase: schema upgrade requested from " + oldVersion + " to " + newVersion); try { for (int i = oldVersion + 1; i <= newVersion; i++) { DBUpdateScript updater = getUpdateScript(db, i); @@ -69,6 +70,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + LOG.info("ActivityDatabase: schema downgrade requested from " + oldVersion + " to " + newVersion); try { for (int i = oldVersion; i >= newVersion; i--) { DBUpdateScript updater = getUpdateScript(db, i); From 459f6baf0823bfd4e43696cb1df2d93c4b8e8664 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 6 Mar 2016 17:28:21 +0100 Subject: [PATCH 089/274] Fix missing column in the creation script (upgrades were fine, but new installation weren't). Reindent changelog file. --- .../schema/ActivityDBCreationScript.java | 2 ++ app/src/main/res/xml/changelog_master.xml | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java index 9aa67c86..20c4ac5b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBCreationScript.java @@ -4,6 +4,7 @@ import android.database.sqlite.SQLiteDatabase; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_INTENSITY; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_PROVIDER; import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_STEPS; @@ -19,6 +20,7 @@ public class ActivityDBCreationScript { + KEY_INTENSITY + " SMALLINT," + KEY_STEPS + " TINYINT," + KEY_TYPE + " TINYINT," + + KEY_CUSTOM_SHORT + " INT," + " PRIMARY KEY (" + KEY_TIMESTAMP + "," + KEY_PROVIDER + ") ON CONFLICT REPLACE)" + DBHelper.getWithoutRowId(); db.execSQL(CREATE_GBACTIVITYSAMPLES_TABLE); } diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 51aac97f..dc0624dc 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,16 @@ - - Pebble: Install and start freshly-installed apps on the watch also in FW 3.x (now same behaviour as 2.x) - Pebble: Fix crash while receiving Health data - Mi Band 1S: Support for synchronizing activity data - Mi Band 1S: Support for reading the heart rate via the "Debug Screen" + + Fix database creation script. Thanks @feclare + + + Pebble: Install and start freshly-installed apps on the watch also in FW 3.x (now + same behaviour as 2.x) + + Pebble: Fix crash while receiving Health data + Mi Band 1S: Support for synchronizing activity data + Mi Band 1S: Support for reading the heart rate via the "Debug Screen" From 619ea04a6356840068b2fb724b0378a21ce7cac9 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 00:36:39 +0100 Subject: [PATCH 090/274] Fix database creation and updates #246 The creation script *must* always do the full creation so that fresh installs get the correct database (no upgrade scripts will run for them) --- .../database/ActivityDatabaseHandler.java | 2 +- .../gadgetbridge/database/DBHelper.java | 13 +++++++++++-- .../database/schema/ActivityDBUpdate_7.java | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 13557906..f186f88e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -34,7 +34,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl private static final Logger LOG = LoggerFactory.getLogger(ActivityDatabaseHandler.class); - private static final int DATABASE_VERSION = 6; + private static final int DATABASE_VERSION = 7; public ActivityDatabaseHandler(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index b9b679bf..7c53d326 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -73,9 +73,18 @@ public class DBHelper { public static boolean existsColumn(String tableName, String columnName, SQLiteDatabase db) { try (Cursor res = db.rawQuery("PRAGMA table_info('" + tableName + "')", null)) { - int result = res.getColumnIndex(columnName); - return result != -1; + int index = res.getColumnIndex("name"); + if (index < 1) { + return false; // something's really wrong + } + while (res.moveToNext()) { + String cn = res.getString(index); + if (columnName.equals(cn)) { + return true; + } + } } + return false; } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java new file mode 100644 index 00000000..84638f3c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java @@ -0,0 +1,16 @@ +package nodomain.freeyourgadget.gadgetbridge.database.schema; + +import android.database.sqlite.SQLiteDatabase; + +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; + +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; +import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; + +/** + * Bugfix for users who installed 0.8.1 cleanly, i.e. without any previous + * database. Perform Update script 6 again. + */ +public class ActivityDBUpdate_7 extends ActivityDBUpdate_6 { +} From 2902e60d517f593fadcb84dace54e64a30ffa9f5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 7 Mar 2016 00:55:17 +0100 Subject: [PATCH 091/274] prepare 0.8.2 --- CHANGELOG.md | 5 +++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 14 ++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db8880c..da05d4f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ###Changelog +####Version 0.8.2 +* Fix database creation and updates (thanks @feclare) +* Add experimental widget to set the alarm time to a configurable number of hours in the future (thanks @0nse) +* Use ckChangeLog to display the Changelog within Gadgetbridge + ####Version 0.8.1 * Pebble: install (and start) freshly-installed apps on the watch instead of showing a Toast that tells the user to do so. (only applies to firmware 3.x) * Pebble: fix crash while receiving Health data diff --git a/app/build.gradle b/app/build.gradle index 508e1b82..fdeccda6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.8.1" - versionCode 42 + versionName "0.8.2" + versionCode 43 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index dc0624dc..02163451 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,14 +1,12 @@ - - Fix database creation script. Thanks @feclare + + Fix database creation and updates + Add experimental widget to set the alarm time to a configurable number of hours in the future + Use ckChangeLog to display the Changelog within Gadgetbridge - - Pebble: Install and start freshly-installed apps on the watch also in FW 3.x (now - same behaviour as 2.x) - + + Pebble: Install and start freshly-installed apps on the watch also in FW 3.x (now same behaviour as 2.x) Pebble: Fix crash while receiving Health data Mi Band 1S: Support for synchronizing activity data Mi Band 1S: Support for reading the heart rate via the "Debug Screen" From dda6cb34e1cda69892986cd3fcb24ab26577f6ec Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 01:09:20 +0100 Subject: [PATCH 092/274] Fix logfile rotation (bug in logback-android) --- app/src/main/assets/logback.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml index f08c0b4c..8d3f96fe 100644 --- a/app/src/main/assets/logback.xml +++ b/app/src/main/assets/logback.xml @@ -15,12 +15,14 @@ - ${GB_LOGFILES_DIR}/gadgetbridge-%d{yyyy-MM-dd}.log.zip + ${GB_LOGFILES_DIR}/gadgetbridge-%d{yyyy-MM-dd}.%i.log.zip 10 + + + 2MB + - - 5MB - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{1} - %msg%n From 2d9673afe760e07d8c081fc9f9cb3b82b366a424 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 01:10:58 +0100 Subject: [PATCH 093/274] Updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da05d4f0..286ce91f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix database creation and updates (thanks @feclare) * Add experimental widget to set the alarm time to a configurable number of hours in the future (thanks @0nse) * Use ckChangeLog to display the Changelog within Gadgetbridge +* Workaround to fix logfile rotation (bug in logback-android) ####Version 0.8.1 * Pebble: install (and start) freshly-installed apps on the watch instead of showing a Toast that tells the user to do so. (only applies to firmware 3.x) From f1ba50b62a26bb9e3836a0df64316df5e2c4d163 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 7 Mar 2016 01:15:23 +0100 Subject: [PATCH 094/274] update xml changelog --- app/src/main/res/xml/changelog_master.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 02163451..07f4ca5e 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,10 @@ - Fix database creation and updates Add experimental widget to set the alarm time to a configurable number of hours in the future Use ckChangeLog to display the Changelog within Gadgetbridge + Fix database creation and updates + Workaround to fix logfile rotation Pebble: Install and start freshly-installed apps on the watch also in FW 3.x (now same behaviour as 2.x) From 50dd7f5eba0b5b92d98e3a371e929881e68ebf27 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 21:36:31 +0100 Subject: [PATCH 095/274] Better check for heartrate support on non-heartrate devices --- .../service/devices/miband/MiBandSupport.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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 63c3c022..1b84f3ff 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 @@ -137,12 +137,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_ACTIVITY_DATA), enable) .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_BATTERY), enable) .notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_SENSOR_DATA), enable); - // unconditionally try to get notifications for HR -- will silently fail when characteristic - // is not available. And at this point, we don't even know whether we support it, because - // no device info is available yet. We would have to do the check in a custom NotifyAction. -// if (supportsHeartRate()) { - builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT), enable); -// } + // cannot use supportsHeartrate() here because we don't have that information yet + BluetoothGattCharacteristic heartrateCharacteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT); + if (heartrateCharacteristic != null) { + builder.notify(heartrateCharacteristic, enable); + } return this; } From be012eca8df9df071cbd9c26eeb42069e4af7d09 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 21:43:45 +0100 Subject: [PATCH 096/274] For Mi Band 1A (fw 5.15.7.14) we get 0xa for auth success #180 --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 1 + 1 file changed, 1 insertion(+) 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 1b84f3ff..20b349ec 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 @@ -778,6 +778,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { GB.toast(getContext(), "Band needs pairing", Toast.LENGTH_LONG, GB.ERROR); break; case MiBandService.NOTIFY_AUTHENTICATION_SUCCESS: // fall through -- not sure which one we get + case MiBandService.NOTIFY_RESET_AUTHENTICATION_SUCCESS: // for Mi 1A case MiBandService.NOTIFY_STATUS_MOTOR_AUTH_SUCCESS: LOG.info("Band successfully authenticated"); // maybe we can perform the rest of the initialization from here From 25e58eb4142fde712e3931fb5473b2a4a867e339 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 22:45:44 +0100 Subject: [PATCH 097/274] Upgrade mpandroidchart to 2.2.3 --- app/build.gradle | 2 +- .../gadgetbridge/activities/charts/AbstractChartFragment.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index fdeccda6..3746bc49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ dependencies { compile 'com.android.support:support-v4:23.1.1' compile 'com.github.tony19:logback-android-classic:1.1.1-4' compile 'org.slf4j:slf4j-api:1.7.7' - compile 'com.github.PhilJay:MPAndroidChart:v2.1.6' + compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index c4775c50..22c79eca 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -20,6 +20,7 @@ import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.interfaces.datasets.IBarDataSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -482,7 +483,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { BarDataSet activitySet = createActivitySet(activityEntries, colors, "Activity"); // create a data object with the datasets CombinedData combinedData = new CombinedData(xLabels); - List list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(activitySet); BarData barData = new BarData(xLabels, list); barData.setGroupSpace(0); From 5ae680cab54436469aef93b74eef25603e1570f1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 22:46:35 +0100 Subject: [PATCH 098/274] Don't flush the logfile synchronously --- app/src/main/assets/logback.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/assets/logback.xml b/app/src/main/assets/logback.xml index 8d3f96fe..07fa3da6 100644 --- a/app/src/main/assets/logback.xml +++ b/app/src/main/assets/logback.xml @@ -26,6 +26,8 @@ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{1} - %msg%n + + false From 5eb8f57b4ca0d0c21540e67ba3ace80d823abe37 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 22:47:34 +0100 Subject: [PATCH 099/274] Some more byte -> int conversions --- .../database/ActivityDatabaseHandler.java | 8 ++++---- .../devices/miband/MiBandSampleProvider.java | 12 ++++++------ .../miband/operations/FetchActivityOperation.java | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index f186f88e..bc1facb3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -226,10 +226,10 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl GBActivitySample sample = new GBActivitySample( provider, cursor.getInt(cursor.getColumnIndex(KEY_TIMESTAMP)), - cursor.getShort(cursor.getColumnIndex(KEY_INTENSITY)), - cursor.getShort(cursor.getColumnIndex(KEY_STEPS)), - (byte) cursor.getShort(cursor.getColumnIndex(KEY_TYPE)), - cursor.getShort(cursor.getColumnIndex(KEY_CUSTOM_SHORT))); + cursor.getInt(cursor.getColumnIndex(KEY_INTENSITY)), + cursor.getInt(cursor.getColumnIndex(KEY_STEPS)), + cursor.getInt(cursor.getColumnIndex(KEY_TYPE)), + cursor.getInt(cursor.getColumnIndex(KEY_CUSTOM_SHORT))); samples.add(sample); } while (cursor.moveToNext()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index 6cde07e1..b9b622d3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -4,12 +4,12 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class MiBandSampleProvider implements SampleProvider { - public static final byte TYPE_DEEP_SLEEP = 5; - public static final byte TYPE_LIGHT_SLEEP = 4; - public static final byte TYPE_ACTIVITY = -1; - public static final byte TYPE_UNKNOWN = -1; - public static final byte TYPE_NONWEAR = 3; - public static final byte TYPE_CHARGING = 6; + public static final int TYPE_DEEP_SLEEP = 5; + public static final int TYPE_LIGHT_SLEEP = 4; + public static final int TYPE_ACTIVITY = -1; + public static final int TYPE_UNKNOWN = -1; + public static final int TYPE_NONWEAR = 3; + public static final int TYPE_CHARGING = 6; // public static final byte TYPE_NREM = 5; // DEEP SLEEP // public static final byte TYPE_ONBED = 7; 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 2b056d9f..6b9b2ca1 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 @@ -333,10 +333,10 @@ public class FetchActivityOperation extends AbstractMiBandOperation { samples[minutes] = new GBActivitySample( sampleProvider, timestampInSeconds, - (short) (intensity & 0xff), - (short) (steps & 0xff), - category, - (short) (heartrate & 0xff)); + intensity & 0xff, + steps & 0xff, + category & 0xff, + heartrate & 0xff); // next minute minutes++; From a96120f91da21e0c60018f986674c200805ce092 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 7 Mar 2016 23:17:02 +0100 Subject: [PATCH 100/274] Clear the chart when there are no samples (e.g. when switching to another day for which no samples are available, the chart now becomes empty instead of displaying the samples from the last day with data. --- .../activities/charts/AbstractChartFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 22c79eca..61f6ad01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -16,6 +16,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; @@ -29,6 +30,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashSet; @@ -389,10 +391,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { LOG.info("" + getTitle() + ": number of samples:" + samples.size()); if (samples.size() > 1) { - float movement_divisor; boolean annotate = true; boolean use_steps_as_movement; - SampleProvider provider = getProvider(gbDevice); int last_type = ActivityKind.TYPE_UNKNOWN; @@ -502,6 +502,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { setupLegend(chart); chart.setData(combinedData); + } else { + CombinedData data = new CombinedData(Collections.emptyList()); + chart.setData(data); } } From a3ee3c15fc7878057d0e93a7e1fbde19242dc3d8 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 8 Mar 2016 11:41:20 +0100 Subject: [PATCH 101/274] Pebble: copy pebble-app-js.js out of the pbw upon installation not upon reading the .pbw This eliminates the need to copy the whole file into a byte[], and all file size limts are gone. --- .../devices/pebble/PBWInstallHandler.java | 12 +++--------- .../devices/pebble/PBWReader.java | 19 ------------------- .../gadgetbridge/util/FileUtils.java | 18 +++++++++++------- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index 9794ca8f..30becd11 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -13,6 +13,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.io.Writer; import nodomain.freeyourgadget.gadgetbridge.R; @@ -167,21 +168,14 @@ public class PBWInstallHandler implements InstallHandler { LOG.error(e.getMessage(), e); } - String jsConfigFile = mPBWReader.getJsConfigurationFile(); + InputStream jsConfigFile = mPBWReader.getInputStreamFile("pebble-js-app.js"); if (jsConfigFile != null) { outputFile = new File(destDir, app.getUUID().toString() + "_config.js"); try { - writer = new BufferedWriter(new FileWriter(outputFile)); + FileUtils.copyStreamToFile(jsConfigFile, outputFile); } catch (IOException e) { LOG.error("Failed to open output file: " + e.getMessage(), e); - return; - } - try { - writer.write(jsConfigFile); - writer.close(); - } catch (IOException e) { - LOG.error("Failed to write to output file: " + e.getMessage(), e); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index e287b328..42f05b74 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -57,7 +57,6 @@ public class PBWReader { private short mAppVersion; private int mIconId; private int mFlags; - private String jsConfigurationFile = null; private JSONObject mAppKeys = null; @@ -213,20 +212,6 @@ public class PBWReader { e.printStackTrace(); break; } - } else if (fileName.equals("pebble-js-app.js")) { - LOG.info("Found JS file: app supports configuration."); - long bytes = ze.getSize(); - if (bytes > 65536) { - LOG.info("size exceeding 64k, skipping"); - continue; - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while ((count = zis.read(buffer)) != -1) { - baos.write(buffer, 0, count); - } - - jsConfigurationFile = baos.toString(); } else if (fileName.equals(platformDir + "pebble-app.bin")) { zis.read(buffer, 0, 108); byte[] tmp_buf = new byte[32]; @@ -342,8 +327,4 @@ public class PBWReader { public JSONObject getAppKeysJSON() { return mAppKeys; } - - public String getJsConfigurationFile() { - return jsConfigurationFile; - } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index de9bc9dc..b354edb7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -47,6 +47,16 @@ public class FileUtils { } } + public static void copyStreamToFile(InputStream inputStream, File destFile) throws IOException { + FileOutputStream fout = new FileOutputStream(destFile); + byte[] buf = new byte[4096]; + while (inputStream.available() > 0) { + int bytes = inputStream.read(buf); + fout.write(buf, 0, bytes); + } + fout.close(); + } + public static void copyURItoFile(Context ctx, Uri uri, File destFile) throws IOException { if (uri.getPath().equals(destFile.getPath())) { return; @@ -60,14 +70,8 @@ public class FileUtils { e.printStackTrace(); return; } - FileOutputStream fout = new FileOutputStream(destFile); - byte[] buf = new byte[4096]; - while (fin.available() > 0) { - int bytes = fin.read(buf); - fout.write(buf, 0, bytes); - } + copyStreamToFile(fin, destFile); fin.close(); - fout.close(); } public static String getStringFromFile(File file) throws IOException { From 4362f7802873d728a81d7a576f7c6143b7703b9d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 8 Mar 2016 11:49:08 +0100 Subject: [PATCH 102/274] Pebble: Do not display Health on original Pebbles --- .../gadgetbridge/activities/AppManagerActivity.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 080dba67..5955ce24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; public class AppManagerActivity extends Activity { @@ -79,7 +80,9 @@ public class AppManagerActivity extends Activity { List systemApps = new ArrayList<>(); systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getFirmwareVersion()))) { + systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); + } return systemApps; } From a89fea9c7da508f10cd523e516cb3fcfe7769382 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 8 Mar 2016 12:02:00 +0100 Subject: [PATCH 103/274] Pebble: Fix crash when starting pebstyle Also make code for "push" handlers more generic --- .../service/devices/pebble/PebbleProtocol.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 5b6ee85d..d426740b 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 @@ -1722,7 +1722,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return null; } - private GBDeviceEvent decodeAppRunState(ByteBuffer buf) { + private GBDeviceEvent[] decodeAppRunState(ByteBuffer buf) { byte command = buf.get(); long uuid_high = buf.getLong(); long uuid_low = buf.getLong(); @@ -1731,9 +1731,10 @@ public class PebbleProtocol extends GBDeviceProtocol { switch (command) { case APPRUNSTATE_START: LOG.info(ENDPOINT_NAME + ": started " + uuid); - if (UUID_PEBSTYLE.equals(uuid)) { - AppMessageHandler handler = mAppMessageHandlers.get(uuid); - return handler.pushMessage()[0]; + + AppMessageHandler handler = mAppMessageHandlers.get(uuid); + if (handler != null) { + return handler.pushMessage(); } break; case APPRUNSTATE_STOP: @@ -2091,7 +2092,7 @@ public class PebbleProtocol extends GBDeviceProtocol { devEvts = new GBDeviceEvent[]{decodeSystemMessage(buf)}; break; case ENDPOINT_APPRUNSTATE: - devEvts = new GBDeviceEvent[]{decodeAppRunState(buf)}; + devEvts = decodeAppRunState(buf); break; case ENDPOINT_BLOBDB: devEvts = new GBDeviceEvent[]{decodeBlobDb(buf)}; From 2da50e27c25f2a4ec6e94c228c9b9f7686e6686f Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Tue, 8 Mar 2016 17:45:11 +0100 Subject: [PATCH 104/274] call the ready event as soon as the app js file has been loaded. Add a button to debug the different steps. --- app/src/main/assets/app_config/configure.html | 5 +++-- .../app_config/js/gadgetbridge_boilerplate.js | 14 ++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/assets/app_config/configure.html b/app/src/main/assets/app_config/configure.html index ac25d82c..a4d10e98 100644 --- a/app/src/main/assets/app_config/configure.html +++ b/app/src/main/assets/app_config/configure.html @@ -11,11 +11,12 @@ - +

Url of the configuration:

- + +

Incoming configuration data:

diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 760a2ec8..3e025ccb 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -72,9 +72,15 @@ function gbPebble() { //needs to be called like this because of original Pebble function name this.openURL = function(url) { - document.getElementById("config_url").innerHTML=url; - var UUID = GBjs.getAppUUID(); - this.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); + if (url.lastIndexOf("http", 0) === 0) { + document.getElementById("config_url").innerHTML=url; + var UUID = GBjs.getAppUUID(); + this.configurationURL = new Uri(url).addQueryParam("return_to", "gadgetbridge://"+UUID+"?config=true&json="); + } else { + //TODO: add custom return_to + location.href = url; + } + } this.getActiveWatchInfo = function() { @@ -122,7 +128,7 @@ if (jsConfigFile != null) { Pebble.parseconfig(t); } else { document.getElementById('step2').style.display="none"; - Pebble.showConfiguration(); + Pebble.ready(); } }); } From 87023ebdb34822e0b8d094dab4a35a92099a6767 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Mar 2016 21:26:55 +0100 Subject: [PATCH 105/274] Don't retrieve the column index again and again in a long loop Also: fix weird iteration logic --- .../database/ActivityDatabaseHandler.java | 25 +++++++++++-------- .../externalevents/K9Receiver.java | 5 ++-- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index bc1facb3..3b40db5b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -221,17 +221,20 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl try (SQLiteDatabase db = this.getReadableDatabase()) { try (Cursor cursor = db.query(TABLE_GBACTIVITYSAMPLES, null, where, null, null, null, order)) { LOG.info("Activity query result: " + cursor.getCount() + " samples"); - if (cursor.moveToFirst()) { - do { - GBActivitySample sample = new GBActivitySample( - provider, - cursor.getInt(cursor.getColumnIndex(KEY_TIMESTAMP)), - cursor.getInt(cursor.getColumnIndex(KEY_INTENSITY)), - cursor.getInt(cursor.getColumnIndex(KEY_STEPS)), - cursor.getInt(cursor.getColumnIndex(KEY_TYPE)), - cursor.getInt(cursor.getColumnIndex(KEY_CUSTOM_SHORT))); - samples.add(sample); - } while (cursor.moveToNext()); + int colTimeStamp = cursor.getColumnIndex(KEY_TIMESTAMP); + int colIntensity = cursor.getColumnIndex(KEY_INTENSITY); + int colSteps = cursor.getColumnIndex(KEY_STEPS); + int colType = cursor.getColumnIndex(KEY_TYPE); + int colCustomShort = cursor.getColumnIndex(KEY_CUSTOM_SHORT); + while (cursor.moveToFirst()) { + GBActivitySample sample = new GBActivitySample( + provider, + cursor.getInt(colTimeStamp), + cursor.getInt(colIntensity), + cursor.getInt(colSteps), + cursor.getInt(colType), + cursor.getInt(colCustomShort)); + samples.add(sample); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index 27b7132c..f9460d60 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -67,8 +67,7 @@ public class K9Receiver extends BroadcastReceiver { try { if (c != null) { - c.moveToFirst(); - do { + while (c.moveToFirst()) { String uri = c.getString(c.getColumnIndex("uri")); if (uri.equals(uriWanted)) { notificationSpec.sender = c.getString(c.getColumnIndex("senderAddress")); @@ -76,7 +75,7 @@ public class K9Receiver extends BroadcastReceiver { notificationSpec.body = c.getString(c.getColumnIndex("preview")); break; } - } while (c.moveToNext()); + } } } finally { if (c != null) { From 7e8281e8d49225d14aaf138ce55888caa6595e47 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Mar 2016 21:32:35 +0100 Subject: [PATCH 106/274] Improve exception handling logic a bit --- .../gadgetbridge/service/DeviceCommunicationService.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 f3b8c58b..0eddd504 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -479,15 +479,12 @@ public class DeviceCommunicationService extends Service { Cursor contactLookup = null; try { contactLookup = contentResolver.query(uri, null, null, null, null); - } catch (SecurityException e) { - return name; - } - - try { if (contactLookup != null && contactLookup.getCount() > 0) { contactLookup.moveToNext(); name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); } + } catch (SecurityException e) { + // ignore, just return name below } finally { if (contactLookup != null) { contactLookup.close(); From d378b4eb7bda400a790feface95beb35e3b4ac94 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Tue, 8 Mar 2016 21:44:12 +0100 Subject: [PATCH 107/274] Intercept clay pebblejs://close url --- .../activities/ExternalPebbleJSActivity.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 36e8101e..122a2969 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -9,6 +9,7 @@ import android.view.MenuItem; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; +import android.webkit.WebViewClient; import android.widget.Toast; import org.json.JSONException; @@ -61,6 +62,7 @@ public class ExternalPebbleJSActivity extends Activity { WebView myWebView = (WebView) findViewById(R.id.configureWebview); myWebView.clearCache(true); + myWebView.setWebViewClient(new GBWebClient()); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true); //needed to access the DOM @@ -87,6 +89,16 @@ public class ExternalPebbleJSActivity extends Activity { return null; } + private class GBWebClient extends WebViewClient { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + url = url.replaceFirst("^pebblejs://close#", "file:///android_asset/app_config/configure.html?config=true&json="); + view.loadUrl(url); + return true; + + } + } + private class JSInterface { public JSInterface() { From 9643fa60628b3c918b40acfe09201badb619bfd0 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Mar 2016 23:29:42 +0100 Subject: [PATCH 108/274] s/moveToFirst/moveToNext --- .../freeyourgadget/gadgetbridge/externalevents/K9Receiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index f9460d60..5683cf83 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -67,7 +67,7 @@ public class K9Receiver extends BroadcastReceiver { try { if (c != null) { - while (c.moveToFirst()) { + while (c.moveToNext()) { String uri = c.getString(c.getColumnIndex("uri")); if (uri.equals(uriWanted)) { notificationSpec.sender = c.getString(c.getColumnIndex("senderAddress")); From 10975feb49ae04df65a3a13ad2982352a16f4c64 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Mar 2016 23:30:31 +0100 Subject: [PATCH 109/274] s/moveToFirst/moveToNext/ --- .../gadgetbridge/database/ActivityDatabaseHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index 3b40db5b..f8812b30 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -226,7 +226,7 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl int colSteps = cursor.getColumnIndex(KEY_STEPS); int colType = cursor.getColumnIndex(KEY_TYPE); int colCustomShort = cursor.getColumnIndex(KEY_CUSTOM_SHORT); - while (cursor.moveToFirst()) { + while (cursor.moveToNext()) { GBActivitySample sample = new GBActivitySample( provider, cursor.getInt(colTimeStamp), From 3f39928df520b969146aa860cf8d8d6b9d7d408a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 8 Mar 2016 23:48:31 +0100 Subject: [PATCH 110/274] Some more cursor-related improvements (closing) --- .../externalevents/K9Receiver.java | 21 ++++++------------- .../gadgetbridge/model/CalendarEvents.java | 14 ++++++------- .../service/DeviceCommunicationService.java | 10 +-------- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index 5683cf83..1b615d24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -55,17 +55,7 @@ public class K9Receiver extends BroadcastReceiver { * It should be the first one returned by the query in most cases, */ - Cursor c = null; - try { - c = context.getContentResolver().query(k9Uri, messagesProjection, null, null, null); - } catch (Exception e) { - e.printStackTrace(); - notificationSpec.sender = "Gadgetbridge"; - notificationSpec.subject = "Permission Error?"; - notificationSpec.body = "Please reinstall Gadgetbridge to enable K-9 Mail notifications"; - } - - try { + try (Cursor c = context.getContentResolver().query(k9Uri, messagesProjection, null, null, null)) { if (c != null) { while (c.moveToNext()) { String uri = c.getString(c.getColumnIndex("uri")); @@ -77,10 +67,11 @@ public class K9Receiver extends BroadcastReceiver { } } } - } finally { - if (c != null) { - c.close(); - } + } catch (Exception e) { + e.printStackTrace(); + notificationSpec.sender = "Gadgetbridge"; + notificationSpec.subject = "Permission Error?"; + notificationSpec.body = "Please reinstall Gadgetbridge to enable K-9 Mail notifications"; } GBApplication.deviceService().onNotification(notificationSpec); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java index 1e814062..296ebda0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java @@ -54,11 +54,11 @@ public class CalendarEvents { ContentUris.appendId(eventsUriBuilder, dtEnd); Uri eventsUri = eventsUriBuilder.build(); - Cursor evtCursor = null; - evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, CalendarContract.Instances.BEGIN + " ASC"); - - if (evtCursor.moveToFirst()) { - do { + try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, CalendarContract.Instances.BEGIN + " ASC")) { + if (evtCursor == null || evtCursor.getCount() == 0) { + return false; + } + while (evtCursor.moveToNext()) { CalendarEvent calEvent = new CalendarEvent( evtCursor.getLong(1), evtCursor.getLong(2), @@ -69,11 +69,9 @@ public class CalendarEvents { evtCursor.getString(7) ); calendarEventList.add(calEvent); - } while (evtCursor.moveToNext()); - + } return true; } - return false; } public class CalendarEvent { 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 0eddd504..0d495422 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -474,21 +474,13 @@ public class DeviceCommunicationService extends Service { return name; } - ContentResolver contentResolver = getContentResolver(); - - Cursor contactLookup = null; - try { - contactLookup = contentResolver.query(uri, null, null, null, null); + try (Cursor contactLookup = getContentResolver().query(uri, null, null, null, null)) { if (contactLookup != null && contactLookup.getCount() > 0) { contactLookup.moveToNext(); name = contactLookup.getString(contactLookup.getColumnIndex(ContactsContract.Data.DISPLAY_NAME)); } } catch (SecurityException e) { // ignore, just return name below - } finally { - if (contactLookup != null) { - contactLookup.close(); - } } return name; From ea855a4cc28acd4707d85f6ee5751d00c842ac1b Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 13 Mar 2016 08:31:50 +0100 Subject: [PATCH 111/274] Also open public URLs with an external browser. --- .../activities/ExternalPebbleJSActivity.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 122a2969..9e783eb5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.app.Activity; +import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; @@ -92,8 +93,15 @@ public class ExternalPebbleJSActivity extends Activity { private class GBWebClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - url = url.replaceFirst("^pebblejs://close#", "file:///android_asset/app_config/configure.html?config=true&json="); - view.loadUrl(url); + if (url.startsWith("http://") || url.startsWith("https://")) { + Intent i = new Intent(Intent.ACTION_VIEW, + Uri.parse(url)); + startActivity(i); + } else { + url = url.replaceFirst("^pebblejs://close#", "file:///android_asset/app_config/configure.html?config=true&json="); + view.loadUrl(url); + } + return true; } @@ -143,11 +151,11 @@ public class ExternalPebbleJSActivity extends Activity { public String getActiveWatchInfo() { JSONObject wi = new JSONObject(); try { - wi.put("firmware",mGBDevice.getFirmwareVersion()); + wi.put("firmware", mGBDevice.getFirmwareVersion()); wi.put("platform", PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion())); wi.put("model", PebbleUtils.getModel(mGBDevice.getHardwareVersion())); //TODO: use real info - wi.put("language","en"); + wi.put("language", "en"); } catch (JSONException e) { e.printStackTrace(); } @@ -173,11 +181,11 @@ public class ExternalPebbleJSActivity extends Activity { public String getAppUUID() { return appUuid.toString(); } - + @JavascriptInterface public String getWatchToken() { //specification says: A string that is is guaranteed to be identical for each Pebble device for the same app across different mobile devices. The token is unique to your app and cannot be used to track Pebble devices across applications. see https://developer.pebble.com/docs/js/Pebble/ - return "gb"+appUuid.toString(); + return "gb" + appUuid.toString(); } } From 91f02ae920d3ee2166f97d31bbe82fe4ed7c201a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 11 Mar 2016 01:31:16 +0100 Subject: [PATCH 112/274] WIP: Lots of work towards double firmware update for Mi 1S #234 --- .../devices/miband/MiBandFWHelper.java | 35 +- .../miband/MiBandFWInstallHandler.java | 8 +- .../service/devices/miband/DeviceInfo.java | 39 +- .../service/devices/miband/Mi1SInfo.java | 64 ++++ .../operations/FetchActivityOperation.java | 1 - .../operations/UpdateFirmwareOperation.java | 356 +++++++++++++++--- app/src/main/res/values/strings.xml | 1 + 7 files changed, 446 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index adcbee54..2d796e10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -12,9 +12,15 @@ import java.io.IOException; import java.io.InputStream; import java.util.Locale; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +/** + * Also see Mi1SInfo. + */ public class MiBandFWHelper { private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class); private static final int MI_FW_BASE_OFFSET = 1056; @@ -117,10 +123,29 @@ public class MiBandFWHelper { return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()]; } + public static String formatFirmwareVersion(int version) { + if (version == -1) + return GBApplication.getContext().getString(R.string._unknown_); + + return String.format("%d.%d.%d.%d", + version >> 24 & 255, + version >> 16 & 255, + version >> 8 & 255, + version & 255); + } + public String getHumanFirmwareVersion() { return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]); } + public String getHumanFirmwareVersion2() { + return format(Mi1SInfo.getFirmware2VersionFrom(getFw())); + } + + public String format(int version) { + return formatFirmwareVersion(version); + } + public byte[] getFw() { return fw; } @@ -142,9 +167,13 @@ public class MiBandFWHelper { if (MiBandConst.MI_1A.equals(deviceHW)) { return getFirmwareVersionMajor() == 5; } -// if (MiBandConst.MI_1S.equals(deviceHW)) { -// return getFirmwareVersionMajor() == 4; -// } + if (true || MiBandConst.MI_1S.equals(deviceHW)) { // FIXME: REMOVE TEMPORARY HACK + return getFirmwareVersionMajor() == 4; + } return false; } + + public boolean isSingleFirmware() { + return Mi1SInfo.isSingleMiBandFirmware(getFw()); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java index 644b94d3..17b891c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java @@ -56,7 +56,13 @@ public class MiBandFWInstallHandler implements InstallHandler { installActivity.setInstallEnabled(false); return; } - StringBuilder builder = new StringBuilder(mContext.getString(R.string.fw_upgrade_notice, helper.getHumanFirmwareVersion())); + StringBuilder builder = new StringBuilder(); + if (helper.isSingleFirmware()) { + builder.append(mContext.getString(R.string.fw_upgrade_notice, helper.getHumanFirmwareVersion())); + } else { + builder.append(mContext.getString(R.string.fw_multi_upgrade_notice, helper.getHumanFirmwareVersion(), helper.getHumanFirmwareVersion2())); + } + if (helper.isFirmwareWhitelisted()) { builder.append(" ").append(mContext.getString(R.string.miband_firmware_known)); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index d7a64de0..5a704497 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -1,17 +1,23 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; public class DeviceInfo extends AbstractInfo { public final String deviceId; public final int profileVersion; + /** + * Mi Band firmware version identifier + */ public final int fwVersion; public final int hwVersion; public final int feature; public final int appearance; + /** + * Heart rate firmware version identifier + */ + public final int fw2Version; private boolean isChecksumCorrect(byte[] data) { @@ -29,6 +35,15 @@ public class DeviceInfo extends AbstractInfo { hwVersion = data[6] & 255; appearance = data[5] & 255; feature = data[4] & 255; + if (data.length == 20) { + int s = 0; + for (int i = 0; i < 4; ++i) { + s |= (data[16 + i] & 255) << i * 8; + } + fw2Version = s; + } else { + fw2Version = -1; + } } else { deviceId = "crc error"; profileVersion = -1; @@ -36,6 +51,7 @@ public class DeviceInfo extends AbstractInfo { hwVersion = -1; feature = -1; appearance = -1; + fw2Version = -1; } } @@ -52,20 +68,21 @@ public class DeviceInfo extends AbstractInfo { } public String getHumanFirmwareVersion() { - if (fwVersion == -1) - return GBApplication.getContext().getString(R.string._unknown_); + return MiBandFWHelper.formatFirmwareVersion(fwVersion); + } - return String.format("%d.%d.%d.%d", - fwVersion >> 24 & 255, - fwVersion >> 16 & 255, - fwVersion >> 8 & 255, - fwVersion & 255); + public String getHumanFirmware2Version() { + return MiBandFWHelper.formatFirmwareVersion(fw2Version); } public int getFirmwareVersion() { return fwVersion; } + public int getHeartrateFirmwareVersion() { + return fw2Version; + } + @Override public String toString() { return "DeviceInfo{" + @@ -75,6 +92,7 @@ public class DeviceInfo extends AbstractInfo { ", hwVersion=" + hwVersion + ", feature=" + feature + ", appearance=" + appearance + + ", fw2Version (hr)=" + fw2Version+ '}'; } @@ -99,7 +117,8 @@ public class DeviceInfo extends AbstractInfo { return MiBandConst.MI_1A; } if (isMilli1S()) { - return MiBandConst.MI_1S; + return getHumanFirmware2Version(); +// return MiBandConst.MI_1S; } return "?"; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java new file mode 100644 index 00000000..6d98e522 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java @@ -0,0 +1,64 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +/** + * FW1 is Mi Band firmware + * FW2 is heartrate firmware + */ +public class Mi1SInfo { + + public static int getFirmware2OffsetIn(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[26] & 255) << 24 + | (wholeFirmwareBytes[27] & 255) << 16 + | (wholeFirmwareBytes[28] & 255) << 8 + | (wholeFirmwareBytes[29] & 255); + } + + public static int getFirmware2LengthIn(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[30] & 255) << 24 + | (wholeFirmwareBytes[31] & 255) << 16 + | (wholeFirmwareBytes[32] & 255) << 8 + | (wholeFirmwareBytes[33] & 255); + } + + public static int getFirmware1OffsetIn(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[12] & 255) << 24 + | (wholeFirmwareBytes[13] & 255) << 16 + | (wholeFirmwareBytes[14] & 255) << 8 + | (wholeFirmwareBytes[15] & 255); + } + + public static int getFirmware1LengthIn(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[16] & 255) << 24 + | (wholeFirmwareBytes[17] & 255) << 16 + | (wholeFirmwareBytes[18] & 255) << 8 + | (wholeFirmwareBytes[19] & 255); + } + + public static int getFirmware1VersionFrom(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[8] & 255) << 24 + | (wholeFirmwareBytes[9] & 255) << 16 + | (wholeFirmwareBytes[10] & 255) << 8 + | wholeFirmwareBytes[11] & 255; + } + + public static int getFirmware2VersionFrom(byte[] wholeFirmwareBytes) + { + return (wholeFirmwareBytes[22] & 255) << 24 + | (wholeFirmwareBytes[23] & 255) << 16 + | (wholeFirmwareBytes[24] & 255) << 8 + | wholeFirmwareBytes[25] & 255; + } + + public static boolean isSingleMiBandFirmware(byte[] wholeFirmwareBytes) { + if ((wholeFirmwareBytes[7] & 255) != 1) { + return false; + } + return false;// FIXME: hack -- should be true! + } + +} 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 6b9b2ca1..e5e14441 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 @@ -319,7 +319,6 @@ public class FetchActivityOperation extends AbstractMiBandOperation { int numSamples = activityStruct.activityDataHolderProgress / bpm; ActivitySample[] samples = new ActivitySample[numSamples]; SampleProvider sampleProvider = new MiBandSampleProvider(); - int s = 0; for (int i = 0; i < activityStruct.activityDataHolderProgress; i += bpm) { category = activityStruct.activityDataHolder[i]; 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 ccae442f..a351287c 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 @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -27,8 +28,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { private final Uri uri; private boolean firmwareInfoSent = false; - private byte[] newFirmware; - private boolean rebootWhenBandReady = false; +// private byte[] newFirmware; +// private boolean rebootWhenBandReady = false; + private UpdateCoordinator updateCoordinator; public UpdateFirmwareOperation(Uri uri, MiBandSupport support) { super(support); @@ -38,20 +40,25 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { @Override protected void doPerform() throws IOException { MiBandFWHelper mFwHelper = new MiBandFWHelper(uri, getContext()); - String mMac = getDevice().getAddress(); - String[] mMacOctets = mMac.split(":"); - int newFwVersion = mFwHelper.getFirmwareVersion(); - int oldFwVersion = getSupport().getDeviceInfo().getFirmwareVersion(); - int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(mFwHelper.getFw()); +// if (getSupport().supportsHeartRate()) { + updateCoordinator = prepareFirmwareInfo1S(mFwHelper.getFw()); +// } else { +// updateCoordinator = sendFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion()); +// } - sendFirmwareInfo(oldFwVersion, newFwVersion, mFwHelper.getFw().length, checksum); - firmwareInfoSent = true; - newFirmware = mFwHelper.getFw(); + updateCoordinator.initNextOperation(); + updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band fw update + firmwareInfoSent = updateCoordinator.sendFwInfo(); + if (!firmwareInfoSent) { + GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); + done(); + } //the firmware will be sent by the notification listener if the band confirms that the metadata are ok. } private void done() { + updateCoordinator = null; operationFinished(); unsetBusy(); } @@ -83,32 +90,49 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { getSupport().logMessageContent(value); return; } + if (updateCoordinator == null) { + LOG.error("received notification when updateCoordinator is null, ignoring!"); + return; + } + switch (value[0]) { case MiBandService.NOTIFY_FW_CHECK_SUCCESS: - if (firmwareInfoSent && newFirmware != null) { - if (sendFirmwareData(newFirmware)) { - rebootWhenBandReady = true; +// if (firmwareInfoSent && newFirmware != null) { + if (firmwareInfoSent) { + GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO); + if (updateCoordinator.sendFwData()) { +// if (sendFirmwareData(newFirmware)) { +// rebootWhenBandReady = true; // disabled for testing } else { //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); } firmwareInfoSent = false; - newFirmware = null; +// newFirmware = null; } break; case MiBandService.NOTIFY_FW_CHECK_FAILED: GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR); firmwareInfoSent = false; - newFirmware = null; +// newFirmware = null; done(); break; case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS: - if (rebootWhenBandReady) { + if (updateCoordinator.initNextOperation()) { + GB.toast(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO); + if (!updateCoordinator.sendFwInfo()) { + GB.toast(getContext(), "Sending Mi Band Firmware failed, aborting. Do NOT reboot your Mi Band!", Toast.LENGTH_LONG, GB.INFO); + done(); + } + break; + } else if (updateCoordinator.needsReboot()) { GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext()); getSupport().onReboot(); - rebootWhenBandReady = false; +// rebootWhenBandReady = false; + } else { + LOG.error("BUG: Successful firmware update without reboot???"); } done(); break; @@ -116,7 +140,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); - rebootWhenBandReady = false; +// rebootWhenBandReady = false; done(); break; @@ -126,20 +150,103 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } } - /** - * Prepare the MiBand to receive the new firmware data. - * Some information about the new firmware version have to be pushed to the MiBand before sending - * the actual firmare. - *

- * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it. - * - * @param currentFwVersion - * @param newFwVersion - * @param newFwSize - * @param checksum - * @see MiBandSupport#handleNotificationNotif - */ - private void sendFirmwareInfo(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) throws IOException { +// /** +// * Prepare the MiBand to receive the new firmware data. +// * Some information about the new firmware version have to be pushed to the MiBand before sending +// * the actual firmare. +// *

+// * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it. +// * +// * @param fwBytes +// * @param newFwVersion +// * @see MiBandSupport#handleNotificationNotif +// */ +// private byte[] sendFirmwareInfo(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) throws IOException { +// private UpdateCoordinator sendFirmwareInfo(byte[] fwBytes, int newFwVersion) throws IOException { +// int newFwSize = fwBytes.length; +// String mMac = getDevice().getAddress(); +// String[] mMacOctets = mMac.split(":"); +// int currentFwVersion = getSupport().getDeviceInfo().getFirmwareVersion(); +// int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(fwBytes); +// +// byte[] fwInfo = new byte[]{ +// MiBandService.COMMAND_SEND_FIRMWARE_INFO, +// (byte) currentFwVersion, +// (byte) (currentFwVersion >> 8), +// (byte) (currentFwVersion >> 16), +// (byte) (currentFwVersion >> 24), +// (byte) newFwVersion, +// (byte) (newFwVersion >> 8), +// (byte) (newFwVersion >> 16), +// (byte) (newFwVersion >> 24), +// (byte) newFwSize, +// (byte) (newFwSize >> 8), +// (byte) checksum, +// (byte) (checksum >> 8) +//// (byte) (checksum >> 8), +//// (byte) 0 // TEST, only for Mi1S! +// }; +// return new SingleUpdateCoordinator(fwInfo, fwBytes); +// } + + private UpdateCoordinator prepareFirmwareInfo1S(byte[] wholeFirmwareBytes) { + int fw2Version = Mi1SInfo.getFirmware2VersionFrom(wholeFirmwareBytes); + int fw2Offset = Mi1SInfo.getFirmware2OffsetIn(wholeFirmwareBytes); + int fw2Length = Mi1SInfo.getFirmware2LengthIn(wholeFirmwareBytes); + + int fw1Version = Mi1SInfo.getFirmware1VersionFrom(wholeFirmwareBytes); + int fw1Offset = Mi1SInfo.getFirmware1OffsetIn(wholeFirmwareBytes); + int fw1Length = Mi1SInfo.getFirmware1LengthIn(wholeFirmwareBytes); + + String[] mMacOctets = getDevice().getAddress().split(":"); + int encodedMac = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])); + + byte[] fw2Bytes = new byte[fw2Length]; + System.arraycopy(wholeFirmwareBytes, fw2Offset, fw2Bytes, 0, fw2Length); + int fw2Checksum = CheckSums.getCRC16(fw2Bytes) ^ encodedMac; + + byte[] fw1Bytes = new byte[fw1Length]; + System.arraycopy(wholeFirmwareBytes, fw1Offset, fw1Bytes, 0, fw1Length); + int fw1Checksum = encodedMac ^ CheckSums.getCRC16(fw1Bytes); + + // check firmware validity? + + int fw1OldVersion = getSupport().getDeviceInfo().getFirmwareVersion(); + int fw2OldVersion = getSupport().getDeviceInfo().getHeartrateFirmwareVersion(); + + boolean rebootWhenFinished = true; + if (Mi1SInfo.isSingleMiBandFirmware(wholeFirmwareBytes)) { + LOG.info("is single Mi Band firmware"); + byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 0, rebootWhenFinished /*, progress monitor */); + return new SingleUpdateCoordinator(fw1Info, fw1Bytes, rebootWhenFinished); + } else { + LOG.info("is multi Mi Band firmware, sending fw2 (hr) now"); + byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */); + byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 1, rebootWhenFinished /*, progress monitor */); + return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished); + } + } + +// private Transaction createUpdateFirmwareTransaction() { +// +// } + + private byte[] prepareFirmwareInfo(byte[] fwBytes, int currentFwVersion, int newFwVersion, int checksum, int something, boolean reboot) { + byte[] fwInfo; + switch (something) { + case -1: + fwInfo = prepareFirmwareUpdateA(currentFwVersion, newFwVersion, fwBytes.length, checksum); + break; + case -2: + fwInfo = prepareFirmwareUpdateB(currentFwVersion, newFwVersion, fwBytes.length, checksum, 0); + break; + default: + fwInfo = prepareFirmwareUpdateB(currentFwVersion, newFwVersion, fwBytes.length, checksum, something); + } + return fwInfo; + } + + private byte[] prepareFirmwareUpdateA(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) { byte[] fwInfo = new byte[]{ MiBandService.COMMAND_SEND_FIRMWARE_INFO, (byte) currentFwVersion, @@ -154,15 +261,50 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { (byte) (newFwSize >> 8), (byte) checksum, (byte) (checksum >> 8) -// (byte) (checksum >> 8), -// (byte) 0 // TEST, only for Mi1S! }; - TransactionBuilder builder = performInitialized("send firmware info"); - builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), fwInfo); - builder.queue(getQueue()); +// byte[] fwInfo = new byte[]{ +// MiBandService.COMMAND_SEND_FIRMWARE_INFO +// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24), +// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24), +// (byte) newFwSize, (byte) (newFwSize >> 8), +// (byte) checksum, (byte) (checksum >> 8)})) { + return fwInfo; } + private byte[] prepareFirmwareUpdateB(int currentFwVersion, int newFwVersion, int newFwSize, int checksum, int something) { + byte[] fwInfo = new byte[]{ + MiBandService.COMMAND_SEND_FIRMWARE_INFO, + (byte) currentFwVersion, + (byte) (currentFwVersion >> 8), + (byte) (currentFwVersion >> 16), + (byte) (currentFwVersion >> 24), + (byte) newFwVersion, + (byte) (newFwVersion >> 8), + (byte) (newFwVersion >> 16), + (byte) (newFwVersion >> 24), + (byte) newFwSize, + (byte) (newFwSize >> 8), + (byte) checksum, + (byte) (checksum >> 8), + (byte) something // 0 TEST, only for Mi1S! + }; + +// // send to CONTROL POINT: +// if (!this.b(CONTROL_POINT, new byte[]{7, +// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24), +// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24), +// (byte) newFwSize, (byte) (newFwSize >> 8), +// (byte) checksum, (byte) (checksum >> 8), (byte) something})) { +// return false; +// } +// // wait for bq != -1 +// if (bq == 12) { +// return true; +// } + return fwInfo; + } + + /** * Method that uploads a firmware (fwbytes) to the MiBand. * The firmware has to be splitted into chunks of 20 bytes each, and periodically a COMMAND_SYNC comand has to be issued to the MiBand. @@ -173,18 +315,17 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { * @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[]) { + private boolean sendFirmwareData(byte[] fwbytes) { int len = fwbytes.length; final int packetLength = 20; int packets = len / packetLength; - byte fwChunk[] = new byte[packetLength]; int firmwareProgress = 0; try { TransactionBuilder builder = performInitialized("send firmware packet"); for (int i = 0; i < packets; i++) { - fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); + byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk); firmwareProgress += packetLength; @@ -198,8 +339,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } if (!(len % packetLength == 0)) { - byte lastChunk[] = new byte[len % packetLength]; - lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); + byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk); firmwareProgress += len % packetLength; } @@ -220,4 +360,134 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } return true; } + + private abstract class UpdateCoordinator { + private final boolean reboot; + + public UpdateCoordinator(boolean needsReboot) { + this.reboot = needsReboot; + } + + abstract byte[] getFirmwareInfo(); + + public abstract byte[] getFirmwareBytes(); + + public abstract boolean initNextOperation(); + + public boolean sendFwInfo() { + try { + TransactionBuilder builder = performInitialized("send firmware info"); + builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo()); + builder.queue(getQueue()); + return true; + } catch (IOException e) { + LOG.error("Error sending firmware info: " + e.getLocalizedMessage(), e); + return false; + } + } + + public boolean sendFwData() { + return sendFirmwareData(getFirmwareBytes()); + } + + public boolean needsReboot() { + return false; // FIXME: renable rebooting +// return reboot; + } + } + + private class SingleUpdateCoordinator extends UpdateCoordinator { + + private final byte[] fwInfo; + private final byte[] fwData; + + public SingleUpdateCoordinator(byte[] fwInfo, byte[] fwData, boolean reboot) { + super(reboot); + this.fwInfo = fwInfo; + this.fwData = fwData; + } + + @Override + public byte[] getFirmwareInfo() { + return fwInfo; + } + + @Override + public byte[] getFirmwareBytes() { + return fwData; + } + + @Override + public boolean initNextOperation() { + return false; + } + } + + enum State { + INITIAL, + SEND_FW2, + SEND_FW1, + FINISHED, UNKNOWN + } + + private class DoubleUpdateCoordinator extends UpdateCoordinator { + + private final byte[] fw1Info; + private final byte[] fw1Data; + + private final byte[] fw21nfo; + private final byte[] fw2Data; + + private byte[] currentFwInfo; + private byte[] currentFwData; + + private State state = State.INITIAL; + + public DoubleUpdateCoordinator(byte[] fw1Info, byte[] fw1Data, byte[] fw2Info, byte[] fw2Data, boolean reboot) { + super(reboot); + this.fw1Info = fw1Info; + this.fw1Data = fw1Data; + this.fw21nfo = fw2Info; + this.fw2Data = fw2Data; + + // start with fw2 (heart rate) + currentFwInfo = fw2Info; + currentFwData = fw2Data; + } + + @Override + public byte[] getFirmwareInfo() { + return currentFwInfo; + } + + @Override + public byte[] getFirmwareBytes() { + return currentFwData; + } + + @Override + public boolean initNextOperation() { + switch (state) { + case INITIAL: + currentFwInfo = fw21nfo; + currentFwData = fw2Data; + state = State.SEND_FW2; + return true; + case SEND_FW2: + currentFwInfo = fw1Info; + currentFwData = fw1Data; + state = State.SEND_FW1; + return fw1Info != null && fw1Data != null; + case SEND_FW1: + currentFwInfo = null; + currentFwData = null; + state = State.FINISHED; + return false; // we're done + default: + state = State.UNKNOWN; + } + return false; + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc9e2a29..22fafcfd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,6 +24,7 @@ FW/App installer You are about to install firmware %s instead of the one currently on your Mi Band. + You are about to install firmwares %1$s and %2$s 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 NOT encouraged to flash it to your Mi Band!" If you still want to proceed and things continue to work properly afterwards, please tell the Gadgetbridge developers to whitelist firmware version: %s From 4aaf3dd162644ab929558dd761b08ffe56aa1e58 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 13 Mar 2016 20:10:02 +0100 Subject: [PATCH 113/274] Use hw version to make device names unique, then mac addr --- .../gadgetbridge/adapter/GBDeviceAdapter.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index d4b555c3..6b48ad64 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -42,24 +42,28 @@ public class GBDeviceAdapter extends ArrayAdapter { } TextView deviceStatusLabel = (TextView) view.findViewById(R.id.device_status); TextView deviceNameLabel = (TextView) view.findViewById(R.id.device_name); - TextView deviceInfoLabel = (TextView) view.findViewById(R.id.device_info); + TextView deviceInfo1Label = (TextView) view.findViewById(R.id.device_info1); + TextView deviceInfo2Label = (TextView) view.findViewById(R.id.device_info2); TextView batteryStatusLabel = (TextView) view.findViewById(R.id.battery_status); ImageView deviceImageView = (ImageView) view.findViewById(R.id.device_image); ProgressBar busyIndicator = (ProgressBar) view.findViewById(R.id.device_busy_indicator); deviceNameLabel.setText(getUniqueDeviceName(device)); - deviceInfoLabel.setText(device.getInfoString()); + deviceInfo1Label.setText(device.getHWInfoString()); + deviceInfo2Label.setText(device.getInfoString()); if (device.isBusy()) { deviceStatusLabel.setText(device.getBusyTask()); busyIndicator.setVisibility(View.VISIBLE); batteryStatusLabel.setVisibility(View.GONE); - deviceInfoLabel.setVisibility(View.GONE); + deviceInfo1Label.setVisibility(View.GONE); + deviceInfo2Label.setVisibility(View.GONE); } else { deviceStatusLabel.setText(device.getStateString()); busyIndicator.setVisibility(View.GONE); batteryStatusLabel.setVisibility(View.VISIBLE); - deviceInfoLabel.setVisibility(View.VISIBLE); + deviceInfo1Label.setVisibility(View.VISIBLE); + deviceInfo2Label.setVisibility(View.VISIBLE); } short batteryLevel = device.getBatteryLevel(); @@ -97,7 +101,10 @@ public class GBDeviceAdapter extends ArrayAdapter { private String getUniqueDeviceName(GBDevice device) { String deviceName = device.getName(); if (!isUniqueDeviceName(device, deviceName)) { - deviceName = deviceName + " " + device.getShortAddress(); + deviceName = deviceName + " " + device.getHardwareVersion(); + if (!isUniqueDeviceName(device, deviceName)) { + deviceName = deviceName + " " + device.getShortAddress(); + } } return deviceName; } From e26e6d7b24b946bae0d751253a5dcc4b25d53ea3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 16 Mar 2016 00:14:38 +0100 Subject: [PATCH 114/274] Display HR firmware version Hide fw,hw,hr versions by default and show them on demand with an info button. --- .../gadgetbridge/adapter/GBDeviceAdapter.java | 78 +++++++++-- .../adapter/ItemWithDetailsAdapter.java | 10 +- .../gadgetbridge/impl/GBDevice.java | 65 +++++++-- .../gadgetbridge/model/GenericItem.java | 62 +++++++++ .../gadgetbridge/model/ItemWithDetails.java | 10 +- .../service/devices/miband/DeviceInfo.java | 15 +-- .../service/devices/miband/MiBandSupport.java | 9 +- .../operations/FetchActivityOperation.java | 2 +- .../ic_information_outline_grey600_24dp.png | Bin 0 -> 1138 bytes .../ic_information_outline_grey600_24dp.png | Bin 0 -> 619 bytes .../ic_information_outline_grey600_24dp.png | Bin 0 -> 1269 bytes .../ic_information_outline_grey600_24dp.png | Bin 0 -> 1814 bytes .../main/res/drawable/information_outline.xml | 8 ++ app/src/main/res/layout/device_item.xml | 126 ++++++++++-------- .../layout/item_with_details_horizontal.xml | 44 ++++++ app/src/main/res/values/strings.xml | 2 + 16 files changed, 336 insertions(+), 95 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_information_outline_grey600_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_information_outline_grey600_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_information_outline_grey600_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_information_outline_grey600_24dp.png create mode 100644 app/src/main/res/drawable/information_outline.xml create mode 100644 app/src/main/res/layout/item_with_details_horizontal.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index 6b48ad64..b655a601 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -8,14 +8,19 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; +import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; +import java.text.Collator; +import java.util.Collections; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; +import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; /** * Adapter for displaying GBDevice instances. @@ -32,7 +37,7 @@ public class GBDeviceAdapter extends ArrayAdapter { @Override public View getView(int position, View view, ViewGroup parent) { - GBDevice device = getItem(position); + final GBDevice device = getItem(position); if (view == null) { LayoutInflater inflater = (LayoutInflater) context @@ -42,37 +47,59 @@ public class GBDeviceAdapter extends ArrayAdapter { } TextView deviceStatusLabel = (TextView) view.findViewById(R.id.device_status); TextView deviceNameLabel = (TextView) view.findViewById(R.id.device_name); - TextView deviceInfo1Label = (TextView) view.findViewById(R.id.device_info1); - TextView deviceInfo2Label = (TextView) view.findViewById(R.id.device_info2); + final ListView deviceInfoList = (ListView) view.findViewById(R.id.device_item_infos); + ItemWithDetailsAdapter infoAdapter = new ItemWithDetailsAdapter(context, device.getDeviceInfos()); + infoAdapter.setHorizontalAlignment(true); + deviceInfoList.setAdapter(infoAdapter); + TextView batteryLabel = (TextView) view.findViewById(R.id.battery_label); TextView batteryStatusLabel = (TextView) view.findViewById(R.id.battery_status); - ImageView deviceImageView = (ImageView) view.findViewById(R.id.device_image); + final ImageView deviceImageView = (ImageView) view.findViewById(R.id.device_image); + ImageView deviceInfoView = (ImageView) view.findViewById(R.id.device_info_image); ProgressBar busyIndicator = (ProgressBar) view.findViewById(R.id.device_busy_indicator); deviceNameLabel.setText(getUniqueDeviceName(device)); - deviceInfo1Label.setText(device.getHWInfoString()); - deviceInfo2Label.setText(device.getInfoString()); if (device.isBusy()) { deviceStatusLabel.setText(device.getBusyTask()); busyIndicator.setVisibility(View.VISIBLE); + batteryLabel.setVisibility(View.GONE); batteryStatusLabel.setVisibility(View.GONE); - deviceInfo1Label.setVisibility(View.GONE); - deviceInfo2Label.setVisibility(View.GONE); } else { deviceStatusLabel.setText(device.getStateString()); busyIndicator.setVisibility(View.GONE); + batteryLabel.setVisibility(View.VISIBLE); batteryStatusLabel.setVisibility(View.VISIBLE); - deviceInfo1Label.setVisibility(View.VISIBLE); - deviceInfo2Label.setVisibility(View.VISIBLE); } + boolean showInfoIcon = device.hasDeviceInfos() && !device.isBusy(); + deviceInfoView.setVisibility(showInfoIcon ? View.VISIBLE : View.GONE); + deviceInfoView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (deviceInfoList.getVisibility() == View.VISIBLE) { + deviceInfoList.setVisibility(View.GONE); + } else { + ArrayAdapter adapter = (ArrayAdapter) deviceInfoList.getAdapter(); + adapter.clear(); + List infos = device.getDeviceInfos(); + Collections.sort(infos); + adapter.addAll(infos); + justifyListViewHeightBasedOnChildren(deviceInfoList); + deviceInfoList.setVisibility(View.VISIBLE); + } + } + }); + short batteryLevel = device.getBatteryLevel(); if (batteryLevel != GBDevice.BATTERY_UNKNOWN) { - batteryStatusLabel.setText("BAT: " + device.getBatteryLevel() + "%"); + batteryLabel.setText("BAT:"); + batteryStatusLabel.setText(device.getBatteryLevel() + "%"); BatteryState batteryState = device.getBatteryState(); if (BatteryState.BATTERY_LOW.equals(batteryState)) { + batteryLabel.setTextColor(Color.RED); batteryStatusLabel.setTextColor(Color.RED); } else { + batteryLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.secondarytext)); batteryStatusLabel.setTextColor(ContextCompat.getColor(getContext(), R.color.secondarytext)); if (BatteryState.BATTERY_CHARGING.equals(batteryState) || @@ -81,6 +108,7 @@ public class GBDeviceAdapter extends ArrayAdapter { } } } else { + batteryLabel.setText(""); batteryStatusLabel.setText(""); } @@ -98,11 +126,35 @@ public class GBDeviceAdapter extends ArrayAdapter { return view; } + public void justifyListViewHeightBasedOnChildren (ListView listView) { + ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter(); + + if (adapter == null) { + return; + } + ViewGroup vg = listView; + int totalHeight = 0; + for (int i = 0; i < adapter.getCount(); i++) { + View listItem = adapter.getView(i, null, vg); + listItem.measure(0, 0); + totalHeight += listItem.getMeasuredHeight(); + } + + ViewGroup.LayoutParams par = listView.getLayoutParams(); + par.height = totalHeight + (listView.getDividerHeight() * (adapter.getCount() - 1)); + listView.setLayoutParams(par); + listView.requestLayout(); + } + private String getUniqueDeviceName(GBDevice device) { String deviceName = device.getName(); if (!isUniqueDeviceName(device, deviceName)) { - deviceName = deviceName + " " + device.getHardwareVersion(); - if (!isUniqueDeviceName(device, deviceName)) { + if (device.getHardwareVersion() != null) { + deviceName = deviceName + " " + device.getHardwareVersion(); + if (!isUniqueDeviceName(device, deviceName)) { + deviceName = deviceName + " " + device.getShortAddress(); + } + } else { deviceName = deviceName + " " + device.getShortAddress(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java index dd1ab2da..fed094ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java @@ -19,6 +19,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; public class ItemWithDetailsAdapter extends ArrayAdapter { private final Context context; + private boolean horizontalAlignment; public ItemWithDetailsAdapter(Context context, List items) { super(context, 0, items); @@ -26,6 +27,9 @@ public class ItemWithDetailsAdapter extends ArrayAdapter { this.context = context; } + public void setHorizontalAlignment(boolean horizontalAlignment) { + this.horizontalAlignment = horizontalAlignment; + } @Override public View getView(int position, View view, ViewGroup parent) { ItemWithDetails item = getItem(position); @@ -34,7 +38,11 @@ public class ItemWithDetailsAdapter extends ArrayAdapter { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - view = inflater.inflate(R.layout.item_with_details, parent, false); + if (horizontalAlignment) { + view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false); + } else { + view = inflater.inflate(R.layout.item_with_details, parent, false); + } } ImageView iconView = (ImageView) view.findViewById(R.id.item_image); TextView nameView = (TextView) view.findViewById(R.id.item_name); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 9e832494..e296ad98 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -10,10 +10,15 @@ import android.support.v4.content.LocalBroadcastManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.List; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; +import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; public class GBDevice implements Parcelable { public static final String ACTION_DEVICE_CHANGED @@ -34,6 +39,8 @@ public class GBDevice implements Parcelable { public static final short BATTERY_UNKNOWN = -1; private static final short BATTERY_THRESHOLD_PERCENT = 10; public static final String EXTRA_DEVICE = "device"; + private static final String DEVINFO_HW_VER = "HW: "; + private static final String DEVINFO_FW_VER = "FW: "; private final String mName; private final String mAddress; private final DeviceType mDeviceType; @@ -45,6 +52,8 @@ public class GBDevice implements Parcelable { private BatteryState mBatteryState; private short mRssi = RSSI_UNKNOWN; private String mBusyTask; + private String infoString; + private List mDeviceInfos; public GBDevice(String address, String name, DeviceType deviceType) { mAddress = address; @@ -65,6 +74,7 @@ public class GBDevice implements Parcelable { mBatteryState = (BatteryState) in.readSerializable(); mRssi = (short) in.readInt(); mBusyTask = in.readString(); + mDeviceInfos = in.readArrayList(getClass().getClassLoader()); validate(); } @@ -82,6 +92,7 @@ public class GBDevice implements Parcelable { dest.writeSerializable(mBatteryState); dest.writeInt(mRssi); dest.writeString(mBusyTask); + dest.writeList(mDeviceInfos); } private void validate() { @@ -213,18 +224,6 @@ public class GBDevice implements Parcelable { return GBApplication.getContext().getString(R.string.unknown_state); } - - public String getInfoString() { - if (mFirmwareVersion != null) { - if (mHardwareVersion != null) { - return GBApplication.getContext().getString(R.string.connectionstate_hw_fw, mHardwareVersion, mFirmwareVersion); - } - return GBApplication.getContext().getString(R.string.connectionstate_fw, mFirmwareVersion); - } else { - return ""; - } - } - public DeviceType getType() { return mDeviceType; } @@ -330,6 +329,48 @@ public class GBDevice implements Parcelable { return ""; } + public boolean hasDeviceInfos() { + return getDeviceInfos().size() > 0; + } + + public List getDeviceInfos() { + List result = new ArrayList<>(); + if (mDeviceInfos != null) { + result.addAll(mDeviceInfos); + } + if (mHardwareVersion != null) { + result.add(new GenericItem(DEVINFO_HW_VER, mHardwareVersion)); + } + if (mFirmwareVersion != null) { + result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion)); + } + return result; + } + + public void setDeviceInfos(List deviceInfos) { + this.mDeviceInfos = deviceInfos; + } + + public void addDeviceInfo(ItemWithDetails info) { + if (mDeviceInfos == null) { + mDeviceInfos = new ArrayList<>(); + } else { + int index = mDeviceInfos.indexOf(info); + if (index >= 0) { + mDeviceInfos.set(index, info); // replace item with new one + return; + } + } + mDeviceInfos.add(info); + } + + public boolean removeDeviceInfo(ItemWithDetails info) { + if (mDeviceInfos == null) { + return false; + } + return mDeviceInfos.remove(info); + } + public enum State { // Note: the order is important! NOT_CONNECTED, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java index f2350768..1e28c869 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java @@ -1,10 +1,31 @@ package nodomain.freeyourgadget.gadgetbridge.model; +import android.os.Parcel; +import android.os.Parcelable; + +import java.text.Collator; + public class GenericItem implements ItemWithDetails { private String name; private String details; private int icon; + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public GenericItem createFromParcel(Parcel source) { + GenericItem item = new GenericItem(); + item.setName(source.readString()); + item.setDetails(source.readString()); + item.setIcon(source.readInt()); + return item; + } + + @Override + public GenericItem[] newArray(int size) { + return new GenericItem[size]; + } + }; + public GenericItem(String name, String details) { this.name = name; this.details = details; @@ -17,6 +38,13 @@ public class GenericItem implements ItemWithDetails { public GenericItem() { } + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(getName()); + dest.writeString(getDetails()); + dest.writeInt(getIcon()); + } + public void setName(String name) { this.name = name; } @@ -43,4 +71,38 @@ public class GenericItem implements ItemWithDetails { public int getIcon() { return icon; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GenericItem that = (GenericItem) o; + + return !(getName() != null ? !getName().equals(that.getName()) : that.getName() != null); + + } + + @Override + public int hashCode() { + return getName() != null ? getName().hashCode() : 0; + } + + @Override + public int compareTo(ItemWithDetails another) { + if (getName() == another.getName()) { + return 0; + } + if (getName() == null) { + return +1; + } else if (another.getName() == null) { + return -1; + } + return Collator.getInstance().compare(getName(), another.getName()); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java index dcb76883..0c5dc497 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java @@ -1,9 +1,17 @@ package nodomain.freeyourgadget.gadgetbridge.model; -public interface ItemWithDetails { +import android.os.Parcelable; + +public interface ItemWithDetails extends Parcelable, Comparable { String getName(); String getDetails(); int getIcon(); + + /** + * Equality is based on #getName() only. + * @param other + */ + boolean equals(Object other); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 5a704497..74450139 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -67,14 +67,6 @@ public class DeviceInfo extends AbstractInfo { return getInt(data, from, 4); } - public String getHumanFirmwareVersion() { - return MiBandFWHelper.formatFirmwareVersion(fwVersion); - } - - public String getHumanFirmware2Version() { - return MiBandFWHelper.formatFirmwareVersion(fw2Version); - } - public int getFirmwareVersion() { return fwVersion; } @@ -83,6 +75,10 @@ public class DeviceInfo extends AbstractInfo { return fw2Version; } + public boolean supportsHeartrate() { + return isMilli1S(); + } + @Override public String toString() { return "DeviceInfo{" + @@ -117,8 +113,7 @@ public class DeviceInfo extends AbstractInfo { return MiBandConst.MI_1A; } if (isMilli1S()) { - return getHumanFirmware2Version(); -// return MiBandConst.MI_1S; + return MiBandConst.MI_1S; } return "?"; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index 53272f32..be8b6182 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 @@ -36,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; @@ -551,7 +552,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } public boolean supportsHeartRate() { - return getDeviceInfo() != null && getDeviceInfo().isMilli1S(); + return getDeviceInfo() != null && getDeviceInfo().supportsHeartrate(); } @Override @@ -815,9 +816,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private void handleDeviceInfo(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { mDeviceInfo = new DeviceInfo(value); + if (getDeviceInfo().supportsHeartrate()) { + getDevice().addDeviceInfo(new GenericItem("HR:", MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getHeartrateFirmwareVersion()))); + } LOG.warn("Device info: " + mDeviceInfo); versionCmd.hwVersion = mDeviceInfo.getHwVersion(); - versionCmd.fwVersion = mDeviceInfo.getHumanFirmwareVersion(); + versionCmd.fwVersion = MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getFirmwareVersion()); handleGBDeviceEvent(versionCmd); } } @@ -936,7 +940,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { List mEvents = upcomingEvents.getCalendarEventList(getContext()); int iteration = 0; - ArrayList alarmList = new ArrayList<>(); for (CalendarEvents.CalendarEvent mEvt : mEvents) { if (iteration >= availableSlots || iteration > 2) { break; 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 e5e14441..42faa337 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 @@ -137,7 +137,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { public FetchActivityOperation(MiBandSupport support) { super(support); - hasExtendedActivityData = support.getDeviceInfo().isMilli1S(); + hasExtendedActivityData = support.getDeviceInfo().supportsHeartrate(); activityDataHolderSize = getBytesPerMinuteOfActivityData() * 60 * 4; // 4h activityStruct = new ActivityStruct(activityDataHolderSize); } diff --git a/app/src/main/res/drawable-hdpi/ic_information_outline_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_information_outline_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..28ac3bdda4777e8c1e28ba359692b5276abdf8b9 GIT binary patch literal 1138 zcmV-&1daQNP)7%Q6#m|dY&Kin z6G0Ob6Lx+SVmFxi8)iNW;5Q<=3INPJ2;e>JFk!rPNd;5;-lUtTY+(i~ju}rF^|uEFKj?Ofz%8p3w19sdNCq^IBl= z3MCSW_W&F>TxYkpw_jXZTKcn_XBCgf_x1MnehlES;eOwB-M5>?TP?&q17Oh*yky(< z%b84OyBje8kk99DjE;_eRxX$C1~8)CkHliJpL4m~rH&PnQucbD_bY%w?fQ0oef{ZH z%Qu-!J`CV70AS_|uIrv}7UwvQH8eEz6@VwT>#A+r4@xN;CMo;_JUuTY*h#`PDGp9HK@57&>+Zh91Fm)nyr+YN~hCX-9DyPNT<_V zN~tNsbxd8EwuYz5yp6E3AX2UP2 zzQik)O8;Jw`f@jXR{SsZ<=VAnSqJt?keS=$+#!gFRt@usy%J>R_Hi}}5<+}!m}64P z5Ox_l=U5BQMnQ!_VHLnNZ4MNR#iP3|Naw6IZr3ztb3u9&J!z<)PNh#hry6%_$*rqej<#Lx|vDh5|9@Vaqa=AP_JUskeKA*qQjhJ{m zzK@x+0LBgXjO)6mn#H#}bx<;y{FIryY4XjggnUhYEyw4XOr~X%-)>oRI2?Y#^Sm|a zV-Pd<6VXSW=e;vIIob9Yck&%%P zEz8;h@DKnyppffCG+n7wUYehu|0&3T|J|Prw{gqiZ@=c#YoLbajsO4v07*qoM6N<$ Eg7w-6a{vGU literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_information_outline_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_information_outline_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..213286b7faa55326b87cf11a73d69c411095f5ff GIT binary patch literal 619 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE(}6TtKSPDj(q#+`jNd$6978;gU!A%!U)WIM=za0adXh%MoPEJF zU$h(%am~4@bTMqx5B5`@qRB<=H}afs9KW)VId+DBR`-!r}8e+xHbFy^Zd>i4@^S)JpIr&UE zgMgEb!LgE#7px8L*F5~yCST8-B{jW#dCr3a>pNCVPrVr!$&$d5pt98FOYQ%LnfC<) zUsTF5xN!epaR2>#p^KfZo>bC-pHK5eL&;%rp2jY@1|YtVaSOLyU1{$@9vt% zU1t~>qOvbVh0oh#;>&9+zk5v+qo6G#gW{2(o#CPD&PK8_r|rwWecH9bwfcd9UTm1< zqODV;FZ$oUw_j7|`m;4fF3%YfdS2eSq5$TkU#pGx(hO_$)?^iY3pDFsiIpvL0b|1L z?|+*^dDo;#A81Ye+j(~GmcG@C1dbM1{JE92^_9yPkprL0cK=;{GpA0j|M*n*U@y&G zci(xZ{Y!Xg82_Jw!+^21>Cxtl1aHX7hdzzT)g;_GPoX z*=w4C4YbzYO370G$9#FT??`4&Vva_{Ha2#Gh$e~X9RSVM zOag#BGk?y^6Vubvj}KiyEEc;8;1d8RswA%BF@O)#>GT(eA|Mip1jFI*rvTp7wKH>$ zh~}C3djP)#gTV(aEiG99*xud_@9gZH2Jj*gy~fN#L=-BKYSuK(Q6WU3W&(r|ZI0u7 z3E&M)^8)}$(==}jA#!Do7jhiu3V?9{r!^tpn5Oxb5Mrxp0p84i1>mC6v<*O*ra2>o z$d^N0Bc$1JoG1VRphaoElg(x?XEK=`zcHHjOX9sYD)~o-VZ1ptHMLj?aY;ZPz{iP1 z;s@7tzXtHU)O^v){hr?#zE8litSdyME~?*mcXwZ(nVI>s8uBWl(P&p75cn3rS=rCb zS8dzAeP99O><=%;gyO#DaVk#$Gk<{M9d<37%}yUS`6uvltx2wCkJJ7Fgb)UR5!pvXsr^1@ zwL~V9*&(8o;vVt16=BaB*xK591=7KcnRCoMcTf`Lyz_o=U3U(^w6tS)dfa}J9dIHw4>S8wt@xzDLFIsx6X;kxdxjS`^A-L1yx z_JLEf!OUx@0F~?mDwuhVsHod1HAeRY+KY_#_P;9;Pzz{sx2rKmnPWY*&1nWgU5{U$vIj*>G=}M8>eSnBQ10Yu#&V<9^v3d#ca>-wyyvJz|v;w?; z-W!U0(z2}n!zSzX>2jM7@=ap&-x9v&?+iw(X^Zc0QVyd@34w f&3cSu9CiEyReujo$LVw-00000NkvXXu0mjf&pu4; literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_information_outline_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_information_outline_grey600_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fb2acbc6a12a7f7540163e6df80d5da0fa04a192 GIT binary patch literal 1814 zcmV+x2kH2UP)bx13{R3cc$I# zba8%~Hh=_=Yh^SR)q(3Nx7*{A1+K8xynP&l*4WJ3YBmlbGtN@@F zKo5W`M0AyzFX*~{RtRzDi4({$j3y%5Kt!vUc@cmyLv(eQh|V$dF=jrH$z*zJTA)-a z^$HQCn0YyX`U<-rAfl7ZY}>Z|V@(NU7{(GJ+6Lg|Y6boXKQZ&xOeS->(vB-C(B{pX zpNhp|Spe%sNSH@CR4SE{J9qBEh`KdR>xswX1pp`(i@M`DP0T!#nP+R7_7XEM!M)MW zFP|5S#bifEM}KGnB@&4V@p$|gfTcc~_X0TRjy~rAaL`x{!&pQ_8vv}wy|SPjr;5em zs*aA1zeil4EnBus85kJ&0l=IxO>Y3m>bkyL2+=px?>&qVqM=YId7whZm zU){ca`^^y(D4kAEbR6e&8F2;xbm+RiwNk=JgxUrm0d*O1QPZ>~?d|Qit1gfbVoae> z_#VJAMYC=qT4Pz(`DzIBD5h!7XXYaSW-6L>>bm}#5aMnS-&)XM`uh4lEhEl}VzD@9 zq{IOL%d*ZFi^VwrPAHl!b7K!gU696xVXPq{OVQ-Zd_Moy{{8!lBPGs#y1Kghr%#`L zq_MH_X#jJ*+7Qu#*4EbFa=F|kzkYrH#M~nAGJr;zIJRxShv1`Ar6I#G_7TxL68o)E zsr0~PQ`5A0 zLm46NlS-vLg|%&a$YqE-(*6qI8PCLtmRXi{s+=ERJ;-sKZ4%P}5v?7LIMt%Pz5O;3 zt;M}^BS=oc1WG28i;1Ys%SJ@IMsl$D)z#IR64MO;B5V7% z+*o@(O!aP#Qk2V}nx?(Z{P+OF%!evDzZ%YHi0C7)90BZ5{HhAwo+_Ws~m#`BKiW{@nlE)Y~*I)t>QH#XXiP2Fp z2Zi0(gXAg~$Z?$KJio$xF6>N)iY#_nu7J7+&60fN_plQkDzeyRxdQ4@Y?{}@SSRE;&_VKcJ0x!A# z-%ttUsdj&V|9I3WirA&U3Y9>fQwe7Fvtc(XQN%8tHmL-m(!JH<537sJjrC~nMCJWO}pT!RTDgt$XQvdP`sVJ93ES@Y6Kxf0}**13zX&POCSIT;k~ak$vvM_6S%Q@)j$Joj#88YWipu_B04EC zCr#5F^;NYSW75k;L?_FgwJtjs8yp;zo#vQ|+SwQLJcXvn?Ez&4%4V}?nA!8MJTq@H zO>=(O3dnE|R&A2Rem0xUo*A}40HA5wR*9*enGdJa>9CLfK1Mp7p2*CHAz2huHy@MY{&EY{2ND)9v$@SubQl0y}GWsx%nFaD?Kyh@yen(N%xg25 z%n`pn1D#RK<#Lx=TU(omXn~~3yvD}Hxh*X%-*t6$4eNw^P!fs6gvpa99|7>DLYyx$ znT#F8Z{Q`@hK7buAlZO(SrL!NFOKBsW}4>wcsza)zzRjPPB->IL + + + \ No newline at end of file diff --git a/app/src/main/res/layout/device_item.xml b/app/src/main/res/layout/device_item.xml index 246dca8d..d7b4b1bb 100644 --- a/app/src/main/res/layout/device_item.xml +++ b/app/src/main/res/layout/device_item.xml @@ -1,75 +1,93 @@ - + - + android:singleLine="true" + android:typeface="sans" /> - + - + - + - + - - - + - - \ No newline at end of file + + + + + diff --git a/app/src/main/res/layout/item_with_details_horizontal.xml b/app/src/main/res/layout/item_with_details_horizontal.xml new file mode 100644 index 00000000..8c318025 --- /dev/null +++ b/app/src/main/res/layout/item_with_details_horizontal.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 22fafcfd..3d60e89a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -231,5 +231,7 @@ Add widget Preferred sleep duration in hours An alarm was set for %1$02d:%2$02d + HW: %1$s + FW: %1$s From c5a887192de1833674caff464ffed21297367149 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 16 Mar 2016 00:22:06 +0100 Subject: [PATCH 115/274] Remove/revert some temporary test code --- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 2 +- .../gadgetbridge/service/devices/miband/Mi1SInfo.java | 4 +++- .../devices/miband/operations/UpdateFirmwareOperation.java | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 2d796e10..ef5b7fbd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -167,7 +167,7 @@ public class MiBandFWHelper { if (MiBandConst.MI_1A.equals(deviceHW)) { return getFirmwareVersionMajor() == 5; } - if (true || MiBandConst.MI_1S.equals(deviceHW)) { // FIXME: REMOVE TEMPORARY HACK + if (MiBandConst.MI_1S.equals(deviceHW)) { return getFirmwareVersionMajor() == 4; } return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java index 6d98e522..ea27e6eb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java @@ -54,11 +54,13 @@ public class Mi1SInfo { | wholeFirmwareBytes[25] & 255; } + // FIXME: this method is wrong. We don't know a way to check if a firmware file + // contains one or more firmwares. public static boolean isSingleMiBandFirmware(byte[] wholeFirmwareBytes) { if ((wholeFirmwareBytes[7] & 255) != 1) { return false; } - return false;// FIXME: hack -- should be true! + return true; } } 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 a351287c..68da0b40 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 @@ -48,7 +48,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { // } updateCoordinator.initNextOperation(); - updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band fw update +// updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band 1s fw update firmwareInfoSent = updateCoordinator.sendFwInfo(); if (!firmwareInfoSent) { GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); @@ -392,8 +392,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } public boolean needsReboot() { - return false; // FIXME: renable rebooting -// return reboot; + return reboot; } } From c224a40d0e0e2da076f55c66827f05e15a7989e3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 16 Mar 2016 22:37:14 +0100 Subject: [PATCH 116/274] update spanish translation from transifex (thanks!) --- app/src/main/res/values-es/strings.xml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9fe44650..f4e48054 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -49,6 +49,7 @@ Opciones de desarrollador Dirección de MiBand Ajustes de Pebble + Rastreador de actividad preferido Permitir el acceso a aplicaciones Android de terceros Permitir el soporte experimental para aplicaciones Android que usan PebbleKit Forzar protocolo de notificación @@ -103,12 +104,7 @@ Haz visible tu dispositivo. No es probable que se detecten los dispositivos que ya están conectados. Nota: Imagen del dispositivo - Sobre ti Nombre/Apodo - Año de nacimiento - Sexo - Altura en cm - Peso en kg Número de vibraciones Monitor de sueño Guardar logs (requiere reiniciar) @@ -202,4 +198,18 @@ Reserva de alarmas para próximos eventos esperando reconexión Reinstalar + Sobre ti + Año de nacimiento + Género + Altura (cm) + Peso (kg) + Activar + Desactivar + autenticando + autenticación requerida + Configurar + Zzz + Añadir widget + Horas de sueño deseadas + Una alarma ha sido fijada para %1$02d:%2$02d From 238e394d21c34e2bf355271b728dcbc92c9dda49 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 16 Mar 2016 22:38:27 +0100 Subject: [PATCH 117/274] update french translation from transifex (thanks) --- app/src/main/res/values-fr/strings.xml | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index de149c54..a5de1a18 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -19,8 +19,9 @@ Installateur d\'applications/micrologiciel 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. \n \n Il n\'est pas conseillé de flasher votre Mi Band. + Ce micrologiciel n\'a pas été testé et peut ne pas être compatible avec Gadgetbridge.\n\nIl n\'est pas conseillé de flasher votre Mi Band. Si vous désirez continuer et que tout fonctionne correctement par la suite, SVP informez-en les développeurs de Gadgetbridge pour demander l\'ajout de ce micrologiciel à leur liste: %s Paramètres @@ -38,8 +39,8 @@ K9-Mail Messages Pebble Support pour les applications qui envoient des notifications au Pebble via Intent. Peut être utilisé pour les conversations. - Support des notififactions génériques - ... y compris lorsque l\'écran est allumé + Support des notifications génériques + ... y compris quand l\'écran est allumé toujours quand l\'écran est éteint jamais @@ -68,8 +69,8 @@ Ceci est un test de notification venant de Gadgetbridge Le Bluetooth n\'est pas supporté. Le Bluetooth est désactivé. - tappez sur l\'appareil pour le connecter au gestionnaire d\'application - tappez sur l\'appareil pour le connecter + Cliquez sur l\'appareil pour le connecter au gestionnaire d\'application + Cliquez sur l\'appareil pour le connecter Connexion impossible. L’adresse Bluetooth est-elle invalide? Gadgetbridge est en fonctionnement Installation du binaire %1$d/%2$d @@ -97,18 +98,13 @@ Autre Gauche Droite - Aucune donnée utilisateur valide fournie, utilisation des données fictives pour le moment + 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 Rendez votre appareil découvrable. Les appareils déjà connectés ne seront pas découverts. Note: Image de l\'appareil - À propos de vous Nom/Pseudo - Année de naissance - Genre - Taille en cm - Poids en kg Nombre de vibrations Moniteur de sommeil Écrire les fichiers journaux (redémarrage requis) @@ -202,4 +198,11 @@ Alarmes à réserver pour événements futurs en attente de reconnexion Réinstaller + A propos de vous + Année de naissance + Genre + Taille en cm + Poids en kg + Activer + Désactiver From 61e3cf434866490ce41bee288ce790fa2e1bc195 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 16 Mar 2016 22:53:36 +0100 Subject: [PATCH 118/274] update japanese translation from transifex (thanks!) --- app/src/main/res/values-ja/strings.xml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index dda3ea21..a2021e85 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -19,6 +19,7 @@ ファームウェア・アプリのインストール お使いのMi Bandに、現在のファームウェアの代わりに%sをインストールしようとしています。 + お使いのMi Bandに、現在のファームウェアの代わりに %1$s および %2$s をインストールしようとしています。 このファームウェアはテスト済で、ガジェットブリッジと互換性があることがわかっています。 このファームウェアは未テストで、ガジェットブリッジと互換性がない可能性があります。\n\nお使いのMi Bandにフラッシュすることは奨励されていません! それでも続行して、その後正しく作業を続ける場合は、ガジェットブリッジ開発者にホワイトリスト ファームウェア バージョンを教えてください: %s @@ -49,6 +50,7 @@ 開発者用設定 Mi Bandのアドレス Pebbleの設定 + お好みのアクティビティ トラッカー 第三者のアンドロイドアップにアクセス権利を与える PebbleKitを使用してAndroidアプリ用の実験的なサポートを有効にします 通知プロトコルを強制する @@ -103,12 +105,7 @@ お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。 注: デバイスイメージ - あなたについて 名前/別名 - 誕生年 - 性別 - 身長(cm) - 体重(kg) バイブレーション回数 睡眠観測 ログファイルを出力 (再起動が必要) @@ -202,4 +199,20 @@ 今後のイベントのために予約するアラーム 再接続の待機中 再インストール + あなたについて + 誕生年 + 性別 + 身長(cm) + 体重(kg) + アクティベート + 非アクティベート + 認証中 + 認証が必要 + 設定 + Zzz + ウィジェットを追加 + 好ましい睡眠時間(時間) + %1$02d:%2$02d のアラームを設定しました + HW: %1$s + FW: %1$s From 89591fd5fe59301cc9323ca9bda85a7f0b3a5683 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 16 Mar 2016 22:54:46 +0100 Subject: [PATCH 119/274] update ukrankian translation from transifex (thanks) --- app/src/main/res/values-uk/strings.xml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index fe50d4f5..d0aac7a8 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -47,6 +47,7 @@ Параметри для розробників Адреса Mi—Band Параметри Pebble + Переважний трекер активності Дозволити доступ іншим особам Додати експериментальну підтримку додатків Android, які використовують PebbleKit Примусовий протокол сповіщень @@ -101,12 +102,7 @@ Під\'єднані на даний момент пристрої, скоріш за все не будуть виявлені. Замітка: Зображення пристрою - Ваші дані Ім\'я/нік - Рік народження - Стать - Зріст в см - Вага в кг Кількість вібрацій Аналіз сну Записувати файли звіту (потрібен перезапуск) @@ -181,6 +177,7 @@ Життєва активність Кроків сьогодні, мета: %1$s Якщо дані не будуть передані на пристрій, пристрій не буде очищений. Корисно, якщо Gadgetbridge використовується разом з іншими додатками. + Дозволяє лишити дані на Mi-браслеті після синхронізації. Зазвичай використовується, якщо GB працює ще з іншими додатками. Не передавати дані про активність Історія кроків Поточні кроки/хв @@ -191,4 +188,20 @@ Легкий сон Глибокий сон Знесилений + Не з\'єднано. + Всі будильники вимкнено + Лишати дані на пристрої + Мікропрограма не підтримується + Ця мікропрограма не підтримується даним пристроєм + очікування повторного підключення + Перевстановити + Про Вас + Рік народження + Стать + Зріст в см + Вага в кг + Увімкнути + Вимкнути + Конфігурація + Додати віджет From 1603d6014428d752460399bd3ca941534eb299fe Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 17 Mar 2016 15:28:43 +0100 Subject: [PATCH 120/274] right align info icon in control center --- app/src/main/res/layout/device_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/device_item.xml b/app/src/main/res/layout/device_item.xml index d7b4b1bb..34c14667 100644 --- a/app/src/main/res/layout/device_item.xml +++ b/app/src/main/res/layout/device_item.xml @@ -57,7 +57,7 @@ android:scaleType="centerInside" android:contentDescription="@string/candidate_item_device_image" android:clickable="true" - android:layout_gravity="center_vertical" /> + android:layout_gravity="center_vertical|right" /> Date: Fri, 18 Mar 2016 16:47:14 +0100 Subject: [PATCH 121/274] Do not show the configure menu item for non configurable watch apps. --- .../gadgetbridge/activities/AppManagerActivity.java | 8 +++++++- .../freeyourgadget/gadgetbridge/impl/GBDeviceApp.java | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 5955ce24..d7297120 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -102,11 +102,14 @@ public class AppManagerActivity extends Activity { for (File file : files) { if (file.getName().endsWith(".pbw")) { String baseName = file.getName().substring(0, file.getName().length() - 4); + //metadata File jsonFile = new File(cachePath, baseName + ".json"); + //configuration + File configFile = new File(cachePath, baseName + "_config.js"); try { String jsonstring = FileUtils.getStringFromFile(jsonFile); JSONObject json = new JSONObject(jsonstring); - cachedAppList.add(new GBDeviceApp(json)); + cachedAppList.add(new GBDeviceApp(json, configFile.exists())); } catch (Exception e) { LOG.warn("could not read json file for " + baseName, e.getMessage(), e); cachedAppList.add(new GBDeviceApp(UUID.fromString(baseName), baseName, "N/A", "", GBDeviceApp.Type.UNKNOWN)); @@ -178,6 +181,9 @@ public class AppManagerActivity extends Activity { } else if (PebbleProtocol.UUID_PEBBLE_HEALTH.equals(selectedApp.getUUID())) { menu.removeItem(R.id.appmanager_app_delete); } + if (!selectedApp.isConfigurable()) { + menu.removeItem(R.id.appmanager_app_configure); + } menu.setHeaderTitle(selectedApp.getName()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java index 9743a083..d2280d17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java @@ -12,6 +12,7 @@ public class GBDeviceApp { private final UUID uuid; private final Type type; private final boolean inCache; + private final boolean configurable; public GBDeviceApp(UUID uuid, String name, String creator, String version, Type type) { this.uuid = uuid; @@ -21,9 +22,10 @@ public class GBDeviceApp { this.type = type; //FIXME: do not assume this.inCache = false; + this.configurable = false; } - public GBDeviceApp(JSONObject json) { + public GBDeviceApp(JSONObject json, boolean configurable) { UUID uuid = UUID.fromString("00000000-0000-0000-0000-000000000000"); String name = ""; String creator = ""; @@ -47,6 +49,7 @@ public class GBDeviceApp { this.type = type; //FIXME: do not assume this.inCache = true; + this.configurable = configurable; } public boolean isInCache() { @@ -94,4 +97,8 @@ public class GBDeviceApp { } return json; } + + public boolean isConfigurable() { + return configurable; + } } From 538961fd2cca3b4ea4b7f9e990716b287a6ab2ed Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Fri, 18 Mar 2016 17:50:24 +0100 Subject: [PATCH 122/274] Add some style, intercept and display toast in case of JS errors --- app/src/main/assets/app_config/configure.html | 42 ++++++++++++++++--- .../activities/ExternalPebbleJSActivity.java | 10 +++++ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/app/src/main/assets/app_config/configure.html b/app/src/main/assets/app_config/configure.html index a4d10e98..dd77ad43 100644 --- a/app/src/main/assets/app_config/configure.html +++ b/app/src/main/assets/app_config/configure.html @@ -1,6 +1,7 @@ - + +

Url of the configuration:

-
- - +
+ +

Incoming configuration data:

-
- +
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 9e783eb5..f6138fb6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -7,7 +7,9 @@ import android.os.Bundle; import android.support.v4.app.NavUtils; import android.util.Log; import android.view.MenuItem; +import android.webkit.ConsoleMessage; import android.webkit.JavascriptInterface; +import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -64,6 +66,7 @@ public class ExternalPebbleJSActivity extends Activity { WebView myWebView = (WebView) findViewById(R.id.configureWebview); myWebView.clearCache(true); myWebView.setWebViewClient(new GBWebClient()); + myWebView.setWebChromeClient(new GBChromeClient()); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true); //needed to access the DOM @@ -90,6 +93,13 @@ public class ExternalPebbleJSActivity extends Activity { return null; } + private class GBChromeClient extends WebChromeClient { + @Override + public boolean onConsoleMessage(ConsoleMessage consoleMessage) { + GB.toast(consoleMessage.message(), Toast.LENGTH_LONG, GB.ERROR); + return super.onConsoleMessage(consoleMessage); + } + } private class GBWebClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { From c2ae9ec530993fce0b09944577e4b4a39646c048 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 18 Mar 2016 22:33:36 +0100 Subject: [PATCH 123/274] Update French translation from transifex (thanks) --- app/src/main/res/values-fr/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a5de1a18..0d578e24 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -50,6 +50,7 @@ Options développeur Adresse Mi Band Paramètres Pebble + Traqueur d\'activité préféré Permettre l\'accès d\'applications tierces Android Activer le support expérimental pour les applications Android utilisant PebbleKit Protocole des notifications en vigueur @@ -205,4 +206,9 @@ Poids en kg Activer Désactiver + authentification + authentification requise + Configurer + ZzZz + Ajouter un widget From 6d8d6d5bc810139c2d1f0d12ab664503ebb316ba Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 17 Mar 2016 23:41:41 +0100 Subject: [PATCH 124/274] Testcases for firmware checking --- app/build.gradle | 2 + .../service/devices/miband/Mi1SInfo.java | 20 ++-- .../service/devices/miband/FirmwareTest.java | 110 ++++++++++++++++++ 3 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java diff --git a/app/build.gradle b/app/build.gradle index 3746bc49..191981db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,8 @@ apply plugin: 'pmd' def ABORT_ON_CHECK_FAILURE=false +tasks.withType(Test) { systemProperty 'MiFirmwareDir', System.getProperty('MiFirmwareDir', null) } + android { compileSdkVersion 23 buildToolsVersion "23.0.2" diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java index ea27e6eb..7a1448e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java @@ -1,12 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; +import android.support.annotation.NonNull; + /** * FW1 is Mi Band firmware * FW2 is heartrate firmware */ public class Mi1SInfo { - public static int getFirmware2OffsetIn(byte[] wholeFirmwareBytes) + public static int getFirmware2OffsetIn(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[26] & 255) << 24 | (wholeFirmwareBytes[27] & 255) << 16 @@ -14,7 +16,7 @@ public class Mi1SInfo { | (wholeFirmwareBytes[29] & 255); } - public static int getFirmware2LengthIn(byte[] wholeFirmwareBytes) + public static int getFirmware2LengthIn(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[30] & 255) << 24 | (wholeFirmwareBytes[31] & 255) << 16 @@ -22,7 +24,7 @@ public class Mi1SInfo { | (wholeFirmwareBytes[33] & 255); } - public static int getFirmware1OffsetIn(byte[] wholeFirmwareBytes) + public static int getFirmware1OffsetIn(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[12] & 255) << 24 | (wholeFirmwareBytes[13] & 255) << 16 @@ -30,7 +32,7 @@ public class Mi1SInfo { | (wholeFirmwareBytes[15] & 255); } - public static int getFirmware1LengthIn(byte[] wholeFirmwareBytes) + public static int getFirmware1LengthIn(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[16] & 255) << 24 | (wholeFirmwareBytes[17] & 255) << 16 @@ -38,7 +40,7 @@ public class Mi1SInfo { | (wholeFirmwareBytes[19] & 255); } - public static int getFirmware1VersionFrom(byte[] wholeFirmwareBytes) + public static int getFirmware1VersionFrom(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[8] & 255) << 24 | (wholeFirmwareBytes[9] & 255) << 16 @@ -46,7 +48,7 @@ public class Mi1SInfo { | wholeFirmwareBytes[11] & 255; } - public static int getFirmware2VersionFrom(byte[] wholeFirmwareBytes) + public static int getFirmware2VersionFrom(@NonNull byte[] wholeFirmwareBytes) { return (wholeFirmwareBytes[22] & 255) << 24 | (wholeFirmwareBytes[23] & 255) << 16 @@ -56,11 +58,11 @@ public class Mi1SInfo { // FIXME: this method is wrong. We don't know a way to check if a firmware file // contains one or more firmwares. - public static boolean isSingleMiBandFirmware(byte[] wholeFirmwareBytes) { + public static boolean isSingleMiBandFirmware(@NonNull byte[] wholeFirmwareBytes) { if ((wholeFirmwareBytes[7] & 255) != 1) { - return false; + return true; } - return true; + return false; } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java new file mode 100644 index 00000000..b842ec1e --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -0,0 +1,110 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; + +public class FirmwareTest { + + private static final long MAX_FILE_SIZE_BYTES = 1024 * 1024; // 1MB + private static final int MI_FW_VERSION = 0; // FIXME + private static final int MI1A_FW_VERSION = 0; // FIXME + private static final int MI1S_FW1_VERSION = 0; + private static final int MI1S_FW2_VERSION = 0; + + @Test + public void testFirmwareMi() throws Exception{ + byte[] wholeFw = getFirmwareMi(); + Assert.assertNotNull(wholeFw); + + Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); + int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw); + Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length); + int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw); +// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion); + + String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); + Assert.assertTrue(version.startsWith("1.")); + } + + @Test + public void testFirmwareMi1A() throws Exception{ + byte[] wholeFw = getFirmwareMi1A(); + Assert.assertNotNull(wholeFw); + + Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); + int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw); + Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length); + int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw); +// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion); + + String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); + Assert.assertTrue(version.startsWith("5.")); + } + + @Test + public void testFirmwareMi1S() throws Exception{ + byte[] wholeFw = getFirmwareMi1S(); + Assert.assertNotNull(wholeFw); + + Assert.assertFalse(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); + + // Mi Band version + int calculatedLengthFw1 = Mi1SInfo.getFirmware1LengthIn(wholeFw); + int calculatedOffsetFw1 = Mi1SInfo.getFirmware1OffsetIn(wholeFw); + int endIndexFw1 = calculatedOffsetFw1 + calculatedLengthFw1; + + int calculatedLengthFw2 = Mi1SInfo.getFirmware2LengthIn(wholeFw); + int calculatedOffsetFw2 = Mi1SInfo.getFirmware2OffsetIn(wholeFw); + int endIndexFw2 = calculatedOffsetFw2 + calculatedLengthFw2; + + Assert.assertTrue(endIndexFw1 <= wholeFw.length - calculatedLengthFw2); + Assert.assertTrue(endIndexFw2 <= wholeFw.length); + + Assert.assertTrue(endIndexFw1 <= calculatedOffsetFw2); + int calculatedVersionFw1 = Mi1SInfo.getFirmware1VersionFrom(wholeFw); +// Assert.assertEquals("Unexpected firmware 1 version: " + calculatedVersionFw1, MI1S_FW1_VERSION, calculatedVersionFw1); + String version1 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw1); + Assert.assertTrue(version1.startsWith("4.")); + + // HR version + int calculatedVersionFw2 = Mi1SInfo.getFirmware2VersionFrom(wholeFw); +// Assert.assertEquals("Unexpected firmware 2 version: " + calculatedVersionFw2, MI1S_FW2_VERSION, calculatedVersionFw2); + String version2 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw2); + Assert.assertTrue(version2.startsWith("1.")); + } + + private File getFirmwareDir() { + String path = System.getProperty("MiFirmwareDir"); + Assert.assertNotNull(path); + File dir = new File(path); + Assert.assertTrue(dir.isDirectory()); + return dir; + } + + private byte[] getFirmwareMi() throws IOException { + return getFirmware(new File(getFirmwareDir(), "Mili.fw")); + } + + private byte[] getFirmwareMi1A() throws IOException { + return getFirmware(new File(getFirmwareDir(), "Mili_1a.fw")); + } + + private byte[] getFirmwareMi1S() throws IOException { + return getFirmware(new File(getFirmwareDir(), "Mili_hr.fw")); + } + + private byte[] getFirmware(File aFile) throws IOException { + Assert.assertNotNull(aFile); + try (FileInputStream stream = new FileInputStream(aFile)) { + return FileUtils.readAll(stream, MAX_FILE_SIZE_BYTES); + } + } + +} From 4f956000c59bebcf5f99f547e137ddb521433365 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Mar 2016 01:05:23 +0100 Subject: [PATCH 125/274] Enhanced support for firmware detection, recognition and upgrade #234 Also supports double firmware upgrade for Mi1S. - so far, only hr firmware upgrade is tested for 1S - adds junit testcases for firmware recognition and handling --- .../devices/miband/MiBandFWHelper.java | 103 ++++--------- .../gadgetbridge/devices/miband/UserInfo.java | 2 +- .../miband/AbstractMi1FirmwareInfo.java | 74 ++++++++++ .../miband/AbstractMi1SFirmwareInfo.java | 18 +++ .../miband/AbstractMiFirmwareInfo.java | 94 ++++++++++++ .../service/devices/miband/DeviceInfo.java | 7 +- .../devices/miband/Mi1AFirmwareInfo.java | 37 +++++ .../devices/miband/Mi1FirmwareInfo.java | 37 +++++ .../devices/miband/Mi1SFirmwareInfo.java | 69 +++++++++ .../devices/miband/Mi1SFirmwareInfoFW1.java | 53 +++++++ .../devices/miband/Mi1SFirmwareInfoFW2.java | 51 +++++++ .../service/devices/miband/Mi1SInfo.java | 68 --------- .../operations/UpdateFirmwareOperation.java | 139 ++++++------------ .../service/devices/miband/FirmwareTest.java | 78 +++++++--- 14 files changed, 573 insertions(+), 257 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index ef5b7fbd..e490e855 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -3,6 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; +import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,27 +11,23 @@ import org.slf4j.LoggerFactory; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Locale; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; /** - * Also see Mi1SInfo. + * Also see Mi1SFirmwareInfo. */ public class MiBandFWHelper { private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class); - private static final int MI_FW_BASE_OFFSET = 1056; - private static final int MI1S_FW_BASE_OFFSET = 1092; private final Uri uri; private final ContentResolver cr; - private byte[] fw; - - private int baseOffset = -1; + private final @NonNull AbstractMiFirmwareInfo firmwareInfo; + private final @NonNull byte[] fw; /** * Provides a different notification API which is also used on Mi1A devices. @@ -56,7 +53,6 @@ public class MiBandFWHelper { throw new IOException("No content resolver"); } - baseOffset = determineBaseOffset(uri); String pebblePattern = ".*\\.(pbw|pbz|pbl)"; if (uri.getPath().matches(pebblePattern)) { @@ -65,62 +61,23 @@ public class MiBandFWHelper { try (InputStream in = new BufferedInputStream(cr.openInputStream(uri))) { this.fw = FileUtils.readAll(in, 1024 * 1024); // 1 MB - if (fw.length <= getOffsetFirmwareVersionMajor()) { - throw new IOException("This doesn't seem to be a Mi Band firmware, file size too small."); - } - byte firmwareVersionMajor = fw[getOffsetFirmwareVersionMajor()]; - if (!isSupportedFirmwareVersionMajor(firmwareVersionMajor)) { - throw new IOException("Firmware major version not supported, either too new or this isn't a Mi Band firmware: " + firmwareVersionMajor); - } + this.firmwareInfo = determineFirmwareInfoFor(fw); } catch (IOException ex) { throw ex; // pass through + } catch (IllegalArgumentException ex) { + throw new IOException("This doesn't seem to be a Mi Band firmware: " + ex.getLocalizedMessage(), ex); } catch (Exception e) { throw new IOException("Error reading firmware file: " + uri.toString(), e); } } - private int getOffsetFirmwareVersionMajor() { - return baseOffset + 3; - } - - private int getOffsetFirmwareVersionMinor() { - return baseOffset + 2; - } - - private int getOffsetFirmwareVersionRevision() { - return baseOffset + 1; - } - - private int getOffsetFirmwareVersionBuild() { - return baseOffset; - } - - private int determineBaseOffset(Uri uri) throws IOException { - String name = uri.getLastPathSegment().toLowerCase(); - if (name.startsWith("mili")) { - if (name.contains("_hr")) { - return MI1S_FW_BASE_OFFSET; - } - return MI_FW_BASE_OFFSET; - } else { - throw new IOException("Unknown file name " + name + "; cannot recognize firmware by it."); - } - } - - private byte getFirmwareVersionMajor() { - return fw[getOffsetFirmwareVersionMajor()]; - } - - private byte getFirmwareVersionMinor() { - return fw[getOffsetFirmwareVersionMinor()]; - } - - private boolean isSupportedFirmwareVersionMajor(byte firmwareVersionMajor) { - return firmwareVersionMajor == 1 || firmwareVersionMajor == 4 || firmwareVersionMajor == 5; - } - public int getFirmwareVersion() { - return (fw[getOffsetFirmwareVersionMajor()] << 24) | (fw[getOffsetFirmwareVersionMinor()] << 16) | (fw[getOffsetFirmwareVersionRevision()] << 8) | fw[getOffsetFirmwareVersionBuild()]; + // FIXME: UnsupportedOperationException! + return firmwareInfo.getFirst().getFirmwareVersion(); + } + + public int getFirmware2Version() { + return firmwareInfo.getFirst().getFirmwareVersion(); } public static String formatFirmwareVersion(int version) { @@ -135,11 +92,11 @@ public class MiBandFWHelper { } public String getHumanFirmwareVersion() { - return String.format(Locale.US, "%d.%d.%d.%d", fw[getOffsetFirmwareVersionMajor()], fw[getOffsetFirmwareVersionMinor()], fw[getOffsetFirmwareVersionRevision()], fw[getOffsetFirmwareVersionBuild()]); + return format(getFirmwareVersion()); } public String getHumanFirmwareVersion2() { - return format(Mi1SInfo.getFirmware2VersionFrom(getFw())); + return format(firmwareInfo.getSecond().getFirmwareVersion()); } public String format(int version) { @@ -160,20 +117,24 @@ public class MiBandFWHelper { } public boolean isFirmwareGenerallyCompatibleWith(GBDevice device) { - String deviceHW = device.getHardwareVersion(); - if (MiBandConst.MI_1.equals(deviceHW)) { - return getFirmwareVersionMajor() == 1; - } - if (MiBandConst.MI_1A.equals(deviceHW)) { - return getFirmwareVersionMajor() == 5; - } - if (MiBandConst.MI_1S.equals(deviceHW)) { - return getFirmwareVersionMajor() == 4; - } - return false; + return firmwareInfo.isGenerallyCompatibleWith(device); } public boolean isSingleFirmware() { - return Mi1SInfo.isSingleMiBandFirmware(getFw()); + return firmwareInfo.isSingleMiBandFirmware(); + } + + /** + * + * @param wholeFirmwareBytes + * @return + * @throws IllegalArgumentException when the data is not recognized as firmware data + */ + public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { + return AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFirmwareBytes); + } + + public AbstractMiFirmwareInfo getFirmwareInfo() { + return firmwareInfo; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java index e17f5c2e..5bb901b3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java @@ -85,7 +85,7 @@ public class UserInfo { sequence[8] = (byte) (type & 0xff); int aliasFrom = 9; - if (mDeviceInfo.isMili1A() || mDeviceInfo.isMilli1S()) { + if (mDeviceInfo.isMili1A() || mDeviceInfo.isMili1S()) { sequence[9] = (byte) (mDeviceInfo.feature & 255); sequence[10] = (byte) (mDeviceInfo.appearance & 255); aliasFrom = 11; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java new file mode 100644 index 00000000..67247583 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -0,0 +1,74 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +/** + * Some helper methods for Mi1 and Mi1A firmware. + */ +public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class); + + private static final int MI1_FW_BASE_OFFSET = 1056; + + protected AbstractMi1FirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + public int getFirmwareOffset() + { + return 0; + } + + public int getFirmwareLength() + { + return wholeFirmwareBytes.length; + } + + public int getFirmwareVersion() + { + return (wholeFirmwareBytes[getOffsetFirmwareVersionMajor()] << 24) + | (wholeFirmwareBytes[getOffsetFirmwareVersionMinor()] << 16) + | (wholeFirmwareBytes[getOffsetFirmwareVersionRevision()] << 8) + | wholeFirmwareBytes[getOffsetFirmwareVersionBuild()]; + } + + private int getOffsetFirmwareVersionMajor() { + return MI1_FW_BASE_OFFSET + 3; + } + + private int getOffsetFirmwareVersionMinor() { + return MI1_FW_BASE_OFFSET + 2; + } + + private int getOffsetFirmwareVersionRevision() { + return MI1_FW_BASE_OFFSET + 1; + } + + private int getOffsetFirmwareVersionBuild() { + return MI1_FW_BASE_OFFSET; + } + + @Override + protected boolean isGenerallySupportedFirmware() { + if (!isSingleMiBandFirmware()) { + return false; + } + try { + int majorVersion = getFirmwareVersionMajor(); + return majorVersion == getSupportedMajorVersion(); + } catch (IllegalArgumentException e) { + return false; + } catch (IndexOutOfBoundsException ex) { + return false; + } + } + + protected abstract int getSupportedMajorVersion(); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java new file mode 100644 index 00000000..e22bf9db --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java @@ -0,0 +1,18 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public abstract class AbstractMi1SFirmwareInfo extends AbstractMiFirmwareInfo { + + public AbstractMi1SFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return MiBandConst.MI_1S.equals(device.getHardwareVersion()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java new file mode 100644 index 00000000..4f988517 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -0,0 +1,94 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +import java.util.Arrays; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public abstract class AbstractMiFirmwareInfo { + /** + * + * @param wholeFirmwareBytes + * @return + * @throws IllegalArgumentException when the data is not recognized as firmware data + */ + public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { + AbstractMiFirmwareInfo[] candidates = getFirmwareInfoCandidatesFor(wholeFirmwareBytes); + if (candidates.length == 0) { + throw new IllegalArgumentException("Unsupported data (maybe not even a firmware?)."); + } + if (candidates.length == 1) { + return candidates[0]; + } + throw new IllegalArgumentException("don't know for which device the firmware is, matches multiple devices"); + } + + private static AbstractMiFirmwareInfo[] getFirmwareInfoCandidatesFor(byte[] wholeFirmwareBytes) { + AbstractMiFirmwareInfo[] candidates = new AbstractMiFirmwareInfo[3]; + int i = 0; + Mi1FirmwareInfo mi1Info = Mi1FirmwareInfo.getInstance(wholeFirmwareBytes); + if (mi1Info != null) { + candidates[i++] = mi1Info; + } + Mi1AFirmwareInfo mi1aInfo = Mi1AFirmwareInfo.getInstance(wholeFirmwareBytes); + if (mi1aInfo != null) { + candidates[i++] = mi1aInfo; + } + Mi1SFirmwareInfo mi1sInfo = Mi1SFirmwareInfo.getInstance(wholeFirmwareBytes); + if (mi1sInfo != null) { + candidates[i++] = mi1sInfo; + } + return Arrays.copyOfRange(candidates, 0, i); + } + + @NonNull + protected byte[] wholeFirmwareBytes; + + public AbstractMiFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { + this.wholeFirmwareBytes = wholeFirmwareBytes; + } + + public abstract int getFirmwareOffset(); + + public abstract int getFirmwareLength(); + + public abstract int getFirmwareVersion(); + + protected abstract boolean isGenerallySupportedFirmware(); + + public abstract boolean isGenerallyCompatibleWith(GBDevice device); + + public @NonNull byte[] getFirmwareBytes() { + return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareLength()); + } + + public int getFirmwareVersionMajor() { + int version = getFirmwareVersion(); + if (version > 0) { + return (version >> 24); + } + throw new IllegalArgumentException("bad firmware version: " + version); + } + + public boolean isSingleMiBandFirmware() { + // TODO: not sure if this is a correct check! + if ((wholeFirmwareBytes[7] & 255) != 1) { + return true; + } + return false; + } + + public AbstractMiFirmwareInfo getFirst() { + if (isSingleMiBandFirmware()) { + return this; + } + throw new UnsupportedOperationException(getClass().getName() + " must override getFirst() and getSecond()"); + } + public AbstractMiFirmwareInfo getSecond() { + if (isSingleMiBandFirmware()) { + throw new UnsupportedOperationException(getClass().getName() + " only supports on firmware"); + } + throw new UnsupportedOperationException(getClass().getName() + " must override getFirst() and getSecond()"); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 74450139..2379fe8e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -1,7 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; public class DeviceInfo extends AbstractInfo { @@ -76,7 +75,7 @@ public class DeviceInfo extends AbstractInfo { } public boolean supportsHeartrate() { - return isMilli1S(); + return isMili1S(); } @Override @@ -100,7 +99,7 @@ public class DeviceInfo extends AbstractInfo { return feature == 5 && appearance == 0 || feature == 0 && hwVersion == 208; } - public boolean isMilli1S() { + public boolean isMili1S() { // TODO: this is probably not quite correct, but hopefully sufficient for early 1S support return feature == 4 && appearance == 0 || feature == 4 && hwVersion == 4; } @@ -112,7 +111,7 @@ public class DeviceInfo extends AbstractInfo { if (isMili1A()) { return MiBandConst.MI_1A; } - if (isMilli1S()) { + if (isMili1S()) { return MiBandConst.MI_1S; } return "?"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java new file mode 100644 index 00000000..ce975245 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java @@ -0,0 +1,37 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class Mi1AFirmwareInfo extends AbstractMi1FirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(Mi1AFirmwareInfo.class); + + public static Mi1AFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + Mi1AFirmwareInfo info = new Mi1AFirmwareInfo(wholeFirmwareBytes); + if (info.isGenerallySupportedFirmware()) { + return info; + } + LOG.info("firmware not supported"); + return null; + } + + protected Mi1AFirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + protected int getSupportedMajorVersion() { + return 5; + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + String hwVersion = device.getHardwareVersion(); + return MiBandConst.MI_1A.equals(hwVersion); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java new file mode 100644 index 00000000..aa506781 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java @@ -0,0 +1,37 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class Mi1FirmwareInfo extends AbstractMi1FirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(Mi1FirmwareInfo.class); + + public static Mi1FirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + Mi1FirmwareInfo info = new Mi1FirmwareInfo(wholeFirmwareBytes); + if (info.isGenerallySupportedFirmware()) { + return info; + } + LOG.info("firmware not supported"); + return null; + } + + protected Mi1FirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + protected int getSupportedMajorVersion() { + return 1; + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + String hwVersion = device.getHardwareVersion(); + return MiBandConst.MI_1.equals(hwVersion); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java new file mode 100644 index 00000000..1447559e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -0,0 +1,69 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * FW1 is Mi Band firmware + * FW2 is heartrate firmware + */ +public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class); + + private final Mi1SFirmwareInfoFW1 fw1Info; + private final Mi1SFirmwareInfoFW2 fw2Info; + + private Mi1SFirmwareInfo(byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + fw1Info = new Mi1SFirmwareInfoFW1(wholeFirmwareBytes); + fw2Info = new Mi1SFirmwareInfoFW2(wholeFirmwareBytes); + } + + @Override + public AbstractMiFirmwareInfo getFirst() { + return fw1Info; + } + + @Override + public AbstractMiFirmwareInfo getSecond() { + return fw2Info; + } + + public static @Nullable Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + Mi1SFirmwareInfo info = new Mi1SFirmwareInfo(wholeFirmwareBytes); + if (info.isGenerallySupportedFirmware()) { + return info; + } + LOG.info("firmware not supported"); + return null; + } + + @Override + protected boolean isGenerallySupportedFirmware() { + if (isSingleMiBandFirmware()) { + return false; + } + try { + return fw1Info.isGenerallySupportedFirmware() && fw2Info.isGenerallySupportedFirmware(); + } catch (IndexOutOfBoundsException ex) { + return false; + } + } + + @Override + public int getFirmwareOffset() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } + + @Override + public int getFirmwareLength() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } + + @Override + public int getFirmwareVersion() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java new file mode 100644 index 00000000..3270f0d8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java @@ -0,0 +1,53 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +/** + * FW1 is Mi Band firmware + * FW2 is heartrate firmware + */ +public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { + + private static final int MI1S_FW_BASE_OFFSET = 1092; + + Mi1SFirmwareInfoFW1(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + public int getFirmwareOffset() + { + return (wholeFirmwareBytes[12] & 255) << 24 + | (wholeFirmwareBytes[13] & 255) << 16 + | (wholeFirmwareBytes[14] & 255) << 8 + | (wholeFirmwareBytes[15] & 255); + } + + @Override + public int getFirmwareLength() + { + return (wholeFirmwareBytes[16] & 255) << 24 + | (wholeFirmwareBytes[17] & 255) << 16 + | (wholeFirmwareBytes[18] & 255) << 8 + | (wholeFirmwareBytes[19] & 255); + } + + @Override + public int getFirmwareVersion() + { + return (wholeFirmwareBytes[8] & 255) << 24 + | (wholeFirmwareBytes[9] & 255) << 16 + | (wholeFirmwareBytes[10] & 255) << 8 + | wholeFirmwareBytes[11] & 255; + } + + @Override + protected boolean isGenerallySupportedFirmware() { + try { + int majorVersion = getFirmwareVersionMajor(); + return majorVersion == 4; + } catch (IllegalArgumentException e) { + return false; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java new file mode 100644 index 00000000..86d4d61e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java @@ -0,0 +1,51 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.NonNull; + +/** + * FW1 is Mi Band firmware + * FW2 is heartrate firmware + */ +public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { + + Mi1SFirmwareInfoFW2(@NonNull byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes); + } + + @Override + public int getFirmwareOffset() + { + return (wholeFirmwareBytes[26] & 255) << 24 + | (wholeFirmwareBytes[27] & 255) << 16 + | (wholeFirmwareBytes[28] & 255) << 8 + | (wholeFirmwareBytes[29] & 255); + } + + @Override + public int getFirmwareLength() + { + return (wholeFirmwareBytes[30] & 255) << 24 + | (wholeFirmwareBytes[31] & 255) << 16 + | (wholeFirmwareBytes[32] & 255) << 8 + | (wholeFirmwareBytes[33] & 255); + } + + @Override + protected boolean isGenerallySupportedFirmware() { + try { + int majorVersion = getFirmwareVersionMajor(); + return majorVersion == 1; + } catch (IllegalArgumentException e) { + return false; + } + } + + @Override + public int getFirmwareVersion() + { + return (wholeFirmwareBytes[22] & 255) << 24 + | (wholeFirmwareBytes[23] & 255) << 16 + | (wholeFirmwareBytes[24] & 255) << 8 + | wholeFirmwareBytes[25] & 255; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java deleted file mode 100644 index 7a1448e1..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SInfo.java +++ /dev/null @@ -1,68 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; - -import android.support.annotation.NonNull; - -/** - * FW1 is Mi Band firmware - * FW2 is heartrate firmware - */ -public class Mi1SInfo { - - public static int getFirmware2OffsetIn(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[26] & 255) << 24 - | (wholeFirmwareBytes[27] & 255) << 16 - | (wholeFirmwareBytes[28] & 255) << 8 - | (wholeFirmwareBytes[29] & 255); - } - - public static int getFirmware2LengthIn(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[30] & 255) << 24 - | (wholeFirmwareBytes[31] & 255) << 16 - | (wholeFirmwareBytes[32] & 255) << 8 - | (wholeFirmwareBytes[33] & 255); - } - - public static int getFirmware1OffsetIn(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[12] & 255) << 24 - | (wholeFirmwareBytes[13] & 255) << 16 - | (wholeFirmwareBytes[14] & 255) << 8 - | (wholeFirmwareBytes[15] & 255); - } - - public static int getFirmware1LengthIn(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[16] & 255) << 24 - | (wholeFirmwareBytes[17] & 255) << 16 - | (wholeFirmwareBytes[18] & 255) << 8 - | (wholeFirmwareBytes[19] & 255); - } - - public static int getFirmware1VersionFrom(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[8] & 255) << 24 - | (wholeFirmwareBytes[9] & 255) << 16 - | (wholeFirmwareBytes[10] & 255) << 8 - | wholeFirmwareBytes[11] & 255; - } - - public static int getFirmware2VersionFrom(@NonNull byte[] wholeFirmwareBytes) - { - return (wholeFirmwareBytes[22] & 255) << 24 - | (wholeFirmwareBytes[23] & 255) << 16 - | (wholeFirmwareBytes[24] & 255) << 8 - | wholeFirmwareBytes[25] & 255; - } - - // FIXME: this method is wrong. We don't know a way to check if a firmware file - // contains one or more firmwares. - public static boolean isSingleMiBandFirmware(@NonNull byte[] wholeFirmwareBytes) { - if ((wholeFirmwareBytes[7] & 255) != 1) { - return true; - } - return false; - } - -} 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 68da0b40..9f98c896 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 @@ -18,7 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.Mi1SInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -28,8 +28,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { private final Uri uri; private boolean firmwareInfoSent = false; -// private byte[] newFirmware; -// private boolean rebootWhenBandReady = false; private UpdateCoordinator updateCoordinator; public UpdateFirmwareOperation(Uri uri, MiBandSupport support) { @@ -41,11 +39,16 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { protected void doPerform() throws IOException { MiBandFWHelper mFwHelper = new MiBandFWHelper(uri, getContext()); -// if (getSupport().supportsHeartRate()) { - updateCoordinator = prepareFirmwareInfo1S(mFwHelper.getFw()); -// } else { -// updateCoordinator = sendFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion()); -// } + AbstractMiFirmwareInfo firmwareInfo = mFwHelper.getFirmwareInfo(); + if (!firmwareInfo.isGenerallyCompatibleWith(getDevice())) { + throw new IOException("Firmware is not compatible with the given device: " + getDevice().getAddress()); + } + + if (getSupport().supportsHeartRate()) { + updateCoordinator = prepareFirmwareInfo1S(firmwareInfo); + } else { + updateCoordinator = prepareFirmwareInfo(mFwHelper.getFw(), mFwHelper.getFirmwareVersion()); + } updateCoordinator.initNextOperation(); // updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band 1s fw update @@ -75,7 +78,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } /** - * React to unsolicited messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION + * React to messages sent by the Mi Band to the MiBandService.UUID_CHARACTERISTIC_NOTIFICATION * characteristic, * These messages appear to be always 1 byte long, with values that are listed in MiBandService. * It is not excluded that there are further values which are still unknown. @@ -97,25 +100,19 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { switch (value[0]) { case MiBandService.NOTIFY_FW_CHECK_SUCCESS: -// if (firmwareInfoSent && newFirmware != null) { if (firmwareInfoSent) { GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO); - if (updateCoordinator.sendFwData()) { -// if (sendFirmwareData(newFirmware)) { -// rebootWhenBandReady = true; // disabled for testing - } else { + if (!updateCoordinator.sendFwData()) { //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); } firmwareInfoSent = false; -// newFirmware = null; } break; case MiBandService.NOTIFY_FW_CHECK_FAILED: GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR); firmwareInfoSent = false; -// newFirmware = null; done(); break; case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS: @@ -130,7 +127,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext()); getSupport().onReboot(); -// rebootWhenBandReady = false; } else { LOG.error("BUG: Successful firmware update without reboot???"); } @@ -140,7 +136,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); -// rebootWhenBandReady = false; done(); break; @@ -150,63 +145,41 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } } -// /** -// * Prepare the MiBand to receive the new firmware data. -// * Some information about the new firmware version have to be pushed to the MiBand before sending -// * the actual firmare. -// *

-// * The Mi Band will send a notification after receiving these data to confirm if the metadata looks good to it. -// * -// * @param fwBytes -// * @param newFwVersion -// * @see MiBandSupport#handleNotificationNotif -// */ -// private byte[] sendFirmwareInfo(int currentFwVersion, int newFwVersion, int newFwSize, int checksum) throws IOException { -// private UpdateCoordinator sendFirmwareInfo(byte[] fwBytes, int newFwVersion) throws IOException { -// int newFwSize = fwBytes.length; -// String mMac = getDevice().getAddress(); -// String[] mMacOctets = mMac.split(":"); -// int currentFwVersion = getSupport().getDeviceInfo().getFirmwareVersion(); -// int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(fwBytes); -// -// byte[] fwInfo = new byte[]{ -// MiBandService.COMMAND_SEND_FIRMWARE_INFO, -// (byte) currentFwVersion, -// (byte) (currentFwVersion >> 8), -// (byte) (currentFwVersion >> 16), -// (byte) (currentFwVersion >> 24), -// (byte) newFwVersion, -// (byte) (newFwVersion >> 8), -// (byte) (newFwVersion >> 16), -// (byte) (newFwVersion >> 24), -// (byte) newFwSize, -// (byte) (newFwSize >> 8), -// (byte) checksum, -// (byte) (checksum >> 8) -//// (byte) (checksum >> 8), -//// (byte) 0 // TEST, only for Mi1S! -// }; -// return new SingleUpdateCoordinator(fwInfo, fwBytes); -// } + /** + * Prepare the MiBand to receive the new firmware data. + * Some information about the new firmware version have to be pushed to the MiBand before sending + * the actual firmare. + *

+ * 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; + String mMac = getDevice().getAddress(); + String[] mMacOctets = mMac.split(":"); + int currentFwVersion = getSupport().getDeviceInfo().getFirmwareVersion(); + int checksum = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])) ^ CheckSums.getCRC16(fwBytes); - private UpdateCoordinator prepareFirmwareInfo1S(byte[] wholeFirmwareBytes) { - int fw2Version = Mi1SInfo.getFirmware2VersionFrom(wholeFirmwareBytes); - int fw2Offset = Mi1SInfo.getFirmware2OffsetIn(wholeFirmwareBytes); - int fw2Length = Mi1SInfo.getFirmware2LengthIn(wholeFirmwareBytes); + byte[] fwInfo = prepareFirmwareUpdateA(currentFwVersion, newFwVersion, newFwSize, checksum); + return new SingleUpdateCoordinator(fwInfo, fwBytes, true); + } - int fw1Version = Mi1SInfo.getFirmware1VersionFrom(wholeFirmwareBytes); - int fw1Offset = Mi1SInfo.getFirmware1OffsetIn(wholeFirmwareBytes); - int fw1Length = Mi1SInfo.getFirmware1LengthIn(wholeFirmwareBytes); + private UpdateCoordinator prepareFirmwareInfo1S(AbstractMiFirmwareInfo info) { + if (info.isSingleMiBandFirmware()) { + throw new IllegalArgumentException("preparing single fw not allowed for 1S"); + } + int fw2Version = info.getSecond().getFirmwareVersion(); + int fw1Version = info.getFirst().getFirmwareVersion(); String[] mMacOctets = getDevice().getAddress().split(":"); int encodedMac = (Integer.decode("0x" + mMacOctets[4]) << 8 | Integer.decode("0x" + mMacOctets[5])); - byte[] fw2Bytes = new byte[fw2Length]; - System.arraycopy(wholeFirmwareBytes, fw2Offset, fw2Bytes, 0, fw2Length); + byte[] fw2Bytes = info.getSecond().getFirmwareBytes(); int fw2Checksum = CheckSums.getCRC16(fw2Bytes) ^ encodedMac; - byte[] fw1Bytes = new byte[fw1Length]; - System.arraycopy(wholeFirmwareBytes, fw1Offset, fw1Bytes, 0, fw1Length); + byte[] fw1Bytes = info.getFirst().getFirmwareBytes(); int fw1Checksum = encodedMac ^ CheckSums.getCRC16(fw1Bytes); // check firmware validity? @@ -215,22 +188,18 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { int fw2OldVersion = getSupport().getDeviceInfo().getHeartrateFirmwareVersion(); boolean rebootWhenFinished = true; - if (Mi1SInfo.isSingleMiBandFirmware(wholeFirmwareBytes)) { + if (info.isSingleMiBandFirmware()) { LOG.info("is single Mi Band firmware"); byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 0, rebootWhenFinished /*, progress monitor */); return new SingleUpdateCoordinator(fw1Info, fw1Bytes, rebootWhenFinished); } else { - LOG.info("is multi Mi Band firmware, sending fw2 (hr) now"); + LOG.info("is multi Mi Band firmware, sending fw2 (hr) first"); byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */); byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 1, rebootWhenFinished /*, progress monitor */); return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished); } } -// private Transaction createUpdateFirmwareTransaction() { -// -// } - private byte[] prepareFirmwareInfo(byte[] fwBytes, int currentFwVersion, int newFwVersion, int checksum, int something, boolean reboot) { byte[] fwInfo; switch (something) { @@ -262,12 +231,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { (byte) checksum, (byte) (checksum >> 8) }; -// byte[] fwInfo = new byte[]{ -// MiBandService.COMMAND_SEND_FIRMWARE_INFO -// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24), -// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24), -// (byte) newFwSize, (byte) (newFwSize >> 8), -// (byte) checksum, (byte) (checksum >> 8)})) { return fwInfo; } @@ -286,21 +249,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { (byte) (newFwSize >> 8), (byte) checksum, (byte) (checksum >> 8), - (byte) something // 0 TEST, only for Mi1S! + (byte) something }; - -// // send to CONTROL POINT: -// if (!this.b(CONTROL_POINT, new byte[]{7, -// (byte) currentFwVersion, (byte) (currentFwVersion >> 8), (byte) (currentFwVersion >> 16), (byte) (currentFwVersion >> 24), -// (byte) newFwVersion, (byte) (newFwVersion >> 8), (byte) (newFwVersion >> 16), (byte) (newFwVersion >> 24), -// (byte) newFwSize, (byte) (newFwSize >> 8), -// (byte) checksum, (byte) (checksum >> 8), (byte) something})) { -// return false; -// } -// // wait for bq != -1 -// if (bq == 12) { -// return true; -// } return fwInfo; } @@ -388,6 +338,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } public boolean sendFwData() { +// if (true) { +// return true; // FIXME: temporarily disabled firmware sending +// } return sendFirmwareData(getFirmwareBytes()); } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index b842ec1e..aeee2b3b 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -6,6 +6,7 @@ import org.junit.Test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -18,19 +19,19 @@ public class FirmwareTest { private static final int MI1S_FW1_VERSION = 0; private static final int MI1S_FW2_VERSION = 0; + private static final int SINGLE = 1; + private static final int DOUBLE = 2; + @Test - public void testFirmwareMi() throws Exception{ + public void testFirmwareMi1() throws Exception{ byte[] wholeFw = getFirmwareMi(); Assert.assertNotNull(wholeFw); - Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); - int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw); - Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length); - int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw); -// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion); - + AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE); + int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("1.")); +// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion); } @Test @@ -38,14 +39,11 @@ public class FirmwareTest { byte[] wholeFw = getFirmwareMi1A(); Assert.assertNotNull(wholeFw); - Assert.assertTrue(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); - int calculatedLength = Mi1SInfo.getFirmware1LengthIn(wholeFw); - Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength < wholeFw.length); - int calculatedVersion = Mi1SInfo.getFirmware1VersionFrom(wholeFw); -// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion); - + AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE); + int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("5.")); +// Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion); } @Test @@ -53,31 +51,71 @@ public class FirmwareTest { byte[] wholeFw = getFirmwareMi1S(); Assert.assertNotNull(wholeFw); - Assert.assertFalse(Mi1SInfo.isSingleMiBandFirmware(wholeFw)); + AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, DOUBLE); // Mi Band version - int calculatedLengthFw1 = Mi1SInfo.getFirmware1LengthIn(wholeFw); - int calculatedOffsetFw1 = Mi1SInfo.getFirmware1OffsetIn(wholeFw); + int calculatedLengthFw1 = info.getFirst().getFirmwareLength(); + int calculatedOffsetFw1 = info.getFirst().getFirmwareOffset(); int endIndexFw1 = calculatedOffsetFw1 + calculatedLengthFw1; - int calculatedLengthFw2 = Mi1SInfo.getFirmware2LengthIn(wholeFw); - int calculatedOffsetFw2 = Mi1SInfo.getFirmware2OffsetIn(wholeFw); + int calculatedLengthFw2 = info.getSecond().getFirmwareLength(); + int calculatedOffsetFw2 = info.getSecond().getFirmwareOffset(); int endIndexFw2 = calculatedOffsetFw2 + calculatedLengthFw2; Assert.assertTrue(endIndexFw1 <= wholeFw.length - calculatedLengthFw2); Assert.assertTrue(endIndexFw2 <= wholeFw.length); Assert.assertTrue(endIndexFw1 <= calculatedOffsetFw2); - int calculatedVersionFw1 = Mi1SInfo.getFirmware1VersionFrom(wholeFw); + int calculatedVersionFw1 = info.getFirst().getFirmwareVersion(); // Assert.assertEquals("Unexpected firmware 1 version: " + calculatedVersionFw1, MI1S_FW1_VERSION, calculatedVersionFw1); String version1 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw1); Assert.assertTrue(version1.startsWith("4.")); // HR version - int calculatedVersionFw2 = Mi1SInfo.getFirmware2VersionFrom(wholeFw); + int calculatedVersionFw2 = info.getSecond().getFirmwareVersion(); // Assert.assertEquals("Unexpected firmware 2 version: " + calculatedVersionFw2, MI1S_FW2_VERSION, calculatedVersionFw2); String version2 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw2); Assert.assertTrue(version2.startsWith("1.")); + + try { + info.getFirmwareVersion(); + Assert.fail("should not get fw version from AbstractMi1SFirmwareInfo"); + } catch (UnsupportedOperationException expected) {} + + Assert.assertNotEquals(info.getFirst().getFirmwareOffset(), info.getSecond().getFirmwareOffset()); + Assert.assertFalse(Arrays.equals(info.getFirst().getFirmwareBytes(), info.getSecond().getFirmwareBytes())); + } + + private AbstractMiFirmwareInfo getFirmwareInfo(byte[] wholeFw, int numFirmwares) { + AbstractMiFirmwareInfo info = AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFw); + switch (numFirmwares) { + case SINGLE: { + Assert.assertTrue("should be single miband firmware", info.isSingleMiBandFirmware()); + Assert.assertSame(info, info.getFirst()); + try { + info.getSecond(); + Assert.fail("should throw UnsuportedOperationException"); + } catch (UnsupportedOperationException expected) {} + int calculatedLength = info.getFirmwareLength(); + Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length); + break; + } + case DOUBLE: { + Assert.assertFalse("should not be single miband firmware", info.isSingleMiBandFirmware()); + Assert.assertNotSame(info, info.getFirst()); + Assert.assertNotSame(info, info.getSecond()); + Assert.assertNotSame(info.getFirst(), info.getSecond()); + int calculatedLength = info.getFirst().getFirmwareLength(); + Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length); + calculatedLength = info.getSecond().getFirmwareLength(); + Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length); + break; + } + default: + Assert.fail("unexpected numFirmwares: " + numFirmwares); + } + Assert.assertTrue(info.isGenerallySupportedFirmware()); + return info; } private File getFirmwareDir() { From e59c0125537338bcf76c081d8faa9a6dfdc584f1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Mar 2016 01:16:20 +0100 Subject: [PATCH 126/274] Disable FirmwareTest for travis --- .../gadgetbridge/service/devices/miband/FirmwareTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index aeee2b3b..0c7471a3 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -11,6 +12,7 @@ import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +@Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") public class FirmwareTest { private static final long MAX_FILE_SIZE_BYTES = 1024 * 1024; // 1MB From b3410dcebedc1854f11f7134e098d5f940852684 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Mar 2016 12:18:43 +0100 Subject: [PATCH 127/274] Improved testcase #234 --- .../service/devices/miband/Mi1SFirmwareInfo.java | 8 +++++++- .../gadgetbridge/service/devices/miband/FirmwareTest.java | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index 1447559e..32193d33 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -46,9 +46,15 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { return false; } try { - return fw1Info.isGenerallySupportedFirmware() && fw2Info.isGenerallySupportedFirmware(); + return fw1Info.isGenerallySupportedFirmware() + && fw2Info.isGenerallySupportedFirmware() + && fw1Info.getFirmwareBytes().length > 0 + && fw2Info.getFirmwareBytes().length > 0; } catch (IndexOutOfBoundsException ex) { return false; + } catch (IllegalArgumentException ex) { + LOG.warn("not supported 1S firmware: ", ex); + return false; } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index 0c7471a3..732464f6 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -33,6 +33,7 @@ public class FirmwareTest { int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("1.")); + Assert.assertArrayEquals(wholeFw, info.getFirmwareBytes()); // Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI_FW_VERSION, calculatedVersion); } @@ -45,6 +46,7 @@ public class FirmwareTest { int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("5.")); + Assert.assertArrayEquals(wholeFw, info.getFirmwareBytes()); // Assert.assertEquals("Unexpected firmware version: " + calculatedVersion, MI1A_FW_VERSION, calculatedVersion); } From 4be192645954821c55e9f9ffc0f8af3e8f112b02 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 15:00:05 +0100 Subject: [PATCH 128/274] reformat code though Android Studio --- .../gadgetbridge/activities/AppManagerActivity.java | 2 +- .../activities/ExternalPebbleJSActivity.java | 1 + .../activities/charts/AbstractChartFragment.java | 1 - .../gadgetbridge/adapter/GBDeviceAdapter.java | 4 +--- .../adapter/ItemWithDetailsAdapter.java | 1 + .../database/schema/ActivityDBUpdate_7.java | 8 -------- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 13 +++++++++---- .../devices/miband/MiBandSampleProvider.java | 10 +++++----- .../gadgetbridge/model/ItemWithDetails.java | 1 + .../service/DeviceCommunicationService.java | 1 - .../devices/miband/AbstractMi1FirmwareInfo.java | 12 +++--------- .../devices/miband/AbstractMiFirmwareInfo.java | 10 +++++++--- .../service/devices/miband/DeviceInfo.java | 2 +- .../service/devices/miband/Mi1SFirmwareInfo.java | 4 +++- .../service/devices/miband/Mi1SFirmwareInfoFW1.java | 13 +++++-------- .../service/devices/miband/Mi1SFirmwareInfoFW2.java | 9 +++------ .../service/devices/miband/MiBandSupport.java | 1 - .../devices/pebble/AppMessageHandlerPebStyle.java | 1 - .../pebble/AppMessageHandlerTimeStylePebble.java | 1 - .../gadgetbridge/util/PebbleUtils.java | 1 + .../service/AbstractServiceTestCase.java | 2 +- .../gadgetbridge/service/TestDeviceSupport.java | 2 +- .../service/devices/miband/FirmwareTest.java | 12 +++++++----- .../gadgetbridge/test/GBMockApplication.java | 1 + .../gadgetbridge/test/GBMockIntent.java | 2 +- 25 files changed, 53 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index d7297120..e2a1d56d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -80,7 +80,7 @@ public class AppManagerActivity extends Activity { List systemApps = new ArrayList<>(); systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getFirmwareVersion()))) { + if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getFirmwareVersion()))) { systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index f6138fb6..caa6a290 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -100,6 +100,7 @@ public class ExternalPebbleJSActivity extends Activity { return super.onConsoleMessage(consoleMessage); } } + private class GBWebClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 61f6ad01..21f7774f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -16,7 +16,6 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; -import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.CombinedData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index b655a601..cd2decd6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -12,14 +12,12 @@ import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; -import java.text.Collator; import java.util.Collections; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; -import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; /** @@ -126,7 +124,7 @@ public class GBDeviceAdapter extends ArrayAdapter { return view; } - public void justifyListViewHeightBasedOnChildren (ListView listView) { + public void justifyListViewHeightBasedOnChildren(ListView listView) { ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter(); if (adapter == null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java index fed094ac..92448d9c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java @@ -30,6 +30,7 @@ public class ItemWithDetailsAdapter extends ArrayAdapter { public void setHorizontalAlignment(boolean horizontalAlignment) { this.horizontalAlignment = horizontalAlignment; } + @Override public View getView(int position, View view, ViewGroup parent) { ItemWithDetails item = getItem(position); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java index 84638f3c..c7077451 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/ActivityDBUpdate_7.java @@ -1,13 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.database.schema; -import android.database.sqlite.SQLiteDatabase; - -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.database.DBUpdateScript; - -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.KEY_CUSTOM_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GBACTIVITYSAMPLES; - /** * Bugfix for users who installed 0.8.1 cleanly, i.e. without any previous * database. Perform Update script 6 again. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index e490e855..3c4ba09b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -26,8 +26,12 @@ public class MiBandFWHelper { private final Uri uri; private final ContentResolver cr; - private final @NonNull AbstractMiFirmwareInfo firmwareInfo; - private final @NonNull byte[] fw; + private final + @NonNull + AbstractMiFirmwareInfo firmwareInfo; + private final + @NonNull + byte[] fw; /** * Provides a different notification API which is also used on Mi1A devices. @@ -125,12 +129,13 @@ public class MiBandFWHelper { } /** - * * @param wholeFirmwareBytes * @return * @throws IllegalArgumentException when the data is not recognized as firmware data */ - public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { + public static + @NonNull + AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { return AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFirmwareBytes); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index b9b622d3..b87406e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -5,11 +5,11 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public class MiBandSampleProvider implements SampleProvider { public static final int TYPE_DEEP_SLEEP = 5; - public static final int TYPE_LIGHT_SLEEP = 4; - public static final int TYPE_ACTIVITY = -1; - public static final int TYPE_UNKNOWN = -1; - public static final int TYPE_NONWEAR = 3; - public static final int TYPE_CHARGING = 6; + public static final int TYPE_LIGHT_SLEEP = 4; + public static final int TYPE_ACTIVITY = -1; + public static final int TYPE_UNKNOWN = -1; + public static final int TYPE_NONWEAR = 3; + public static final int TYPE_CHARGING = 6; // public static final byte TYPE_NREM = 5; // DEEP SLEEP // public static final byte TYPE_ONBED = 7; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java index 0c5dc497..078ced99 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java @@ -11,6 +11,7 @@ public interface ItemWithDetails extends Parcelable, Comparable /** * Equality is based on #getName() only. + * * @param other */ boolean equals(Object other); 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 0d495422..ad49e23d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -3,7 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.service; import android.app.NotificationManager; import android.app.Service; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java index 67247583..cf626593 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -5,9 +5,6 @@ import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; - /** * Some helper methods for Mi1 and Mi1A firmware. */ @@ -21,18 +18,15 @@ public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { } @Override - public int getFirmwareOffset() - { + public int getFirmwareOffset() { return 0; } - public int getFirmwareLength() - { + public int getFirmwareLength() { return wholeFirmwareBytes.length; } - public int getFirmwareVersion() - { + public int getFirmwareVersion() { return (wholeFirmwareBytes[getOffsetFirmwareVersionMajor()] << 24) | (wholeFirmwareBytes[getOffsetFirmwareVersionMinor()] << 16) | (wholeFirmwareBytes[getOffsetFirmwareVersionRevision()] << 8) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index 4f988517..e303e45f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -8,12 +8,13 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; public abstract class AbstractMiFirmwareInfo { /** - * * @param wholeFirmwareBytes * @return * @throws IllegalArgumentException when the data is not recognized as firmware data */ - public static @NonNull AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { + public static + @NonNull + AbstractMiFirmwareInfo determineFirmwareInfoFor(byte[] wholeFirmwareBytes) { AbstractMiFirmwareInfo[] candidates = getFirmwareInfoCandidatesFor(wholeFirmwareBytes); if (candidates.length == 0) { throw new IllegalArgumentException("Unsupported data (maybe not even a firmware?)."); @@ -59,7 +60,9 @@ public abstract class AbstractMiFirmwareInfo { public abstract boolean isGenerallyCompatibleWith(GBDevice device); - public @NonNull byte[] getFirmwareBytes() { + public + @NonNull + byte[] getFirmwareBytes() { return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareLength()); } @@ -85,6 +88,7 @@ public abstract class AbstractMiFirmwareInfo { } throw new UnsupportedOperationException(getClass().getName() + " must override getFirst() and getSecond()"); } + public AbstractMiFirmwareInfo getSecond() { if (isSingleMiBandFirmware()) { throw new UnsupportedOperationException(getClass().getName() + " only supports on firmware"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 2379fe8e..ddad3f24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -87,7 +87,7 @@ public class DeviceInfo extends AbstractInfo { ", hwVersion=" + hwVersion + ", feature=" + feature + ", appearance=" + appearance + - ", fw2Version (hr)=" + fw2Version+ + ", fw2Version (hr)=" + fw2Version + '}'; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index 32193d33..c9301b90 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -31,7 +31,9 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { return fw2Info; } - public static @Nullable Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + public static + @Nullable + Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { Mi1SFirmwareInfo info = new Mi1SFirmwareInfo(wholeFirmwareBytes); if (info.isGenerallySupportedFirmware()) { return info; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java index 3270f0d8..b8ea7aab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java @@ -15,8 +15,7 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareOffset() - { + public int getFirmwareOffset() { return (wholeFirmwareBytes[12] & 255) << 24 | (wholeFirmwareBytes[13] & 255) << 16 | (wholeFirmwareBytes[14] & 255) << 8 @@ -24,8 +23,7 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareLength() - { + public int getFirmwareLength() { return (wholeFirmwareBytes[16] & 255) << 24 | (wholeFirmwareBytes[17] & 255) << 16 | (wholeFirmwareBytes[18] & 255) << 8 @@ -33,15 +31,14 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareVersion() - { + public int getFirmwareVersion() { return (wholeFirmwareBytes[8] & 255) << 24 | (wholeFirmwareBytes[9] & 255) << 16 | (wholeFirmwareBytes[10] & 255) << 8 | wholeFirmwareBytes[11] & 255; } - @Override + @Override protected boolean isGenerallySupportedFirmware() { try { int majorVersion = getFirmwareVersionMajor(); @@ -49,5 +46,5 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { } catch (IllegalArgumentException e) { return false; } - } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java index 86d4d61e..9cd013f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java @@ -13,8 +13,7 @@ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareOffset() - { + public int getFirmwareOffset() { return (wholeFirmwareBytes[26] & 255) << 24 | (wholeFirmwareBytes[27] & 255) << 16 | (wholeFirmwareBytes[28] & 255) << 8 @@ -22,8 +21,7 @@ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareLength() - { + public int getFirmwareLength() { return (wholeFirmwareBytes[30] & 255) << 24 | (wholeFirmwareBytes[31] & 255) << 16 | (wholeFirmwareBytes[32] & 255) << 8 @@ -41,8 +39,7 @@ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { } @Override - public int getFirmwareVersion() - { + public int getFirmwareVersion() { return (wholeFirmwareBytes[22] & 255) << 24 | (wholeFirmwareBytes[23] & 255) << 16 | (wholeFirmwareBytes[24] & 255) << 8 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 be8b6182..8dab5a4c 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 @@ -30,7 +30,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; -import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index 8b90c5a6..781e56c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; public class AppMessageHandlerPebStyle extends AppMessageHandler { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index fb1583f7..2ab8ae2d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -11,7 +11,6 @@ import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; public class AppMessageHandlerTimeStylePebble extends AppMessageHandler { public static final int KEY_SETTING_SIDEBAR_LEFT = 9; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index 0e13e925..11ec9678 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -12,6 +12,7 @@ public class PebbleUtils { } return platformName; } + public static String getModel(String hwRev) { //TODO: get real data? String model; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java index c7ba649f..fe9e03e3 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java @@ -28,7 +28,7 @@ public abstract class AbstractServiceTestCase { private NotificationManager mNotificationManager; private MockHelper mMockHelper; - protected AbstractServiceTestCase(Class serviceClass) { + protected AbstractServiceTestCase(Class serviceClass) { mServiceClass = serviceClass; Assert.assertNotNull(serviceClass); } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 8722f00d..29043020 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -47,7 +47,7 @@ public class TestDeviceSupport extends AbstractDeviceSupport { @Override public void onNotification(NotificationSpec notificationSpec) { - + } @Override diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index 732464f6..8ceee215 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -25,7 +25,7 @@ public class FirmwareTest { private static final int DOUBLE = 2; @Test - public void testFirmwareMi1() throws Exception{ + public void testFirmwareMi1() throws Exception { byte[] wholeFw = getFirmwareMi(); Assert.assertNotNull(wholeFw); @@ -38,7 +38,7 @@ public class FirmwareTest { } @Test - public void testFirmwareMi1A() throws Exception{ + public void testFirmwareMi1A() throws Exception { byte[] wholeFw = getFirmwareMi1A(); Assert.assertNotNull(wholeFw); @@ -51,7 +51,7 @@ public class FirmwareTest { } @Test - public void testFirmwareMi1S() throws Exception{ + public void testFirmwareMi1S() throws Exception { byte[] wholeFw = getFirmwareMi1S(); Assert.assertNotNull(wholeFw); @@ -84,7 +84,8 @@ public class FirmwareTest { try { info.getFirmwareVersion(); Assert.fail("should not get fw version from AbstractMi1SFirmwareInfo"); - } catch (UnsupportedOperationException expected) {} + } catch (UnsupportedOperationException expected) { + } Assert.assertNotEquals(info.getFirst().getFirmwareOffset(), info.getSecond().getFirmwareOffset()); Assert.assertFalse(Arrays.equals(info.getFirst().getFirmwareBytes(), info.getSecond().getFirmwareBytes())); @@ -99,7 +100,8 @@ public class FirmwareTest { try { info.getSecond(); Assert.fail("should throw UnsuportedOperationException"); - } catch (UnsupportedOperationException expected) {} + } catch (UnsupportedOperationException expected) { + } int calculatedLength = info.getFirmwareLength(); Assert.assertTrue("Unexpected firmware length: " + wholeFw.length, calculatedLength <= wholeFw.length); break; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java index b9f16b12..805716fa 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java @@ -19,6 +19,7 @@ public class GBMockApplication extends MockApplication { public Context getApplicationContext() { return this; } + @Override public PackageManager getPackageManager() { return mPackageManager; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java index 667e4497..080361e3 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockIntent.java @@ -12,7 +12,7 @@ import java.util.Map; public class GBMockIntent extends Intent { private String mAction; - private final Map extras = new HashMap<>(); + private final Map extras = new HashMap<>(); @NonNull @Override From b5f71febdc004344256b936a9f204d371c2d4003 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 15:16:06 +0100 Subject: [PATCH 129/274] bump vestion to 0.9.0, update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ app/build.gradle | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 286ce91f..e6bc3ddf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ ###Changelog +####Version 0.9.0 (next) +* Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) +* Mi Band: Improve firmware detection and updates, including 1S support +* Mi Band: Display HR FW for 1S +* FW and HW versions are only displayed after tapping on the "info" button in Control Center +* Do not display activity samples when navigating too far in the past + ####Version 0.8.2 * Fix database creation and updates (thanks @feclare) * Add experimental widget to set the alarm time to a configurable number of hours in the future (thanks @0nse) diff --git a/app/build.gradle b/app/build.gradle index 191981db..961e8814 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.8.2" - versionCode 43 + versionName "0.9.0" + versionCode 44 } buildTypes { release { From 4fe94899095670f8f67b485cfb6b5ba3e7f46b54 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 15:34:07 +0100 Subject: [PATCH 130/274] update German translation --- app/src/main/res/values-de/strings.xml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 757aa96c..f9665cef 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -19,6 +19,7 @@ FW/App Installer Es soll die Firmware %s anstelle der aktuell installierten Version aufs Mi Band gespielt werden. + Es sollen die Firmwares %1$s und %2$s anstelle der aktuell installierten Versionen aufs Mi Band gespielt werden. Diese Firmware ist getestet worden und ist zu Gadgetbridge kompatibel. Diese Firmware ist nicht getestet und könnte inkompatibel mit Gadgetbridge sein.\n\nEs wird nicht empfohlen sie zu installieren! Falls die Firmware trotzdem installiert werden soll und nachher alles korrekt funktioniert, melde bitte den Gadgetbridge Entwicklern, dass die Firmware %s tauglich ist. @@ -45,9 +46,11 @@ niemals Sperre für Apps Vorgefertigte Antworten + Gemeinsame Endung Entwickleroptionen Mi Band MAC Adresse Pebble Einstellungen + Bevorzugter Aktivitätstracker Erlaube Zugriff von anderen Android Apps Experimentelle Unterstützung für Android Apps, die PebbleKit benutzen Benachrichtigungsprotokoll erzwingen @@ -102,12 +105,7 @@ Mach Dein Gerät auffindbar. Derzeit verbundene Geräte sind in der Regel nicht auffindbar. Tipp: Bild des Geräts - Über Dich Name/Alias - Geburtsjahr - Geschlecht - Größe in cm - Gewicht in kg Anzahl der Vibrationen Schlafmonitor Log-Dateien schreiben (Neustart erforderlich) @@ -200,6 +198,17 @@ Diese Firmware ist nicht mit dem Gerät kompatibel warte auf eingehende Verbindung Erneut installieren + Über Dich + Geburtsjahr + Geschlecht + Größe in cm + Gewicht in kg + aktivieren + deaktivieren + Zzz Widget hinzufügen - Bevorzugte Schlafdauer in Stunden + Gewünschte Schlafdauer in Stunden + Ein Wecker wurde auf %1$02d:%2$02d gestellt + HW: %1$s + FW: %1$s From c5a7ca4b5ba155d3b3ae3cfc3a6d270fc666c4f2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 15:38:05 +0100 Subject: [PATCH 131/274] properly re-sync all translation files with transifex (this only reorders stuff since I had to repush broken tranlations to transifex again) --- app/src/main/res/values-it/strings.xml | 10 +++++----- app/src/main/res/values-ko/strings.xml | 10 +++++----- app/src/main/res/values-pl/strings.xml | 10 +++++----- app/src/main/res/values-ru/strings.xml | 10 +++++----- app/src/main/res/values-vi/strings.xml | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 79f253d8..8bff7471 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -95,12 +95,7 @@ Imposta il tuo dispositivo perchè sia rilevabile. I dispositivi attualmente connessi non saranno probabilmente rilevati. Nota: Immagine dispositivo - Informazioni sull\'utilizzatore Nome / Soprannome - Anno di nascita - Genere - Altezza in cm - Peso in kg Numero vibrazioni Monitoraggio del sonno Salva il log su file (richiede riavvio) @@ -176,4 +171,9 @@ Passi di oggi, traguardo: %1$s Se il trasferimento non viene confermato, i dati rimangono memorizzati sulla Mi Band. Utile se GB è usato insieme ad altre app. Non confermare il trasferimento dati + Informazioni sull\'utilizzatore + Anno di nascita + Genere + Altezza in cm + Peso in kg diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 258105d3..407990cd 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -102,12 +102,7 @@ 기기를 발견 가능하도록 설정하세요. 현재 연결된 기기들은 발견될 수 없습니다. 알림: 기기 이미지 - 당신에 대해 이름/별명 - 출생년도 - 성별 - 키 (cm) - 몸무게 (kg) 진동 횟수 수면 측정계 기록 파일 작성 (재시작 필요) @@ -201,4 +196,9 @@ 다가오는 이벤트를 위해 예약할 알람 재접속을 기다리는 중 재설치 + 당신에 대해 + 출생년도 + 성별 + 키 (cm) + 몸무게 (kg) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9d21e263..1047a982 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -102,12 +102,7 @@ Uwidocznij swoje urządzenie. Aktualnie połączone urządzenia prawdopodobnie nie będą znalezione. Uwaga Obraz urządzenia - O tobie Nazwisko/Pseudonim - Data urodzenia - Płeć - Wzrost w cm - Waga w kg Liczba wibracji Monitor snu Zapisuj logi (wymaga restartu) @@ -200,4 +195,9 @@ Ten firmware nie jest kompatybilny z urządzeniem Alarmy zarezerwowane dla nadchodzących zdarzeń oczekiwanie na ponowne połaczenie + O tobie + Data urodzenia + Płeć + Wzrost w cm + Waga w kg diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6efeeb00..6b444aec 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -103,12 +103,7 @@ Подключённые в настоящее время устройства, скорее всего, не будут обнаружены. Заметка: Изображение устройства - Ваши данные Имя/псевдоним - Год рождения - Пол - Рост в см - Вес в кг Количество вибраций Анализ сна Записывать файлы журнала (нужен перезапуск) @@ -202,4 +197,9 @@ Резервные сигналы для предстоящих событий Ожидание переподключения Переустановка + Ваши данные + Год рождения + Пол + Рост в см + Вес в кг diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 56b39a6e..9d059902 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -81,12 +81,7 @@ Cài đặt Ghi chú: Ảnh thiết bị - Thông tin về bạn Tên/Bí danh - Năm sinh - Giới tính - Chiều cao bằng cm - Trọng lượng bằng kg Trình giám sát giấc ngủ Ghi tập tin nhật ký (cần khởi động lại) đang khởi chạy @@ -129,4 +124,9 @@ Giữ dữ liệu hoạt động trên thiết bị Phần cứng không tương thích Phần cứng này không tương thích với thiết bị + Thông tin về bạn + Năm sinh + Giới tính + Chiều cao bằng cm + Trọng lượng bằng kg From dbeded8d04451ca70e81ef285c5eb2e2fb2763a3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 17:53:55 +0100 Subject: [PATCH 132/274] In Control Center, do not show alarm configuration in context menu if device does not support it --- .../gadgetbridge/activities/ControlCenter.java | 3 +++ .../gadgetbridge/devices/DeviceCoordinator.java | 7 +++++++ .../gadgetbridge/devices/UnknownDeviceCoordinator.java | 5 +++++ .../gadgetbridge/devices/miband/MiBandCoordinator.java | 5 +++++ .../gadgetbridge/devices/pebble/PebbleCoordinator.java | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index e4b04a6b..13b04486 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -212,6 +212,9 @@ public class ControlCenter extends Activity { if (!coordinator.supportsScreenshots()) { menu.removeItem(R.id.controlcenter_take_screenshot); } + if (!coordinator.supportsAlarmConfiguration()) { + menu.removeItem(R.id.controlcenter_configure_alarms); + } if (selectedDevice.getState() == GBDevice.State.NOT_CONNECTED) { menu.removeItem(R.id.controlcenter_disconnect); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index fc5ea93e..e1bfe822 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -100,4 +100,11 @@ public interface DeviceCoordinator { * @return */ boolean supportsScreenshots(); + + /** + * Returns true if this device/coordinator supports settig alarms. + * + * @return + */ + boolean supportsAlarmConfiguration(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index 052c1730..7d9b88d8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -83,4 +83,9 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { public boolean supportsScreenshots() { return false; } + + @Override + public boolean supportsAlarmConfiguration() { + return false; + } } 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 868cda13..3ecf4c65 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 @@ -74,6 +74,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return false; } + @Override + public boolean supportsAlarmConfiguration() { + return true; + } + public static boolean hasValidUserInfo() { String dummyMacAddress = MiBandService.MAC_ADDRESS_FILTER_1_1A + ":00:00:00"; try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index a76534d1..f7017d7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -76,4 +76,9 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { public boolean supportsScreenshots() { return true; } + + @Override + public boolean supportsAlarmConfiguration() { + return false; + } } From 4a3547228e8d0b535db9bfc21bab4e2cdb1fa2a5 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Sun, 20 Mar 2016 18:15:45 +0100 Subject: [PATCH 133/274] Update changelog md file and bring xml file on par with it. --- CHANGELOG.md | 1 + app/src/main/res/xml/changelog_master.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6bc3ddf..dbc14535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ####Version 0.9.0 (next) * Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) +* Pebble: hide the alarm management activity as it's unsupported * Mi Band: Improve firmware detection and updates, including 1S support * Mi Band: Display HR FW for 1S * FW and HW versions are only displayed after tapping on the "info" button in Control Center diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 07f4ca5e..b101eae9 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,13 @@ + + Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) + Pebble: hide the alarm management activity as it's unsupported + Mi Band: Improve firmware detection and updates, including 1S support + Mi Band: Display HR FW for 1S + FW and HW versions are only displayed after tapping on the "info" button in Control Center + Do not display activity samples when navigating too far in the past + Add experimental widget to set the alarm time to a configurable number of hours in the future Use ckChangeLog to display the Changelog within Gadgetbridge From f046e66bf1661b336238d00bba7a3884b0e713d3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 18:19:44 +0100 Subject: [PATCH 134/274] update Italian translation (thanks @danielegobbetti) --- app/src/main/res/values-it/strings.xml | 39 ++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 8bff7471..fa3cd0d7 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -19,6 +19,7 @@ Installazione FW/App Il firmware %s verrà installato al posto di quello attualmente sulla Mi Band. + 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\nInstallarlo a proprio rischio sulla Mi Band. Se hai intenzione di procedere e tutto continua a funzionare ti invitiamo a contattare gli sviluppatori e dire loro di aggiungere il firmware: %s a quelli testati @@ -26,11 +27,14 @@ Impostazioni Impostazioni globali Collegati al dispositivo quando il bluetooth viene acceso + Applicazione musicale preferita + Default Data e ora Sincronizza l\'ora Sincronizza l\'orario al collegamento oppure quando viene cambiata l\'ora / il fuso orario in android. Notifiche Ripetizioni + Chiamate telefoniche SMS K9-Mail Pebble Messages @@ -41,13 +45,19 @@ se lo schermo è spento mai Blocca applicazioni + Risposte preimpostate + Suffisso applicato alle risposte automatiche Opzioni di sviluppo Indirizzo Miband Impostazioni Pebble + Tracker delle attività preferito + Consenti accesso ad altre applicazioni + Attiva l\'accesso sperimentale ad applicazioni Android che usano PebbleKit Forza protocollo delle notifiche Questa opzione forza l\'utilizzo della versione più recente delle notifiche in dipendenza del firmware del tuo dispositivo. ABILITALO SOLO SE SAI COSA STAI FACENDO! Abilita funzionalità non testate Abilita funzionalità non testate. ABILITARE SOLO SE SI SA QUELLO CHE SI STA FACENDO! + Tentativi di riconessione non connesso in collegamento connesso @@ -170,10 +180,39 @@ Attività in tempo reale Passi di oggi, traguardo: %1$s Se il trasferimento non viene confermato, i dati rimangono memorizzati sulla Mi Band. Utile se GB è usato insieme ad altre app. + Conserva i dati delle attività sulla Mi Band anche dopo averli sincronizzati. Utile se GB è usato insieme ad altre app. Non confermare il trasferimento dati + Storico dei passi + Passi/minuto + Passi totali + Storico passi/minuto + Inizia la tua attività + Attività + Sonno leggero + Sonno profondo + Non indossata + Non connesso. + Tutte le sveglie disabilitate + Conserva i dati delle attività sul dispositivo + Firmware non compatibile + Questo firmware non è compatibile con il dispositivo + Sveglie da riservare per i prossimi eventi del calendario + in attesa di riconessione + Re-installazion Informazioni sull\'utilizzatore Anno di nascita Genere Altezza in cm Peso in kg + Attiva + Disattiva + in autenticazione + autenticazione necessaria + Configura + Zzz + Aggiungi widge + Ore di sonno preferite + Impostata sveglia per %1$02d:%2$02d + HW: %1$s + FW: %1$s From 76fc7a2aecc8d6f98ad096862a4c8631b222cd07 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 20 Mar 2016 19:48:54 +0100 Subject: [PATCH 135/274] always save last device address when connecting, fixes #258 --- .../service/DeviceCommunicationService.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 ad49e23d..e16d9325 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -164,6 +164,7 @@ public class DeviceCommunicationService extends Service { // when we get past this, we should have valid mDeviceSupport and mGBDevice instances + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); switch (action) { case ACTION_START: start(); @@ -171,19 +172,21 @@ public class DeviceCommunicationService extends Service { case ACTION_CONNECT: start(); // ensure started GBDevice gbDevice = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); + String btDeviceAddress = null; if (gbDevice == null) { - String btDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - if (sharedPrefs != null) { // may be null in test cases - if (btDeviceAddress == null) { - btDeviceAddress = sharedPrefs.getString("last_device_address", null); - } else { - sharedPrefs.edit().putString("last_device_address", btDeviceAddress).apply(); - } + btDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); + if (btDeviceAddress == null && sharedPrefs != null) { // may be null in test cases + btDeviceAddress = sharedPrefs.getString("last_device_address", null); } if (btDeviceAddress != null) { gbDevice = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this); } + } else { + btDeviceAddress = gbDevice.getAddress(); + } + + if (sharedPrefs != null) { + sharedPrefs.edit().putString("last_device_address", btDeviceAddress).apply(); } if (gbDevice != null && !isConnecting() && !isConnected()) { @@ -232,7 +235,6 @@ public class DeviceCommunicationService extends Service { if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) > 0) || (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null)) { // NOTE: maybe not where it belongs - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); if (sharedPrefs.getBoolean("pebble_force_untested", false)) { // I would rather like to save that as an array in ShadredPreferences // this would work but I dont know how to do the same in the Settings Activity's xml From f7b71c1f9641dfb2b91843d667ba3c5ad0f74ab2 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Mar 2016 23:41:57 +0100 Subject: [PATCH 136/274] Add logging to firmware detection #234 --- .../devices/miband/AbstractMi1FirmwareInfo.java | 14 ++++++++++---- .../service/devices/miband/Mi1SFirmwareInfo.java | 5 +++-- .../devices/miband/Mi1SFirmwareInfoFW1.java | 16 ++++++++++++---- .../devices/miband/Mi1SFirmwareInfoFW2.java | 15 ++++++++++++--- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java index cf626593..3e423641 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -52,16 +52,22 @@ public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { @Override protected boolean isGenerallySupportedFirmware() { if (!isSingleMiBandFirmware()) { + LOG.warn("not a single firmware"); return false; } try { int majorVersion = getFirmwareVersionMajor(); - return majorVersion == getSupportedMajorVersion(); - } catch (IllegalArgumentException e) { - return false; + if (majorVersion == getSupportedMajorVersion()) { + return true; + } else { + LOG.info("Only major version " + getSupportedMajorVersion() + " is supported: " + majorVersion); + } + } catch (IllegalArgumentException ex) { + LOG.warn("invalid firmware or bug: " + ex.getLocalizedMessage(), ex); } catch (IndexOutOfBoundsException ex) { - return false; + LOG.warn("not supported firmware: " + ex.getLocalizedMessage(), ex); } + return false; } protected abstract int getSupportedMajorVersion(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index c9301b90..058bfb43 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory; * FW2 is heartrate firmware */ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { - private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class); + private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfo.class); private final Mi1SFirmwareInfoFW1 fw1Info; private final Mi1SFirmwareInfoFW2 fw2Info; @@ -53,9 +53,10 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { && fw1Info.getFirmwareBytes().length > 0 && fw2Info.getFirmwareBytes().length > 0; } catch (IndexOutOfBoundsException ex) { + LOG.warn("invalid firmware or bug: " + ex.getLocalizedMessage(), ex); return false; } catch (IllegalArgumentException ex) { - LOG.warn("not supported 1S firmware: ", ex); + LOG.warn("not supported 1S firmware: " + ex.getLocalizedMessage(), ex); return false; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java index b8ea7aab..9bb5e572 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java @@ -2,12 +2,15 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.support.annotation.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * FW1 is Mi Band firmware * FW2 is heartrate firmware */ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { - + private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfoFW1.class); private static final int MI1S_FW_BASE_OFFSET = 1092; Mi1SFirmwareInfoFW1(@NonNull byte[] wholeFirmwareBytes) { @@ -42,9 +45,14 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { protected boolean isGenerallySupportedFirmware() { try { int majorVersion = getFirmwareVersionMajor(); - return majorVersion == 4; - } catch (IllegalArgumentException e) { - return false; + if (majorVersion == 4) { + return true; + } else { + LOG.warn("Only major version 4 is supported for 1S fw1: " + majorVersion); + } + } catch (IllegalArgumentException ex) { + LOG.warn("not supported 1S firmware 1: " + ex.getLocalizedMessage(), ex); } + return false; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java index 9cd013f7..38484f7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java @@ -2,11 +2,15 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.support.annotation.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * FW1 is Mi Band firmware * FW2 is heartrate firmware */ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfoFW2.class); Mi1SFirmwareInfoFW2(@NonNull byte[] wholeFirmwareBytes) { super(wholeFirmwareBytes); @@ -32,10 +36,15 @@ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { protected boolean isGenerallySupportedFirmware() { try { int majorVersion = getFirmwareVersionMajor(); - return majorVersion == 1; - } catch (IllegalArgumentException e) { - return false; + if (majorVersion == 1) { + return true; + } else { + LOG.warn("Only major version 1 is supported for 1S fw2: " + majorVersion); + } + } catch (IllegalArgumentException ex) { + LOG.warn("not supported 1S firmware 2: " + ex.getLocalizedMessage(), ex); } + return false; } @Override From 275839a7f4cbc2b016d67768fb730718fddd65e6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 20 Mar 2016 23:55:48 +0100 Subject: [PATCH 137/274] last arg of copyOfRange() is index, not length! --- .../service/devices/miband/AbstractMiFirmwareInfo.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index e303e45f..f1d4d074 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -60,10 +60,8 @@ public abstract class AbstractMiFirmwareInfo { public abstract boolean isGenerallyCompatibleWith(GBDevice device); - public - @NonNull - byte[] getFirmwareBytes() { - return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareLength()); + public @NonNull byte[] getFirmwareBytes() { + return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareOffset() + getFirmwareLength()); } public int getFirmwareVersionMajor() { From 1aadcb958b1dc76757e5802289bd19b0d8d43584 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 21 Mar 2016 20:20:08 +0100 Subject: [PATCH 138/274] update changelog --- CHANGELOG.md | 3 ++- app/src/main/res/xml/changelog_master.xml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc14535..5bb6019b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,13 @@ ###Changelog -####Version 0.9.0 (next) +####Version 0.9.0 * Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) * Pebble: hide the alarm management activity as it's unsupported * Mi Band: Improve firmware detection and updates, including 1S support * Mi Band: Display HR FW for 1S * FW and HW versions are only displayed after tapping on the "info" button in Control Center * Do not display activity samples when navigating too far in the past +* Fix auto connect which was broken under some circumstances ####Version 0.8.2 * Fix database creation and updates (thanks @feclare) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index b101eae9..71f48881 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -7,6 +7,7 @@ Mi Band: Display HR FW for 1S FW and HW versions are only displayed after tapping on the "info" button in Control Center Do not display activity samples when navigating too far in the past + Fix auto connect which was broken under some circumstances Add experimental widget to set the alarm time to a configurable number of hours in the future From 1933e2bf10b169201ffc7438837539afa7d2c900 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Mon, 21 Mar 2016 21:19:32 +0100 Subject: [PATCH 139/274] Localize the title of the configuration activity. Auto open local settings (e.g. clay) --- app/src/main/AndroidManifest.xml | 2 +- app/src/main/assets/app_config/configure.html | 2 +- .../main/assets/app_config/js/gadgetbridge_boilerplate.js | 3 +++ .../gadgetbridge/activities/ExternalPebbleJSActivity.java | 6 +++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c04cea7e..5d0ac7d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -261,7 +261,7 @@

Url of the configuration:

- +
diff --git a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js index 3e025ccb..df85886d 100644 --- a/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js +++ b/app/src/main/assets/app_config/js/gadgetbridge_boilerplate.js @@ -111,6 +111,8 @@ function gbPebble() { GBjs.gbLog("app wanted to show: " + title + " body: "+ body); } + this.ready = function() { + } } @@ -129,6 +131,7 @@ if (jsConfigFile != null) { } else { document.getElementById('step2').style.display="none"; Pebble.ready(); + Pebble.showConfiguration(); } }); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index caa6a290..2f86bfaf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -96,9 +96,13 @@ public class ExternalPebbleJSActivity extends Activity { private class GBChromeClient extends WebChromeClient { @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { - GB.toast(consoleMessage.message(), Toast.LENGTH_LONG, GB.ERROR); + if (ConsoleMessage.MessageLevel.ERROR.equals(consoleMessage.messageLevel())) { + GB.toast(consoleMessage.message(), Toast.LENGTH_LONG, GB.ERROR); + //TODO: show error page + } return super.onConsoleMessage(consoleMessage); } + } private class GBWebClient extends WebViewClient { From 424d9cd142b7248e46b2b53be7fd526668970926 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 21 Mar 2016 23:41:37 +0100 Subject: [PATCH 140/274] More work on firmware detection, recognition and validation #234 Should be as robust as possible now. --- .gitignore | 2 + .../miband/AbstractMi1FirmwareInfo.java | 41 +++++++++++++++-- .../miband/AbstractMi1SFirmwareInfo.java | 5 +++ .../miband/AbstractMiFirmwareInfo.java | 15 ++++--- .../devices/miband/Mi1SFirmwareInfo.java | 44 +++++++++++++++++-- .../devices/miband/Mi1SFirmwareInfoFW1.java | 5 +++ .../devices/miband/Mi1SFirmwareInfoFW2.java | 5 +++ .../gadgetbridge/util/ArrayUtils.java | 38 ++++++++++++++++ .../service/devices/miband/FirmwareTest.java | 14 ++++-- 9 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java diff --git a/.gitignore b/.gitignore index 91efc16b..9bcb005c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ proguard/ *.iml MPChartLib + +fw.dirs diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java index 3e423641..f14fd50b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -5,18 +5,37 @@ import android.support.annotation.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + /** * Some helper methods for Mi1 and Mi1A firmware. */ public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class); + private static final byte[] SINGLE_FW_HEADER = new byte[] { + 0, + (byte)0x98, + 0, + (byte)0x20, + (byte)0x89, + 4, + 0, + (byte)0x20 + }; + private static final int SINGLE_FW_HEADER_OFFSET = 0; + private static final int MI1_FW_BASE_OFFSET = 1056; protected AbstractMi1FirmwareInfo(@NonNull byte[] wholeFirmwareBytes) { super(wholeFirmwareBytes); } + @Override + public boolean isSingleMiBandFirmware() { + return true; + } + @Override public int getFirmwareOffset() { return 0; @@ -51,11 +70,11 @@ public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { @Override protected boolean isGenerallySupportedFirmware() { - if (!isSingleMiBandFirmware()) { - LOG.warn("not a single firmware"); - return false; - } try { + if (!isHeaderValid()) { + LOG.info("unrecognized header"); + return false; + } int majorVersion = getFirmwareVersionMajor(); if (majorVersion == getSupportedMajorVersion()) { return true; @@ -70,5 +89,19 @@ public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { return false; } + protected boolean isHeaderValid() { + // TODO: not sure if this is a correct check! + return ArrayUtils.equals(SINGLE_FW_HEADER, wholeFirmwareBytes, SINGLE_FW_HEADER_OFFSET, SINGLE_FW_HEADER_OFFSET + SINGLE_FW_HEADER.length); + } + + @Override + public void checkValid() throws IllegalArgumentException { + super.checkValid(); + + if (wholeFirmwareBytes.length < SINGLE_FW_HEADER.length) { + throw new IllegalArgumentException("firmware too small: " + wholeFirmwareBytes.length); + } + } + protected abstract int getSupportedMajorVersion(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java index e22bf9db..cbe8148c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java @@ -15,4 +15,9 @@ public abstract class AbstractMi1SFirmwareInfo extends AbstractMiFirmwareInfo { public boolean isGenerallyCompatibleWith(GBDevice device) { return MiBandConst.MI_1S.equals(device.getHardwareVersion()); } + + @Override + public boolean isSingleMiBandFirmware() { + return false; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index f1d4d074..b7e79a31 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -2,11 +2,14 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.support.annotation.NonNull; +import java.lang.reflect.Array; import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; public abstract class AbstractMiFirmwareInfo { + /** * @param wholeFirmwareBytes * @return @@ -20,6 +23,7 @@ public abstract class AbstractMiFirmwareInfo { throw new IllegalArgumentException("Unsupported data (maybe not even a firmware?)."); } if (candidates.length == 1) { + candidates[0].checkValid(); return candidates[0]; } throw new IllegalArgumentException("don't know for which device the firmware is, matches multiple devices"); @@ -58,6 +62,8 @@ public abstract class AbstractMiFirmwareInfo { protected abstract boolean isGenerallySupportedFirmware(); + protected abstract boolean isHeaderValid(); + public abstract boolean isGenerallyCompatibleWith(GBDevice device); public @NonNull byte[] getFirmwareBytes() { @@ -72,12 +78,9 @@ public abstract class AbstractMiFirmwareInfo { throw new IllegalArgumentException("bad firmware version: " + version); } - public boolean isSingleMiBandFirmware() { - // TODO: not sure if this is a correct check! - if ((wholeFirmwareBytes[7] & 255) != 1) { - return true; - } - return false; + public abstract boolean isSingleMiBandFirmware(); + + public void checkValid() throws IllegalArgumentException { } public AbstractMiFirmwareInfo getFirst() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index 058bfb43..12c2388f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -5,6 +5,8 @@ import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + /** * FW1 is Mi Band firmware * FW2 is heartrate firmware @@ -12,6 +14,14 @@ import org.slf4j.LoggerFactory; public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfo.class); + private static final byte[] DOUBLE_FW_HEADER = new byte[] { + (byte)0x78, + (byte)0x75, + (byte)0x63, + (byte)0x6b + }; + private static final int DOUBLE_FW_HEADER_OFFSET = 0; + private final Mi1SFirmwareInfoFW1 fw1Info; private final Mi1SFirmwareInfoFW2 fw2Info; @@ -21,6 +31,33 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { fw2Info = new Mi1SFirmwareInfoFW2(wholeFirmwareBytes); } + protected boolean isHeaderValid() { + // TODO: not sure if this is a correct check! + return ArrayUtils.equals(DOUBLE_FW_HEADER, wholeFirmwareBytes, DOUBLE_FW_HEADER_OFFSET, DOUBLE_FW_HEADER_OFFSET + DOUBLE_FW_HEADER.length); + } + + @Override + public void checkValid() throws IllegalArgumentException { + super.checkValid(); + int firstEndIndex = getFirst().getFirmwareOffset() + getFirst().getFirmwareLength(); + if (getSecond().getFirmwareOffset() < firstEndIndex) { + throw new IllegalArgumentException("Invalid firmware offsets/lengths: " + getLengthsOffsetsString()); + } + int secondEndIndex = getSecond().getFirmwareOffset(); + if (wholeFirmwareBytes.length < firstEndIndex || wholeFirmwareBytes.length < secondEndIndex) { + throw new IllegalArgumentException("Invalid firmware size, or invalid offsets/lengths: " + getLengthsOffsetsString()); + } + if (getSecond().getFirmwareOffset() < firstEndIndex) { + throw new IllegalArgumentException("Invalid firmware, second fw starts before first fw ends: " + firstEndIndex + "," + getSecond().getFirmwareOffset()); + } + } + + protected String getLengthsOffsetsString() { + return getFirst().getFirmwareOffset() + "," + getFirst().getFirmwareLength() + + "; " + + getSecond().getFirmwareOffset() + "," + getSecond().getFirmwareLength(); + } + @Override public AbstractMiFirmwareInfo getFirst() { return fw1Info; @@ -44,10 +81,11 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { @Override protected boolean isGenerallySupportedFirmware() { - if (isSingleMiBandFirmware()) { - return false; - } try { + if (!isHeaderValid()) { + LOG.info("unrecognized header"); + return false; + } return fw1Info.isGenerallySupportedFirmware() && fw2Info.isGenerallySupportedFirmware() && fw1Info.getFirmwareBytes().length > 0 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java index 9bb5e572..ff0ad86c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java @@ -17,6 +17,11 @@ public class Mi1SFirmwareInfoFW1 extends AbstractMi1SFirmwareInfo { super(wholeFirmwareBytes); } + @Override + protected boolean isHeaderValid() { + return true; + } + @Override public int getFirmwareOffset() { return (wholeFirmwareBytes[12] & 255) << 24 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java index 38484f7b..febec98e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java @@ -16,6 +16,11 @@ public class Mi1SFirmwareInfoFW2 extends AbstractMi1SFirmwareInfo { super(wholeFirmwareBytes); } + @Override + protected boolean isHeaderValid() { + return true; + } + @Override public int getFirmwareOffset() { return (wholeFirmwareBytes[26] & 255) << 24 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java new file mode 100644 index 00000000..da0aa7ee --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java @@ -0,0 +1,38 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class ArrayUtils { + /** + * Checks the two given arrays for equality, but comparing only a subset of the second + * array with the whole first array. + * @param first the whole array to compare against + * @param second the array, of which a subset shall be compared against the whole first array + * @param secondStartIndex the start index (inclusive) of the second array from which to start the comparison + * @param secondEndIndex the end index (exclusive) of the second array until which to compare + * @return whether the first byte array is equal to the specified subset of the second byte array + * @throws IllegalArgumentException when one of the arrays is null or start and end index are wrong + */ + public static boolean equals(byte[] first, byte[] second, int secondStartIndex, int secondEndIndex) { + if (first == null) { + throw new IllegalArgumentException("first must not be null"); + } + if (second == null) { + throw new IllegalArgumentException("second must not be null"); + } + if (secondStartIndex >= secondEndIndex) { + throw new IllegalArgumentException("secondStartIndex must be smaller than secondEndIndex"); + } + if (second.length < secondEndIndex) { + throw new IllegalArgumentException("secondStartIndex must be smaller than secondEndIndex"); + } + if (first.length < secondEndIndex) { + return false; + } + int len = secondEndIndex - secondStartIndex; + for (int i = 0; i < len; i++) { + if (first[i] != second[secondStartIndex + i]) { + return false; + } + } + return true; + } +} diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index 8ceee215..2d81aa37 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -12,7 +13,7 @@ import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; -@Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") +//@Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") public class FirmwareTest { private static final long MAX_FILE_SIZE_BYTES = 1024 * 1024; // 1MB @@ -24,6 +25,11 @@ public class FirmwareTest { private static final int SINGLE = 1; private static final int DOUBLE = 2; + @BeforeClass + public static void setupSuite() { + getFirmwareDir(); // throws if firmware directory not available + } + @Test public void testFirmwareMi1() throws Exception { byte[] wholeFw = getFirmwareMi(); @@ -124,11 +130,11 @@ public class FirmwareTest { return info; } - private File getFirmwareDir() { + private static File getFirmwareDir() { String path = System.getProperty("MiFirmwareDir"); - Assert.assertNotNull(path); + Assert.assertNotNull("You must run this test with -DMiFirmwareDir=/path/to/directory/with/miband/firmwarefiles/", path); File dir = new File(path); - Assert.assertTrue(dir.isDirectory()); + Assert.assertTrue("System property MiFirmwareDir should point to a directory continaing the Mi Band firmware files", dir.isDirectory()); return dir; } From b419c93254555e7b84b9519bc87f624921f73030 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 21 Mar 2016 23:44:58 +0100 Subject: [PATCH 141/274] Disable the test for travis again --- .../gadgetbridge/service/devices/miband/FirmwareTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index 2d81aa37..bc43e069 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -13,7 +13,7 @@ import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; -//@Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") +@Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") public class FirmwareTest { private static final long MAX_FILE_SIZE_BYTES = 1024 * 1024; // 1MB From 5eb2d04513c310ac4fa511d144f5954bd401fc56 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 22 Mar 2016 13:21:24 +0100 Subject: [PATCH 142/274] update README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cae0d15b..b790dedf 100644 --- a/README.md +++ b/README.md @@ -37,17 +37,20 @@ need to create an account and transmit any of your data to the vendor's servers. * Take and share screenshots from the Pebble's screen * PebbleKit support for 3rd Party Android Apps (experimental) * Fetch activity data from Pebble Health, Misfit and Morpheuz (experimental) +* Configure watchfaces / apps (limited compatibility, experimental) ## Notes about Firmware 3.x (Pebble Time, updated OG) * Listing installed watchfaces will simply display previously installed watchapps, no matter if they are still installed or not. -## How to use (Pebble) +## Getting Started (Pebble) -1. Pair your Pebble through Gadgetbridge's Discovery Activity or the Android Bluetooth Settings +1. Pair your Pebble through the Android's Bluetooth Settings 2. Start Gadgetbridge, tap on the device you want to connect to 3. To test, choose "Debug" from the menu and play around +For more information read [this wiki article](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Getting-Started-(Pebble)) + ## Features (Mi Band) * Mi Band notifications (LEDs + vibration) for @@ -98,9 +101,12 @@ Contributions are welcome, be it feedback, bugreports, documentation, translatio on any of the open [issues](https://github.com/Freeyourgadget/Gadgetbridge/issues?q=is%3Aopen+is%3Aissue); just leave a comment that you're working on one to avoid duplicated work. +Please do not use the issue tracker as a forum, do not ask for ETAs and read the issue conversation before posting. + Translations can be contributed via https://www.transifex.com/projects/p/gadgetbridge/resource/strings/ or manually. + ## Having problems? 1. Open Gadgetbridge's settings and check the option to write log files From 767f359319cea6c30755716a61b07f2a5f2a8054 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 22 Mar 2016 13:43:01 +0100 Subject: [PATCH 143/274] Launch Android's Bluetooth settings instead of our activiy on Android >= 6.0 BLE scanning does not work on Android 6.0 and for the Pebble it does not add any value anyway. --- .../gadgetbridge/activities/ControlCenter.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 13b04486..187c5c95 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -186,8 +186,7 @@ public class ControlCenter extends Activity { GBApplication.deviceService().start(); enableSwipeRefresh(selectedDevice); - if (GB.isBluetoothEnabled() && deviceList.isEmpty()) { - // start discovery when no devices are present + if (GB.isBluetoothEnabled() && deviceList.isEmpty() && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { startActivity(new Intent(this, DiscoveryActivity.class)); } else { GBApplication.deviceService().requestDeviceInfo(); @@ -334,8 +333,11 @@ public class ControlCenter extends Activity { LocalBroadcastManager.getInstance(this).sendBroadcast(quitIntent); return true; case R.id.action_discover: - Intent discoverIntent = new Intent(this, DiscoveryActivity.class); - startActivity(discoverIntent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + startActivity(new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS)); + } else { + startActivity(new Intent(this, DiscoveryActivity.class)); + } return true; } From b0ec74696dacc2500f79ff9e1e38e9c99c1c1413 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Mar 2016 16:11:55 +0100 Subject: [PATCH 144/274] Give better feedback when a firmware cannot be installed #234 --- .../devices/miband/MiBandFWHelper.java | 18 +++++++++++------ .../miband/MiBandFWInstallHandler.java | 8 ++++++++ .../miband/AbstractMiFirmwareInfo.java | 20 ++++++++++++++++++- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 3c4ba09b..b1ba5813 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -26,12 +26,13 @@ public class MiBandFWHelper { private final Uri uri; private final ContentResolver cr; - private final - @NonNull - AbstractMiFirmwareInfo firmwareInfo; - private final - @NonNull - byte[] fw; + /** + * The backing firmware info instance, which in general supports the provided + * given firmware. You must call AbstractMiFirmwareInfo#checkValid() before + * attempting to flash it. + */ + private final @NonNull AbstractMiFirmwareInfo firmwareInfo; + private final @NonNull byte[] fw; /** * Provides a different notification API which is also used on Mi1A devices. @@ -139,6 +140,11 @@ public class MiBandFWHelper { return AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFirmwareBytes); } + /** + * The backing firmware info instance, which in general supports the provided + * given firmware. You MUST call AbstractMiFirmwareInfo#checkValid() AND + * isGenerallyCompatibleWithDevice() before attempting to flash it. + */ public AbstractMiFirmwareInfo getFirmwareInfo() { return firmwareInfo; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java index 17b891c4..8fcc91a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java @@ -47,6 +47,14 @@ public class MiBandFWInstallHandler implements InstallHandler { return; } + try { + helper.getFirmwareInfo().checkValid(); + } catch (IllegalArgumentException ex) { + installActivity.setInfoText(ex.getLocalizedMessage()); + installActivity.setInstallEnabled(false); + return; + } + GenericItem fwItem = new GenericItem(mContext.getString(R.string.miband_installhandler_miband_firmware, helper.getHumanFirmwareVersion())); fwItem.setIcon(R.drawable.ic_device_miband); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index b7e79a31..c184db9a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -23,7 +23,6 @@ public abstract class AbstractMiFirmwareInfo { throw new IllegalArgumentException("Unsupported data (maybe not even a firmware?)."); } if (candidates.length == 1) { - candidates[0].checkValid(); return candidates[0]; } throw new IllegalArgumentException("don't know for which device the firmware is, matches multiple devices"); @@ -60,10 +59,24 @@ public abstract class AbstractMiFirmwareInfo { public abstract int getFirmwareVersion(); + /** + * Returns true if the firmware data is recognized as such and can be + * handled by this instance. No further sanity checks are done at this point. + */ protected abstract boolean isGenerallySupportedFirmware(); + /** + * This method checks whether the firmware data is recognized as such and can be handled + * by this instance. It will be called by #isGenerallySupportedFirmware() in order to check + * whether this instance can be used at all or shall be thrown away. + */ protected abstract boolean isHeaderValid(); + /** + * Checks whether this instance, with the provided firmware data is compatible with the + * given device. Must be called to avoid installing Mi1 firmware on Mi1A, for example. + * @param device + */ public abstract boolean isGenerallyCompatibleWith(GBDevice device); public @NonNull byte[] getFirmwareBytes() { @@ -80,6 +93,11 @@ public abstract class AbstractMiFirmwareInfo { public abstract boolean isSingleMiBandFirmware(); + /** + * Performs a thorough sanity check of the firmware data and throws IllegalArgumentException + * if there's any problem with it. + * @throws IllegalArgumentException + */ public void checkValid() throws IllegalArgumentException { } From df3a06ac9b6952dcfa7c966b13bd4f551abe601d Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 22 Mar 2016 21:55:15 +0100 Subject: [PATCH 145/274] Reformat code, make getter of @NonNull members also @NonNull --- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index b1ba5813..21d5e64e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -31,8 +31,10 @@ public class MiBandFWHelper { * given firmware. You must call AbstractMiFirmwareInfo#checkValid() before * attempting to flash it. */ - private final @NonNull AbstractMiFirmwareInfo firmwareInfo; - private final @NonNull byte[] fw; + @NonNull + private final AbstractMiFirmwareInfo firmwareInfo; + @NonNull + private final byte[] fw; /** * Provides a different notification API which is also used on Mi1A devices. @@ -108,6 +110,7 @@ public class MiBandFWHelper { return formatFirmwareVersion(version); } + @NonNull public byte[] getFw() { return fw; } @@ -145,6 +148,7 @@ public class MiBandFWHelper { * given firmware. You MUST call AbstractMiFirmwareInfo#checkValid() AND * isGenerallyCompatibleWithDevice() before attempting to flash it. */ + @NonNull public AbstractMiFirmwareInfo getFirmwareInfo() { return firmwareInfo; } From 71461642f7ad0f632683998603b306c696d1b59e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 22 Mar 2016 23:01:41 +0100 Subject: [PATCH 146/274] Fix enabling "Write Log File" option (closes #261) --- .../activities/SettingsActivity.java | 25 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 2 files changed, 26 insertions(+) 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 596a5141..616e678a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -7,11 +7,16 @@ import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; import android.support.v4.content.LocalBroadcastManager; +import android.widget.Toast; +import java.io.IOException; import java.util.List; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_GENDER; import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM; @@ -80,6 +85,26 @@ public class SettingsActivity extends AbstractSettingsActivity { }); + pref = findPreference("log_to_file"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + if (Boolean.TRUE.equals(newVal)) { + try { + FileUtils.getExternalFilesDir(); // ensures that it is created + } catch (IOException ex) { + GB.toast(getApplicationContext(), + getString(R.string.error_creating_directory_for_logfiles, ex.getLocalizedMessage()), + Toast.LENGTH_LONG, + GB.ERROR, + ex); + } + } + return true; + } + + }); + // Get all receivers of Media Buttons Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d60e89a..fb65429d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,5 +233,6 @@ An alarm was set for %1$02d:%2$02d HW: %1$s FW: %1$s + Error creating directory for log files: %1$s From 9d9ef8a6f8352510e7c37c465f211c8da556dcf6 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 23 Mar 2016 22:06:48 +0100 Subject: [PATCH 147/274] Some cleanup --- .../gadgetbridge/devices/miband/MiBandFWHelper.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 21d5e64e..4e88e43a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -24,8 +24,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; public class MiBandFWHelper { private static final Logger LOG = LoggerFactory.getLogger(MiBandFWHelper.class); - private final Uri uri; - private final ContentResolver cr; /** * The backing firmware info instance, which in general supports the provided * given firmware. You must call AbstractMiFirmwareInfo#checkValid() before @@ -54,19 +52,12 @@ public class MiBandFWHelper { }; public MiBandFWHelper(Uri uri, Context context) throws IOException { - this.uri = uri; - cr = context.getContentResolver(); - if (cr == null) { - throw new IOException("No content resolver"); - } - String pebblePattern = ".*\\.(pbw|pbz|pbl)"; - if (uri.getPath().matches(pebblePattern)) { throw new IOException("Firmware has a filename that looks like a Pebble app/firmware."); } - try (InputStream in = new BufferedInputStream(cr.openInputStream(uri))) { + try (InputStream in = new BufferedInputStream(context.getContentResolver().openInputStream(uri))) { this.fw = FileUtils.readAll(in, 1024 * 1024); // 1 MB this.firmwareInfo = determineFirmwareInfoFor(fw); } catch (IOException ex) { From 1348bad4d36bf58ec38d2283aae5c0d19316b99d Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 23 Mar 2016 22:17:01 +0100 Subject: [PATCH 148/274] Improved log output --- .../service/btle/actions/SetDeviceStateAction.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java index a1198928..f88f4a95 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java @@ -26,4 +26,9 @@ public class SetDeviceStateAction extends PlainAction { public Context getContext() { return context; } + + @Override + public String toString() { + return super.toString() + " to " + deviceState; + } } From 11ac01f0e8524a30aa4e2e270122ecaeb21b14b5 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 23 Mar 2016 22:50:42 +0100 Subject: [PATCH 149/274] Set low latency mode during initialization #249 This appears to fix the initialization getting stuck sometimes, e.g. after turning on bluetooth and then connecting. The band incidentally sends 0x8 when it's stuck (won't accept the UUID_PAIR request). --- .../service/devices/miband/MiBandSupport.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 8dab5a4c..ff12bf68 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 @@ -97,6 +97,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { protected TransactionBuilder initializeDevice(TransactionBuilder builder) { builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext())); enableNotifications(builder, true) + .setLowLatency(builder) .pair(builder) .requestDeviceInfo(builder) .sendUserInfo(builder) @@ -106,10 +107,21 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { .enableFurtherNotifications(builder, true) .setCurrentTime(builder) .requestBatteryInfo(builder) + .setHighLatency(builder) .setInitialized(builder); return builder; } + private MiBandSupport setLowLatency(TransactionBuilder builder) { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency()); + return this; + } + + private MiBandSupport setHighLatency(TransactionBuilder builder) { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getHighLatency()); + return this; + } + private MiBandSupport checkAuthenticationNeeded(TransactionBuilder builder, GBDevice device) { builder.add(new CheckAuthenticationNeededAction(device)); return this; From 0e435d6d94b10793876fb84f9f66c30a1fb25f59 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 23 Mar 2016 23:34:42 +0100 Subject: [PATCH 150/274] Fix for device item not clickable when info items are visible --- .../freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java index cd2decd6..a1ed4359 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapter.java @@ -84,6 +84,7 @@ public class GBDeviceAdapter extends ArrayAdapter { adapter.addAll(infos); justifyListViewHeightBasedOnChildren(deviceInfoList); deviceInfoList.setVisibility(View.VISIBLE); + deviceInfoList.setFocusable(false); } } }); From e5b0afb916f73014ae048ec3316a83cddb7aa654 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Mar 2016 21:26:12 +0100 Subject: [PATCH 151/274] Enable low latency during activity sync --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 4 ++-- .../devices/miband/operations/FetchActivityOperation.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) 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 ff12bf68..b789938a 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 @@ -112,12 +112,12 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return builder; } - private MiBandSupport setLowLatency(TransactionBuilder builder) { + public MiBandSupport setLowLatency(TransactionBuilder builder) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency()); return this; } - private MiBandSupport setHighLatency(TransactionBuilder builder) { + public MiBandSupport setHighLatency(TransactionBuilder builder) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getHighLatency()); return this; } 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 42faa337..ea4dd069 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 @@ -147,7 +147,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { // scheduleTaskExecutor = Executors.newScheduledThreadPool(1); TransactionBuilder builder = performInitialized("fetch activity data"); -// builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency()); + getSupport().setLowLatency(builder); builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), fetch); builder.queue(getQueue()); @@ -403,6 +403,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { if (prefs.getBoolean(MiBandConst.PREF_MIBAND_DONT_ACK_TRANSFER, false)) { builder = performInitialized("send acknowledge"); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_STOP_SYNC_DATA}); + getSupport().setHighLatency(builder); builder.queue(getQueue()); } handleActivityFetchFinish(); From 89eddb13b025f4ace7e9e0dc7ad39f02b1309963 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Mar 2016 22:10:23 +0100 Subject: [PATCH 152/274] Fixed connection issues by reading the date from the band #249 --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 b789938a..81f44548 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 @@ -98,6 +98,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext())); enableNotifications(builder, true) .setLowLatency(builder) + .readDate(builder) // without reading the data, we get sporadic connection problems, especially directly after turning on BT .pair(builder) .requestDeviceInfo(builder) .sendUserInfo(builder) @@ -112,6 +113,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return builder; } + private MiBandSupport readDate(TransactionBuilder builder) { + builder.read(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_DATE_TIME)); + return this; + } + public MiBandSupport setLowLatency(TransactionBuilder builder) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_LE_PARAMS), getLowLatency()); return this; From ce382198d1a0b090c749f6d0ff2619f0d1207f94 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Mar 2016 22:22:58 +0100 Subject: [PATCH 153/274] updated for 0.9.1 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb6019b..6eb5f793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ ###Changelog +####Version 0.9.1 +* Mi Band: Fixed sporadic connection problems (stuck on "Initializing" #249) +* Mi Band: enable low latency connection (faster) during initialization and activity sync +* Mi Band: better feedback for firmware update +* Device Item is now clickable also when the information entries are visible +* Fix enabling log file writing #261 + ####Version 0.9.0 * Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) * Pebble: hide the alarm management activity as it's unsupported From 3714ec82da92c1bf3bf9fc5c0d31ce0fec6a6798 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 24 Mar 2016 22:23:16 +0100 Subject: [PATCH 154/274] Extracted String "HR: ", (= Heart Rate Firmware Version) --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 4 +++- app/src/main/res/values/strings.xml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) 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 81f44548..907bb140 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 @@ -834,7 +834,9 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { if (status == BluetoothGatt.GATT_SUCCESS) { mDeviceInfo = new DeviceInfo(value); if (getDeviceInfo().supportsHeartrate()) { - getDevice().addDeviceInfo(new GenericItem("HR:", MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getHeartrateFirmwareVersion()))); + getDevice().addDeviceInfo(new GenericItem( + getContext().getString(R.string.DEVINFO_HR_VER), + MiBandFWHelper.formatFirmwareVersion(mDeviceInfo.getHeartrateFirmwareVersion()))); } LOG.warn("Device info: " + mDeviceInfo); versionCmd.hwVersion = mDeviceInfo.getHwVersion(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fb65429d..4a9d3ece 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -234,5 +234,6 @@ HW: %1$s FW: %1$s Error creating directory for log files: %1$s + "HR: " From 98949f3b5465b91ae8e95b43e2e83461e0ca78a2 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Mar 2016 11:29:50 +0100 Subject: [PATCH 155/274] bump version update xml changelog --- CHANGELOG.md | 4 ++-- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 7 +++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eb5f793..21a12e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ###Changelog ####Version 0.9.1 -* Mi Band: Fixed sporadic connection problems (stuck on "Initializing" #249) -* Mi Band: enable low latency connection (faster) during initialization and activity sync +* Mi Band: fix sporadic connection problems (stuck on "Initializing" #249) +* Mi Band: enable low latency connection (faster) during initialization and activity sync * Mi Band: better feedback for firmware update * Device Item is now clickable also when the information entries are visible * Fix enabling log file writing #261 diff --git a/app/build.gradle b/app/build.gradle index 961e8814..dbb2b1d1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.0" - versionCode 44 + versionName "0.9.1" + versionCode 45 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 71f48881..e7ce6e5d 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,12 @@ + + Mi Band: fix sporadic connection problems (stuck on "Initializing" #249) + Mi Band: enable low latency connection (faster) during initialization and activity sync + Mi Band: better feedback for firmware update + Device Item is now clickable also when the information entries are visible + Fix enabling log file writing #261 + Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) Pebble: hide the alarm management activity as it's unsupported From a208907ba752d024a13d9de8d0fa1094e008cf3c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 25 Mar 2016 23:09:25 +0100 Subject: [PATCH 156/274] update Japanese from transifex (thanks!) --- app/src/main/res/values-ja/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a2021e85..1ca2b0ed 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -215,4 +215,6 @@ %1$02d:%2$02d のアラームを設定しました HW: %1$s FW: %1$s + ログファイルのディレクトリを作成中にエラー: %1$s + HR: From 8165751e57fb91dae196c17da5a330ae1975849a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 25 Mar 2016 22:21:12 +0100 Subject: [PATCH 157/274] Refactoring to test the double firmware update procedure #234 (while performing the same, known to be working firmware update for Mi1A) Result: double firmware update procedure works on Mi1A. Also updated FirmwareTest. Perform all tests not only in the test itself, but also at runtime before doing the actual update. Further: - fix setting of firmwareInfoSent state variable, which prevented installation of the section firmware - make one string translatable --- .../miband/AbstractMiFirmwareInfo.java | 7 ++ .../miband/CompositeMiFirmwareInfo.java | 96 +++++++++++++++++++ .../service/devices/miband/DeviceInfo.java | 10 +- .../devices/miband/Mi1SFirmwareInfo.java | 91 +++--------------- .../service/devices/miband/MiBandSupport.java | 6 ++ .../devices/miband/TestMi1AFirmwareInfo.java | 76 +++++++++++++++ .../operations/UpdateFirmwareOperation.java | 20 +++- app/src/main/res/values/strings.xml | 1 + .../service/devices/miband/FirmwareTest.java | 55 ++++++++--- 9 files changed, 267 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index c184db9a..43a36089 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -29,6 +29,13 @@ public abstract class AbstractMiFirmwareInfo { } private static AbstractMiFirmwareInfo[] getFirmwareInfoCandidatesFor(byte[] wholeFirmwareBytes) { + if (MiBandSupport.MI_1A_HR_FW_UPDATE_TEST_MODE_ENABLED) { + TestMi1AFirmwareInfo info = TestMi1AFirmwareInfo.getInstance(wholeFirmwareBytes); + if (info != null) { + return new AbstractMiFirmwareInfo[] { info }; + } + } + AbstractMiFirmwareInfo[] candidates = new AbstractMiFirmwareInfo[3]; int i = 0; Mi1FirmwareInfo mi1Info = Mi1FirmwareInfo.getInstance(wholeFirmwareBytes); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java new file mode 100644 index 00000000..1ec25c29 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java @@ -0,0 +1,96 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Abstract Mi firmware info class with two child info instances. + */ +public abstract class CompositeMiFirmwareInfo extends AbstractMiFirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(CompositeMiFirmwareInfo.class); + + private final T fw1Info; + private final T fw2Info; + + protected CompositeMiFirmwareInfo(byte[] wholeFirmwareBytes, T info1, T info2) { + super(wholeFirmwareBytes); + fw1Info = info1; + fw2Info = info2; + } + + @Override + public void checkValid() throws IllegalArgumentException { + super.checkValid(); + + if (getFirst().getFirmwareOffset() == getSecond().getFirmwareOffset()) { + throw new IllegalArgumentException("Illegal firmware offsets: " + getLengthsOffsetsString()); + } + if (getFirst().getFirmwareOffset() < 0 || getSecond().getFirmwareOffset() < 0 + || getFirst().getFirmwareLength() <= 0 || getSecond().getFirmwareLength() <= 0) { + throw new IllegalArgumentException("Illegal firmware offsets/lengths: " + getLengthsOffsetsString()); + } + + int firstEndIndex = getFirst().getFirmwareOffset() + getFirst().getFirmwareLength(); + if (getSecond().getFirmwareOffset() < firstEndIndex) { + throw new IllegalArgumentException("Invalid firmware, second fw starts before first fw ends: " + firstEndIndex + "," + getSecond().getFirmwareOffset()); + } + int secondEndIndex = getSecond().getFirmwareOffset(); + if (wholeFirmwareBytes.length < firstEndIndex || wholeFirmwareBytes.length < secondEndIndex) { + throw new IllegalArgumentException("Invalid firmware size, or invalid offsets/lengths: " + getLengthsOffsetsString()); + } + + getFirst().checkValid(); + getSecond().checkValid(); + } + + protected String getLengthsOffsetsString() { + return getFirst().getFirmwareOffset() + "," + getFirst().getFirmwareLength() + + "; " + + getSecond().getFirmwareOffset() + "," + getSecond().getFirmwareLength(); + } + + @Override + public T getFirst() { + return fw1Info; + } + + @Override + public T getSecond() { + return fw2Info; + } + + @Override + protected boolean isGenerallySupportedFirmware() { + try { + if (!isHeaderValid()) { + LOG.info("unrecognized header"); + return false; + } + return fw1Info.isGenerallySupportedFirmware() + && fw2Info.isGenerallySupportedFirmware() + && fw1Info.getFirmwareBytes().length > 0 + && fw2Info.getFirmwareBytes().length > 0; + } catch (IndexOutOfBoundsException ex) { + LOG.warn("invalid firmware or bug: " + ex.getLocalizedMessage(), ex); + return false; + } catch (IllegalArgumentException ex) { + LOG.warn("not supported 1S firmware: " + ex.getLocalizedMessage(), ex); + return false; + } + } + + @Override + public int getFirmwareOffset() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } + + @Override + public int getFirmwareLength() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } + + @Override + public int getFirmwareVersion() { + throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index ddad3f24..21798878 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -17,6 +17,7 @@ public class DeviceInfo extends AbstractInfo { * Heart rate firmware version identifier */ public final int fw2Version; + private boolean test1AHRMode; private boolean isChecksumCorrect(byte[] data) { @@ -71,11 +72,18 @@ public class DeviceInfo extends AbstractInfo { } public int getHeartrateFirmwareVersion() { + if (test1AHRMode) { + return fwVersion; + } return fw2Version; } + public void setTest1AHRMode(boolean enableTestMode) { + test1AHRMode = enableTestMode; + } + public boolean supportsHeartrate() { - return isMili1S(); + return isMili1S() || (test1AHRMode && isMili1A()); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index 12c2388f..d2aa5cb1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -5,13 +5,15 @@ import android.support.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; /** * FW1 is Mi Band firmware * FW2 is heartrate firmware */ -public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { +public class Mi1SFirmwareInfo extends CompositeMiFirmwareInfo { private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfo.class); private static final byte[] DOUBLE_FW_HEADER = new byte[] { @@ -22,13 +24,18 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { }; private static final int DOUBLE_FW_HEADER_OFFSET = 0; - private final Mi1SFirmwareInfoFW1 fw1Info; - private final Mi1SFirmwareInfoFW2 fw2Info; - private Mi1SFirmwareInfo(byte[] wholeFirmwareBytes) { - super(wholeFirmwareBytes); - fw1Info = new Mi1SFirmwareInfoFW1(wholeFirmwareBytes); - fw2Info = new Mi1SFirmwareInfoFW2(wholeFirmwareBytes); + super(wholeFirmwareBytes, new Mi1SFirmwareInfoFW1(wholeFirmwareBytes), new Mi1SFirmwareInfoFW2(wholeFirmwareBytes)); + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return MiBandConst.MI_1S.equals(device.getHardwareVersion()); + } + + @Override + public boolean isSingleMiBandFirmware() { + return false; } protected boolean isHeaderValid() { @@ -36,41 +43,8 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { return ArrayUtils.equals(DOUBLE_FW_HEADER, wholeFirmwareBytes, DOUBLE_FW_HEADER_OFFSET, DOUBLE_FW_HEADER_OFFSET + DOUBLE_FW_HEADER.length); } - @Override - public void checkValid() throws IllegalArgumentException { - super.checkValid(); - int firstEndIndex = getFirst().getFirmwareOffset() + getFirst().getFirmwareLength(); - if (getSecond().getFirmwareOffset() < firstEndIndex) { - throw new IllegalArgumentException("Invalid firmware offsets/lengths: " + getLengthsOffsetsString()); - } - int secondEndIndex = getSecond().getFirmwareOffset(); - if (wholeFirmwareBytes.length < firstEndIndex || wholeFirmwareBytes.length < secondEndIndex) { - throw new IllegalArgumentException("Invalid firmware size, or invalid offsets/lengths: " + getLengthsOffsetsString()); - } - if (getSecond().getFirmwareOffset() < firstEndIndex) { - throw new IllegalArgumentException("Invalid firmware, second fw starts before first fw ends: " + firstEndIndex + "," + getSecond().getFirmwareOffset()); - } - } - - protected String getLengthsOffsetsString() { - return getFirst().getFirmwareOffset() + "," + getFirst().getFirmwareLength() - + "; " - + getSecond().getFirmwareOffset() + "," + getSecond().getFirmwareLength(); - } - - @Override - public AbstractMiFirmwareInfo getFirst() { - return fw1Info; - } - - @Override - public AbstractMiFirmwareInfo getSecond() { - return fw2Info; - } - - public static @Nullable - Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + public static Mi1SFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { Mi1SFirmwareInfo info = new Mi1SFirmwareInfo(wholeFirmwareBytes); if (info.isGenerallySupportedFirmware()) { return info; @@ -78,39 +52,4 @@ public class Mi1SFirmwareInfo extends AbstractMi1SFirmwareInfo { LOG.info("firmware not supported"); return null; } - - @Override - protected boolean isGenerallySupportedFirmware() { - try { - if (!isHeaderValid()) { - LOG.info("unrecognized header"); - return false; - } - return fw1Info.isGenerallySupportedFirmware() - && fw2Info.isGenerallySupportedFirmware() - && fw1Info.getFirmwareBytes().length > 0 - && fw2Info.getFirmwareBytes().length > 0; - } catch (IndexOutOfBoundsException ex) { - LOG.warn("invalid firmware or bug: " + ex.getLocalizedMessage(), ex); - return false; - } catch (IllegalArgumentException ex) { - LOG.warn("not supported 1S firmware: " + ex.getLocalizedMessage(), ex); - return false; - } - } - - @Override - public int getFirmwareOffset() { - throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); - } - - @Override - public int getFirmwareLength() { - throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); - } - - @Override - public int getFirmwareVersion() { - throw new UnsupportedOperationException("call this method on getFirmwareXInfo()"); - } } 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 907bb140..a4aa4a42 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 @@ -77,6 +77,11 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ge public class MiBandSupport extends AbstractBTLEDeviceSupport { private static final Logger LOG = LoggerFactory.getLogger(MiBandSupport.class); + /** + * This is just for temporary testing of Mi1A double firmware update. + * DO NOT SET TO TRUE UNLESS YOU KNOW WHAT YOU'RE DOING! + */ + public static final boolean MI_1A_HR_FW_UPDATE_TEST_MODE_ENABLED = false; private volatile boolean telephoneRinging; private volatile boolean isLocatingDevice; @@ -833,6 +838,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private void handleDeviceInfo(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { mDeviceInfo = new DeviceInfo(value); + mDeviceInfo.setTest1AHRMode(MI_1A_HR_FW_UPDATE_TEST_MODE_ENABLED); if (getDeviceInfo().supportsHeartrate()) { getDevice().addDeviceInfo(new GenericItem( getContext().getString(R.string.DEVINFO_HR_VER), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java new file mode 100644 index 00000000..14c4df33 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java @@ -0,0 +1,76 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; + +import android.support.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +/** + * This is a class just for testing the dual fw firmware update procedure. + * It uses two instances of the known-to-be-working Mi1A firmware update instances + * and combines them in a CompositeMiFirmwareInfo. + * + * Most methods simply delegate to one of the child instances (FW1). + * + * FW1 is the default Mi 1A Band firmware + * FW2 is the same default Mi 1A Band firmware + */ +public class TestMi1AFirmwareInfo extends CompositeMiFirmwareInfo { + private static final Logger LOG = LoggerFactory.getLogger(TestMi1AFirmwareInfo.class); + + private TestMi1AFirmwareInfo(byte[] wholeFirmwareBytes) { + super(wholeFirmwareBytes, new Mi1AFirmwareInfo(wholeFirmwareBytes), new Mi1AFirmwareInfo(wholeFirmwareBytes)); + } + + @Override + public void checkValid() throws IllegalArgumentException { +// super.checkValid(); +// unfortunately we cannot use all of the checks in the superclass, so we roll our own + + if (getFirst().getFirmwareOffset() != getSecond().getFirmwareOffset()) { + throw new IllegalArgumentException("Test firmware offsets should be the same: " + getLengthsOffsetsString()); + } + if (getFirst().getFirmwareOffset() < 0 || getSecond().getFirmwareOffset() < 0 + || getFirst().getFirmwareLength() <= 0 || getSecond().getFirmwareLength() <= 0) { + throw new IllegalArgumentException("Illegal test firmware offsets/lengths: " + getLengthsOffsetsString()); + } + + if (getFirst().getFirmwareLength() != getSecond().getFirmwareLength()) { + throw new IllegalArgumentException("Illegal test firmware lengths: " + getLengthsOffsetsString()); + } + int firstEndIndex = getFirst().getFirmwareOffset() + getFirst().getFirmwareLength(); + int secondEndIndex = getSecond().getFirmwareOffset(); + if (wholeFirmwareBytes.length < firstEndIndex || wholeFirmwareBytes.length < secondEndIndex) { + throw new IllegalArgumentException("Invalid test firmware size, or invalid test offsets/lengths: " + getLengthsOffsetsString()); + } + + getFirst().checkValid(); + getSecond().checkValid(); + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return getFirst().isGenerallyCompatibleWith(device); + } + + @Override + public boolean isSingleMiBandFirmware() { + return false; + } + + protected boolean isHeaderValid() { + return getFirst().isHeaderValid(); + } + + @Nullable + public static TestMi1AFirmwareInfo getInstance(byte[] wholeFirmwareBytes) { + TestMi1AFirmwareInfo info = new TestMi1AFirmwareInfo(wholeFirmwareBytes); + if (info.isGenerallySupportedFirmware()) { + return info; + } + LOG.info("firmware not supported"); + return null; + } +} 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 9f98c896..d3cf3b2b 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 @@ -15,7 +15,9 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFirmwareInfo; @@ -52,8 +54,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { updateCoordinator.initNextOperation(); // updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band 1s fw update - firmwareInfoSent = updateCoordinator.sendFwInfo(); - if (!firmwareInfoSent) { + if (!updateCoordinator.sendFwInfo()) { GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); done(); } @@ -282,7 +283,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { if ((i > 0) && (i % 50 == 0)) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); - builder.add(new SetProgressAction("Firmware update in progress", true, (int) (((float) firmwareProgress) / len * 100), getContext())); + builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, (int) (((float) firmwareProgress) / len * 100), getContext())); } LOG.info("Firmware update progress:" + firmwareProgress + " total len:" + len + " progress:" + (int) (((float) firmwareProgress) / len * 100)); @@ -298,14 +299,14 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { if (firmwareProgress >= len) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); } else { - GB.updateInstallNotification("Firmware write failed", false, 0, getContext()); + GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); } builder.queue(getQueue()); } catch (IOException ex) { LOG.error("Unable to send fw to MI", ex); - GB.updateInstallNotification("Firmware write failed", false, 0, getContext()); + GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); return false; } return true; @@ -329,6 +330,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { TransactionBuilder builder = performInitialized("send firmware info"); builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo()); + builder.add(new FirmwareInfoSucceededAction()); builder.queue(getQueue()); return true; } catch (IOException e) { @@ -442,4 +444,12 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { return false; } } + + private class FirmwareInfoSucceededAction extends PlainAction { + @Override + public boolean run(BluetoothGatt gatt) { + firmwareInfoSent = true; + return true; + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a9d3ece..5978634e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -235,5 +235,6 @@ FW: %1$s Error creating directory for log files: %1$s "HR: " + Firmware update in progress diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index bc43e069..dfac214f 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -36,6 +36,8 @@ public class FirmwareTest { Assert.assertNotNull(wholeFw); AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE); + info.checkValid(); + int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("1.")); @@ -49,6 +51,8 @@ public class FirmwareTest { Assert.assertNotNull(wholeFw); AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, SINGLE); + info.checkValid(); + int calculatedVersion = info.getFirmwareVersion(); String version = MiBandFWHelper.formatFirmwareVersion(calculatedVersion); Assert.assertTrue(version.startsWith("5.")); @@ -62,20 +66,9 @@ public class FirmwareTest { Assert.assertNotNull(wholeFw); AbstractMiFirmwareInfo info = getFirmwareInfo(wholeFw, DOUBLE); + info.checkValid(); // Mi Band version - int calculatedLengthFw1 = info.getFirst().getFirmwareLength(); - int calculatedOffsetFw1 = info.getFirst().getFirmwareOffset(); - int endIndexFw1 = calculatedOffsetFw1 + calculatedLengthFw1; - - int calculatedLengthFw2 = info.getSecond().getFirmwareLength(); - int calculatedOffsetFw2 = info.getSecond().getFirmwareOffset(); - int endIndexFw2 = calculatedOffsetFw2 + calculatedLengthFw2; - - Assert.assertTrue(endIndexFw1 <= wholeFw.length - calculatedLengthFw2); - Assert.assertTrue(endIndexFw2 <= wholeFw.length); - - Assert.assertTrue(endIndexFw1 <= calculatedOffsetFw2); int calculatedVersionFw1 = info.getFirst().getFirmwareVersion(); // Assert.assertEquals("Unexpected firmware 1 version: " + calculatedVersionFw1, MI1S_FW1_VERSION, calculatedVersionFw1); String version1 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw1); @@ -93,12 +86,48 @@ public class FirmwareTest { } catch (UnsupportedOperationException expected) { } - Assert.assertNotEquals(info.getFirst().getFirmwareOffset(), info.getSecond().getFirmwareOffset()); Assert.assertFalse(Arrays.equals(info.getFirst().getFirmwareBytes(), info.getSecond().getFirmwareBytes())); } + @Test + public void testDoubleFirmwareMi1A() throws Exception { + byte[] wholeFw = getFirmwareMi1A(); + Assert.assertNotNull(wholeFw); + + AbstractMiFirmwareInfo info = TestMi1AFirmwareInfo.getInstance(wholeFw); + Assert.assertNotNull(info); + info.checkValid(); + + // Mi Band version + int calculatedVersionFw1 = info.getFirst().getFirmwareVersion(); +// Assert.assertEquals("Unexpected firmware 1 version: " + calculatedVersionFw1, MI1S_FW1_VERSION, calculatedVersionFw1); + String version1 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw1); + Assert.assertTrue(version1.startsWith("5.")); + + // Same Mi Band version + int calculatedVersionFw2 = info.getSecond().getFirmwareVersion(); +// Assert.assertEquals("Unexpected firmware 2 version: " + calculatedVersionFw2, MI1S_FW2_VERSION, calculatedVersionFw2); + String version2 = MiBandFWHelper.formatFirmwareVersion(calculatedVersionFw2); + Assert.assertTrue(version2.startsWith("5.")); + + try { + info.getFirmwareVersion(); + Assert.fail("should not get fw version from AbstractMi1SFirmwareInfo"); + } catch (UnsupportedOperationException expected) { + } + + // these are actually the same with this test info! + Assert.assertEquals(info.getFirst().getFirmwareOffset(), info.getSecond().getFirmwareOffset()); + Assert.assertTrue(Arrays.equals(info.getFirst().getFirmwareBytes(), info.getSecond().getFirmwareBytes())); + } + private AbstractMiFirmwareInfo getFirmwareInfo(byte[] wholeFw, int numFirmwares) { AbstractMiFirmwareInfo info = AbstractMiFirmwareInfo.determineFirmwareInfoFor(wholeFw); + assertFirmwareInfo(info, wholeFw, numFirmwares); + return info; + } + + private AbstractMiFirmwareInfo assertFirmwareInfo(AbstractMiFirmwareInfo info, byte[] wholeFw, int numFirmwares) { switch (numFirmwares) { case SINGLE: { Assert.assertTrue("should be single miband firmware", info.isSingleMiBandFirmware()); From bff583793016e946ae209426ec4ee845c6c532ca Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 25 Mar 2016 23:54:42 +0100 Subject: [PATCH 158/274] Sort device infos --- .../nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index e296ad98..763e6375 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -344,6 +345,7 @@ public class GBDevice implements Parcelable { if (mFirmwareVersion != null) { result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion)); } + Collections.sort(result); return result; } From 298b7542a4d7b0af017e670b1902502395430fb5 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 26 Mar 2016 00:01:32 +0100 Subject: [PATCH 159/274] Update changelog for 0.9.2 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a12e34..c1af7af5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ###Changelog +####Version 0.9.2 +* Mi Band: Second firmware of Mi1S could not be updated (#234) +* Fixed ordering issue of device infos being displayed + ####Version 0.9.1 * Mi Band: fix sporadic connection problems (stuck on "Initializing" #249) * Mi Band: enable low latency connection (faster) during initialization and activity sync From a70c31f96501e0712d03060b54d3e32c83dae7f2 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 26 Mar 2016 00:10:32 +0100 Subject: [PATCH 160/274] Add commandline for running all firmware tests --- .../gadgetbridge/service/devices/miband/FirmwareTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java index dfac214f..5f6053b9 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/FirmwareTest.java @@ -13,6 +13,7 @@ import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +// while read i; do ./gradlew --daemon -a --offline -DMiFirmwareDir=$i test; if test $? != 0; then echo "Failure in $i" && break; fi; done < fw.dirs @Ignore("Disabled for travis -- needs vm parameter -DMiFirmwareDir=/path/to/firmware/directory/") public class FirmwareTest { From 9d29e4db3fc140a07f315d5cd24fafef82724a11 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 26 Mar 2016 10:04:02 +0100 Subject: [PATCH 161/274] bump version, update xml changelog --- CHANGELOG.md | 4 ++-- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1af7af5..8c5bc3d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ###Changelog ####Version 0.9.2 -* Mi Band: Second firmware of Mi1S could not be updated (#234) -* Fixed ordering issue of device infos being displayed +* Mi Band: Fix update of second (HR) firmware on Mi1S (#234) +* Fix ordering issue of device infos being displayed ####Version 0.9.1 * Mi Band: fix sporadic connection problems (stuck on "Initializing" #249) diff --git a/app/build.gradle b/app/build.gradle index dbb2b1d1..accb84ae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.1" - versionCode 45 + versionName "0.9.2" + versionCode 46 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index e7ce6e5d..608a543c 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,9 @@ + + Mi Band: Fix update of second (HR) firmware on Mi1S (#234) + Fix ordering issue of device infos being displayed + Mi Band: fix sporadic connection problems (stuck on "Initializing" #249) Mi Band: enable low latency connection (faster) during initialization and activity sync From bfcfe82f17250cb47c524213cab7facfa47bc223 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 26 Mar 2016 20:45:07 +0100 Subject: [PATCH 162/274] Improve pairing activity: #254 - add hint about rebooting phone - request enabling bluetooth --- .../gadgetbridge/activities/DiscoveryActivity.java | 10 +++++++++- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) 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 5b0d5549..81a5a2f6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -249,7 +249,13 @@ public class DiscoveryActivity extends Activity implements AdapterView.OnItemCli private void bluetoothStateChanged(int oldState, int newState) { discoveryFinished(); - startButton.setEnabled(newState == BluetoothAdapter.STATE_ON); + if (newState == BluetoothAdapter.STATE_ON) { + this.adapter = BluetoothAdapter.getDefaultAdapter(); + startButton.setEnabled(true); + } else { + this.adapter = null; + startButton.setEnabled(false); + } } private void discoveryFinished() { @@ -286,6 +292,8 @@ public class DiscoveryActivity extends Activity implements AdapterView.OnItemCli BluetoothAdapter adapter = bluetoothService.getAdapter(); if (!adapter.isEnabled()) { LOG.warn("Bluetooth not enabled"); + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivity(enableBtIntent); this.adapter = null; return false; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5978634e..25a6f77e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -116,7 +116,7 @@ No valid user data given, using dummy user data for now. When your Mi Band vibrates and blinks, tap it a few times in a row. Install - Make your device discoverable. Currently connected devices will likely not be discovered. + Make your device discoverable. Currently connected devices will likely not be discovered. If your device does not show up after two minutes, try again after rebooting your mobile device. Note: Device Image Name/Alias From adfef3db4253e32824d70acf490762a9308f63d6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Mar 2016 17:44:20 +0200 Subject: [PATCH 163/274] Prepare code for more music metadata (duration, track count, current track number) Oh and format code --- .../activities/DebugActivity.java | 2 +- .../activities/SettingsActivity.java | 1 - .../gadgetbridge/devices/EventHandler.java | 2 +- .../devices/miband/MiBandFWHelper.java | 1 - .../externalevents/MusicPlaybackReceiver.java | 11 ++++- .../gadgetbridge/impl/GBDeviceService.java | 8 +++- .../gadgetbridge/model/DeviceService.java | 3 ++ .../service/DeviceCommunicationService.java | 8 +++- .../service/ServiceDeviceSupport.java | 4 +- .../miband/AbstractMi1FirmwareInfo.java | 10 ++-- .../miband/AbstractMiFirmwareInfo.java | 9 ++-- .../devices/miband/Mi1SFirmwareInfo.java | 10 ++-- .../service/devices/miband/MiBandSupport.java | 2 +- .../devices/miband/TestMi1AFirmwareInfo.java | 4 +- .../operations/UpdateFirmwareOperation.java | 1 - .../devices/pebble/PebbleProtocol.java | 46 ++++++++++++++++++- .../serial/AbstractSerialDeviceSupport.java | 4 +- .../service/serial/GBDeviceProtocol.java | 2 +- .../gadgetbridge/util/ArrayUtils.java | 7 +-- .../service/TestDeviceSupport.java | 2 +- 20 files changed, 99 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 757fdd6e..2418f97a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -202,7 +202,7 @@ public class DebugActivity extends Activity { GBApplication.deviceService().onSetMusicInfo( editContent.getText().toString() + "(artist)", editContent.getText().toString() + "(album)", - editContent.getText().toString() + "(tracl)"); + editContent.getText().toString() + "(track)", 90, 10, 2); } }); 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 616e678a..34942ef2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -12,7 +12,6 @@ import android.widget.Toast; import java.io.IOException; import java.util.List; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index adcf077d..f03413af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -24,7 +24,7 @@ public interface EventHandler { void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command); - void onSetMusicInfo(String artist, String album, String track); + void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr); void onEnableRealtimeSteps(boolean enable); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 4e88e43a..20ff39d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; -import android.content.ContentResolver; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 371e33d4..1b452030 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -19,9 +19,16 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { String artist = intent.getStringExtra("artist"); String album = intent.getStringExtra("album"); String track = intent.getStringExtra("track"); - + /* + Bundle bundle = intent.getExtras(); + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + LOG.info(String.format("%s %s (%s)", key, + value.toString(), value.getClass().getName())); + } + */ LOG.info("Current track: " + artist + ", " + album + ", " + track); - GBApplication.deviceService().onSetMusicInfo(artist, album, track); + GBApplication.deviceService().onSetMusicInfo(artist, album, track, 0, 0, 0); } } 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 22278693..62d7f15c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -124,10 +124,14 @@ public class GBDeviceService implements DeviceService { } @Override - public void onSetMusicInfo(String artist, String album, String track) { + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { Intent intent = createIntent().setAction(ACTION_SETMUSICINFO) .putExtra(EXTRA_MUSIC_ARTIST, artist) - .putExtra(EXTRA_MUSIC_TRACK, track); + .putExtra(EXTRA_MUSIC_ALBUM, album) + .putExtra(EXTRA_MUSIC_TRACK, track) + .putExtra(EXTRA_MUSIC_DURATION, duration) + .putExtra(EXTRA_MUSIC_TRACKCOUNT, trackCount) + .putExtra(EXTRA_MUSIC_TRACKNR, trackNr); invokeService(intent); } 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 498cb29c..8217f6d4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -49,6 +49,9 @@ public interface DeviceService extends EventHandler { String EXTRA_MUSIC_ARTIST = "music_artist"; String EXTRA_MUSIC_ALBUM = "music_album"; String EXTRA_MUSIC_TRACK = "music_track"; + String EXTRA_MUSIC_DURATION = "music_duration"; + String EXTRA_MUSIC_TRACKNR = "music_tracknr"; + String EXTRA_MUSIC_TRACKCOUNT = "music_trackcount"; String EXTRA_APP_UUID = "app_uuid"; String EXTRA_APP_START = "app_start"; String EXTRA_APP_CONFIG = "app_config"; 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 e16d9325..a9261f7b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -70,7 +70,10 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ENA import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_DURATION; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACK; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKCOUNT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKNR; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_FLAGS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_ID; @@ -290,7 +293,10 @@ public class DeviceCommunicationService extends Service { String artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST); String album = intent.getStringExtra(EXTRA_MUSIC_ALBUM); String track = intent.getStringExtra(EXTRA_MUSIC_TRACK); - mDeviceSupport.onSetMusicInfo(artist, album, track); + int duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0); + int trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0); + int trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); + mDeviceSupport.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); break; case ACTION_REQUEST_APPINFO: mDeviceSupport.onAppInfoReq(); 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 d13c90d2..993b0319 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -139,11 +139,11 @@ public class ServiceDeviceSupport implements DeviceSupport { } @Override - public void onSetMusicInfo(String artist, String album, String track) { + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { if (checkBusy("set music info")) { return; } - delegate.onSetMusicInfo(artist, album, track); + delegate.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java index f14fd50b..e274c9ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -13,15 +13,15 @@ import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; public abstract class AbstractMi1FirmwareInfo extends AbstractMiFirmwareInfo { private static final Logger LOG = LoggerFactory.getLogger(AbstractMi1FirmwareInfo.class); - private static final byte[] SINGLE_FW_HEADER = new byte[] { + private static final byte[] SINGLE_FW_HEADER = new byte[]{ 0, - (byte)0x98, + (byte) 0x98, 0, - (byte)0x20, - (byte)0x89, + (byte) 0x20, + (byte) 0x89, 4, 0, - (byte)0x20 + (byte) 0x20 }; private static final int SINGLE_FW_HEADER_OFFSET = 0; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index 43a36089..a4b3daf6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -2,11 +2,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.support.annotation.NonNull; -import java.lang.reflect.Array; import java.util.Arrays; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; public abstract class AbstractMiFirmwareInfo { @@ -32,7 +30,7 @@ public abstract class AbstractMiFirmwareInfo { if (MiBandSupport.MI_1A_HR_FW_UPDATE_TEST_MODE_ENABLED) { TestMi1AFirmwareInfo info = TestMi1AFirmwareInfo.getInstance(wholeFirmwareBytes); if (info != null) { - return new AbstractMiFirmwareInfo[] { info }; + return new AbstractMiFirmwareInfo[]{info}; } } @@ -82,11 +80,13 @@ public abstract class AbstractMiFirmwareInfo { /** * Checks whether this instance, with the provided firmware data is compatible with the * given device. Must be called to avoid installing Mi1 firmware on Mi1A, for example. + * * @param device */ public abstract boolean isGenerallyCompatibleWith(GBDevice device); - public @NonNull byte[] getFirmwareBytes() { + @NonNull + public byte[] getFirmwareBytes() { return Arrays.copyOfRange(wholeFirmwareBytes, getFirmwareOffset(), getFirmwareOffset() + getFirmwareLength()); } @@ -103,6 +103,7 @@ public abstract class AbstractMiFirmwareInfo { /** * Performs a thorough sanity check of the firmware data and throws IllegalArgumentException * if there's any problem with it. + * * @throws IllegalArgumentException */ public void checkValid() throws IllegalArgumentException { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index d2aa5cb1..926afca1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -16,11 +16,11 @@ import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; public class Mi1SFirmwareInfo extends CompositeMiFirmwareInfo { private static final Logger LOG = LoggerFactory.getLogger(Mi1SFirmwareInfo.class); - private static final byte[] DOUBLE_FW_HEADER = new byte[] { - (byte)0x78, - (byte)0x75, - (byte)0x63, - (byte)0x6b + private static final byte[] DOUBLE_FW_HEADER = new byte[]{ + (byte) 0x78, + (byte) 0x75, + (byte) 0x63, + (byte) 0x6b }; private static final int DOUBLE_FW_HEADER_OFFSET = 0; 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 a4aa4a42..733b89ac 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 @@ -540,7 +540,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void onSetMusicInfo(String artist, String album, String track) { + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { // not supported } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java index 14c4df33..9df8b778 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java @@ -11,9 +11,9 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; * This is a class just for testing the dual fw firmware update procedure. * It uses two instances of the known-to-be-working Mi1A firmware update instances * and combines them in a CompositeMiFirmwareInfo. - * + *

* Most methods simply delegate to one of the child instances (FW1). - * + *

* FW1 is the default Mi 1A Band firmware * FW2 is the same default Mi 1A Band firmware */ 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 d3cf3b2b..f2de50d9 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 @@ -15,7 +15,6 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; 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 d426740b..3a7e2a37 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 @@ -1083,9 +1083,51 @@ public class PebbleProtocol extends GBDeviceProtocol { } @Override - public byte[] encodeSetMusicInfo(String artist, String album, String track) { + public byte[] encodeSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { String[] parts = {artist, album, track}; - return encodeMessage(ENDPOINT_MUSICCONTROL, MUSICCONTROL_SETMUSICINFO, 0, parts); + if (duration == 0) { + return encodeMessage(ENDPOINT_MUSICCONTROL, MUSICCONTROL_SETMUSICINFO, 0, parts); + } else { + // Calculate length first + int length = LENGTH_PREFIX + 13; + if (parts != null) { + for (String s : parts) { + if (s == null || s.equals("")) { + length++; // encode null or empty strings as 0x00 later + continue; + } + length += (1 + s.getBytes().length); + } + } + + // Encode Prefix + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) (length - LENGTH_PREFIX)); + buf.putShort(ENDPOINT_MUSICCONTROL); + buf.put(MUSICCONTROL_SETMUSICINFO); + + // Encode Pascal-Style Strings + for (String s : parts) { + if (s == null || s.equals("")) { + buf.put((byte) 0x00); + continue; + } + + int partlength = s.getBytes().length; + if (partlength > 255) partlength = 255; + buf.put((byte) partlength); + buf.put(s.getBytes(), 0, partlength); + } + + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.putInt(duration*1000); + buf.putInt(trackCount); + buf.putInt(trackNr); + + return buf.array(); + + } } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index eaea3c84..5563de59 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -126,8 +126,8 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport } @Override - public void onSetMusicInfo(String artist, String album, String track) { - byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(artist, album, track); + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(artist, album, track, duration, trackCount, trackNr); sendToDevice(bytes); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 439c7529..b08b105d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -20,7 +20,7 @@ public abstract class GBDeviceProtocol { return null; } - public byte[] encodeSetMusicInfo(String artist, String album, String track) { + public byte[] encodeSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { return null; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java index da0aa7ee..7c767cc1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java @@ -4,10 +4,11 @@ public class ArrayUtils { /** * Checks the two given arrays for equality, but comparing only a subset of the second * array with the whole first array. - * @param first the whole array to compare against - * @param second the array, of which a subset shall be compared against the whole first array + * + * @param first the whole array to compare against + * @param second the array, of which a subset shall be compared against the whole first array * @param secondStartIndex the start index (inclusive) of the second array from which to start the comparison - * @param secondEndIndex the end index (exclusive) of the second array until which to compare + * @param secondEndIndex the end index (exclusive) of the second array until which to compare * @return whether the first byte array is equal to the specified subset of the second byte array * @throws IllegalArgumentException when one of the arrays is null or start and end index are wrong */ diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 29043020..6528b613 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -66,7 +66,7 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } @Override - public void onSetMusicInfo(String artist, String album, String track) { + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { } From 6ce63276a318fd8c15fe6bcf5e09d690efaca6e5 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 27 Mar 2016 19:50:32 +0200 Subject: [PATCH 164/274] play around with play states, simplify weired nested switch --- .../activities/DebugActivity.java | 2 +- .../devices/pebble/PebbleProtocol.java | 86 +++++++++++-------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 2418f97a..3d438bcd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -202,7 +202,7 @@ public class DebugActivity extends Activity { GBApplication.deviceService().onSetMusicInfo( editContent.getText().toString() + "(artist)", editContent.getText().toString() + "(album)", - editContent.getText().toString() + "(track)", 90, 10, 2); + editContent.getText().toString() + "(track)", 20, 10, 2); } }); 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 3a7e2a37..fc00f327 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 @@ -111,7 +111,9 @@ public class PebbleProtocol extends GBDeviceProtocol { static final byte PHONECONTROL_START = 8; static final byte PHONECONTROL_END = 9; - static final byte MUSICCONTROL_SETMUSICINFO = 16; + static final byte MUSICCONTROL_SETMUSICINFO = 0x10; + static final byte MUSICCONTROL_SETPLAYSTATE = 0x11; + static final byte MUSICCONTROL_PLAYPAUSE = 1; static final byte MUSICCONTROL_PAUSE = 2; static final byte MUSICCONTROL_PLAY = 3; @@ -119,7 +121,13 @@ public class PebbleProtocol extends GBDeviceProtocol { static final byte MUSICCONTROL_PREVIOUS = 5; static final byte MUSICCONTROL_VOLUMEUP = 6; static final byte MUSICCONTROL_VOLUMEDOWN = 7; - static final byte MUSICCONTROL_GETNOWPLAYING = 7; + static final byte MUSICCONTROL_GETNOWPLAYING = 8; + + static final byte MUSICCONTROL_STATE_PAUSED = 0x00; + static final byte MUSICCONTROL_STATE_PLAYING = 0x01; + static final byte MUSICCONTROL_STATE_REWINDING = 0x02; + static final byte MUSICCONTROL_STATE_FASTWORWARDING = 0x03; + static final byte MUSICCONTROL_STATE_UNKNOWN = 0x04; static final byte NOTIFICATIONACTION_ACK = 0; static final byte NOTIFICATIONACTION_NACK = 1; @@ -407,7 +415,6 @@ public class PebbleProtocol extends GBDeviceProtocol { if (parts != null) { for (String s : parts) { if (s == null || s.equals("")) { - //buf.put((byte)0x01); buf.put((byte) 0x00); continue; } @@ -802,33 +809,21 @@ public class PebbleProtocol extends GBDeviceProtocol { icon_id = PebbleIconID.GENERIC_SMS; color_id = PebbleColor.VividViolet; break; + case TWITTER: + icon_id = PebbleIconID.NOTIFICATION_TWITTER; + color_id = PebbleColor.BlueMoon; + break; + case FACEBOOK: + icon_id = PebbleIconID.NOTIFICATION_FACEBOOK; + color_id = PebbleColor.VeryLightBlue; + break; + case CHAT: + icon_id = PebbleIconID.NOTIFICATION_HIPCHAT; + color_id = PebbleColor.Inchworm; + break; default: - switch (notificationType) { - case TWITTER: - icon_id = PebbleIconID.NOTIFICATION_TWITTER; - color_id = PebbleColor.BlueMoon; - break; - case EMAIL: - icon_id = PebbleIconID.GENERIC_EMAIL; - color_id = PebbleColor.JaegerGreen; - break; - case SMS: - icon_id = PebbleIconID.GENERIC_SMS; - color_id = PebbleColor.VividViolet; - break; - case FACEBOOK: - icon_id = PebbleIconID.NOTIFICATION_FACEBOOK; - color_id = PebbleColor.VeryLightBlue; - break; - case CHAT: - icon_id = PebbleIconID.NOTIFICATION_HIPCHAT; - color_id = PebbleColor.Inchworm; - break; - default: - icon_id = PebbleIconID.NOTIFICATION_GENERIC; - color_id = PebbleColor.Red; - break; - } + icon_id = PebbleIconID.NOTIFICATION_GENERIC; + color_id = PebbleColor.Red; break; } // Calculate length first @@ -1082,14 +1077,34 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeMessage(ENDPOINT_PHONECONTROL, pebbleCmd, 0, parts); } + public byte[] encodeSetMusicState(byte state, int position, int playRate, byte shuffle, byte repeat) { + int length = LENGTH_PREFIX + 12; + // Encode Prefix + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.putShort((short) (length - LENGTH_PREFIX)); + buf.putShort(ENDPOINT_MUSICCONTROL); + + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.put(MUSICCONTROL_SETPLAYSTATE); + buf.put(state); + buf.putInt(position); + buf.putInt(playRate); + buf.put(shuffle); + buf.put(repeat); + + return buf.array(); + } + @Override public byte[] encodeSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { String[] parts = {artist, album, track}; if (duration == 0) { return encodeMessage(ENDPOINT_MUSICCONTROL, MUSICCONTROL_SETMUSICINFO, 0, parts); } else { + byte[] stateMessage = encodeSetMusicState(MUSICCONTROL_STATE_PLAYING, 0, 100, (byte) 1, (byte) 1); // Calculate length first - int length = LENGTH_PREFIX + 13; + int length = LENGTH_PREFIX + 9; if (parts != null) { for (String s : parts) { if (s == null || s.equals("")) { @@ -1101,7 +1116,7 @@ public class PebbleProtocol extends GBDeviceProtocol { } // Encode Prefix - ByteBuffer buf = ByteBuffer.allocate(length); + ByteBuffer buf = ByteBuffer.allocate(length + stateMessage.length); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort((short) (length - LENGTH_PREFIX)); buf.putShort(ENDPOINT_MUSICCONTROL); @@ -1121,12 +1136,13 @@ public class PebbleProtocol extends GBDeviceProtocol { } buf.order(ByteOrder.LITTLE_ENDIAN); - buf.putInt(duration*1000); - buf.putInt(trackCount); - buf.putInt(trackNr); + buf.putInt(duration * 1000); + buf.putShort((short) (trackCount & 0xffff)); + buf.putShort((short) (trackNr & 0xffff)); + + buf.put(stateMessage); return buf.array(); - } } From 8815f0d134e19648b664be12d709fa804511b256 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 28 Mar 2016 21:30:05 +0200 Subject: [PATCH 165/274] Small cleanups and fixlets. --- .../btle/actions/SetProgressAction.java | 5 +++ .../operations/UpdateFirmwareOperation.java | 39 ++++++++----------- app/src/main/res/values/strings.xml | 1 + 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java index 8f3a826b..7a3e2d20 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java @@ -3,9 +3,13 @@ package nodomain.freeyourgadget.gadgetbridge.service.btle.actions; import android.bluetooth.BluetoothGatt; import android.content.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import nodomain.freeyourgadget.gadgetbridge.util.GB; public class SetProgressAction extends PlainAction { + private static final Logger LOG = LoggerFactory.getLogger(SetProgressAction.class); private final String text; private final boolean ongoing; @@ -30,6 +34,7 @@ public class SetProgressAction extends PlainAction { @Override public boolean run(BluetoothGatt gatt) { + LOG.info(toString()); GB.updateInstallNotification(this.text, this.ongoing, this.percentage, this.context); return true; } 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 f2de50d9..3acff6e6 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 @@ -52,7 +52,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } updateCoordinator.initNextOperation(); -// updateCoordinator.initNextOperation(); // FIXME: remove, just testing mi band 1s fw update if (!updateCoordinator.sendFwInfo()) { GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); done(); @@ -103,7 +102,6 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { if (firmwareInfoSent) { GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO); if (!updateCoordinator.sendFwData()) { - //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); } @@ -119,7 +117,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { if (updateCoordinator.initNextOperation()) { GB.toast(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO); if (!updateCoordinator.sendFwInfo()) { - GB.toast(getContext(), "Sending Mi Band Firmware failed, aborting. Do NOT reboot your Mi Band!", Toast.LENGTH_LONG, GB.INFO); + GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); done(); } break; @@ -256,10 +254,10 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { /** - * Method that uploads a firmware (fwbytes) to the MiBand. - * The firmware has to be splitted into chunks of 20 bytes each, and periodically a COMMAND_SYNC comand has to be issued to the MiBand. + * Method that uploads a firmware (fwbytes) to the Mi Band. + * The firmware has to be split into chunks of 20 bytes each, and periodically a COMMAND_SYNC command has to be issued to the Mi Band. *

- * The Mi Band will send a notification after receiving these data to confirm if the firmware looks good to it. + * The Mi Band will send a notification after receiving this data to confirm if the firmware looks good to it. * * @param fwbytes * @return whether the transfer succeeded or not. Only a BT layer exception will cause the transmission to fail. @@ -270,6 +268,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { final int packetLength = 20; int packets = len / packetLength; + // going from 0 to len int firmwareProgress = 0; try { @@ -280,32 +279,25 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk); firmwareProgress += packetLength; + int progressPercent = (int) (((float) firmwareProgress) / len) * 100; if ((i > 0) && (i % 50 == 0)) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); - builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, (int) (((float) firmwareProgress) / len * 100), getContext())); + builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, progressPercent, getContext())); } - - LOG.info("Firmware update progress:" + firmwareProgress + " total len:" + len + " progress:" + (int) (((float) firmwareProgress) / len * 100)); } - if (!(len % packetLength == 0)) { + if (firmwareProgress < len) { byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk); - firmwareProgress += len % packetLength; - } - - LOG.info("Firmware update progress:" + firmwareProgress + " total len:" + len + " progress:" + (firmwareProgress / len)); - if (firmwareProgress >= len) { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); - } else { - GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); + firmwareProgress = len; } + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); builder.queue(getQueue()); } catch (IOException ex) { LOG.error("Unable to send fw to MI", ex); - GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); + GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_firmware_not_sent), false, 0, getContext()); return false; } return true; @@ -381,7 +373,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { INITIAL, SEND_FW2, SEND_FW1, - FINISHED, UNKNOWN + FINISHED, + UNKNOWN } private class DoubleUpdateCoordinator extends UpdateCoordinator { @@ -389,7 +382,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { private final byte[] fw1Info; private final byte[] fw1Data; - private final byte[] fw21nfo; + private final byte[] fw2Info; private final byte[] fw2Data; private byte[] currentFwInfo; @@ -401,7 +394,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { super(reboot); this.fw1Info = fw1Info; this.fw1Data = fw1Data; - this.fw21nfo = fw2Info; + this.fw2Info = fw2Info; this.fw2Data = fw2Data; // start with fw2 (heart rate) @@ -423,7 +416,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { public boolean initNextOperation() { switch (state) { case INITIAL: - currentFwInfo = fw21nfo; + currentFwInfo = fw2Info; currentFwData = fw2Data; state = State.SEND_FW2; return true; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25a6f77e..3bcbddd8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -236,5 +236,6 @@ Error creating directory for log files: %1$s "HR: " Firmware update in progress + Firmware not sent From cbc57b4407ee5302605132857bc2166d94e42e1c Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 28 Mar 2016 23:44:39 +0200 Subject: [PATCH 166/274] Pebble: Fix stupid bug that made Pebble Health unavailable in App Manager (Fixes #269) --- .../gadgetbridge/activities/AppManagerActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index e2a1d56d..6b566f3e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -80,7 +80,7 @@ public class AppManagerActivity extends Activity { List systemApps = new ArrayList<>(); systemApps.add(new GBDeviceApp(UUID.fromString("4dab81a6-d2fc-458a-992c-7a1f3b96a970"), "Sports (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); systemApps.add(new GBDeviceApp(UUID.fromString("cf1e816a-9db0-4511-bbb8-f60c48ca8fac"), "Golf (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); - if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getFirmwareVersion()))) { + if (mGBDevice != null && !"aplite".equals(PebbleUtils.getPlatformName(mGBDevice.getHardwareVersion()))) { systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM)); } From b3590fed358afed180f48d7faceb9677a0836952 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 28 Mar 2016 23:56:20 +0200 Subject: [PATCH 167/274] For simplicity hide some internal states from the user Display connecting->connected instead of connecting->connected->initializing->initialized --- .../freeyourgadget/gadgetbridge/impl/GBDevice.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 763e6375..1e46d978 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -53,7 +53,6 @@ public class GBDevice implements Parcelable { private BatteryState mBatteryState; private short mRssi = RSSI_UNKNOWN; private String mBusyTask; - private String infoString; private List mDeviceInfos; public GBDevice(String address, String name, DeviceType deviceType) { @@ -204,23 +203,25 @@ public class GBDevice implements Parcelable { } public String getStateString() { + /* + * for simplicity the user wont see all internal states, just connecting -> connected + * instead of connecting->connected->initializing->initialized + */ switch (mState) { case NOT_CONNECTED: return GBApplication.getContext().getString(R.string.not_connected); case WAITING_FOR_RECONNECT: return GBApplication.getContext().getString(R.string.waiting_for_reconnect); case CONNECTING: - return GBApplication.getContext().getString(R.string.connecting); case CONNECTED: - return GBApplication.getContext().getString(R.string.connected); case INITIALIZING: - return GBApplication.getContext().getString(R.string.initializing); + return GBApplication.getContext().getString(R.string.connecting); case AUTHENTICATION_REQUIRED: return GBApplication.getContext().getString(R.string.authentication_required); case AUTHENTICATING: return GBApplication.getContext().getString(R.string.authenticating); case INITIALIZED: - return GBApplication.getContext().getString(R.string.initialized); + return GBApplication.getContext().getString(R.string.connected); } return GBApplication.getContext().getString(R.string.unknown_state); } From 834a727a39ec5d083866a9a59ae5e31361540171 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Tue, 29 Mar 2016 00:05:29 +0200 Subject: [PATCH 168/274] update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c5bc3d5..7986a69b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ###Changelog +####Version 0.9.3 (next) +* Pebble: Fix Pebble Health activation (was not available in the App Manager) +* Simplify connection state display (only connecting->connected) +* Small improvements to the pairing activity + ####Version 0.9.2 * Mi Band: Fix update of second (HR) firmware on Mi1S (#234) * Fix ordering issue of device infos being displayed From e931cf47d71da2c898054426525cd4e730fcc790 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Mar 2016 22:13:15 +0200 Subject: [PATCH 169/274] Need to pass '0' as parameter to mi band fw metadata info #234 --- .../devices/miband/operations/UpdateFirmwareOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3acff6e6..710f4a34 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 @@ -193,7 +193,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } else { LOG.info("is multi Mi Band firmware, sending fw2 (hr) first"); byte[] fw2Info = prepareFirmwareInfo(fw2Bytes, fw2OldVersion, fw2Version, fw2Checksum, 1, rebootWhenFinished /*, progress monitor */); - byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 1, rebootWhenFinished /*, progress monitor */); + byte[] fw1Info = prepareFirmwareInfo(fw1Bytes, fw1OldVersion, fw1Version, fw1Checksum, 0, rebootWhenFinished /*, progress monitor */); return new DoubleUpdateCoordinator(fw1Info, fw1Bytes, fw2Info, fw2Bytes, rebootWhenFinished); } } From f8c761068e99ad321e6cbb5b1cdca9253eff1481 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Mar 2016 22:41:21 +0200 Subject: [PATCH 170/274] Updated for 0.9.3 --- CHANGELOG.md | 3 ++- app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 6 ++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7986a69b..c691eebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ ###Changelog -####Version 0.9.3 (next) +####Version 0.9.3 * Pebble: Fix Pebble Health activation (was not available in the App Manager) * Simplify connection state display (only connecting->connected) * Small improvements to the pairing activity +* Mi Band 1S: Fix for mi band firmware update ####Version 0.9.2 * Mi Band: Fix update of second (HR) firmware on Mi1S (#234) diff --git a/app/build.gradle b/app/build.gradle index accb84ae..a1f7b997 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.2" - versionCode 46 + versionName "0.9.3" + versionCode 47 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 608a543c..0c25965e 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,11 @@ + + Pebble: Fix Pebble Health activation (was not available in the App Manager) + Simplify connection state display (only connecting->connected) + Small improvements to the pairing activity + Mi Band 1S: Fix for mi band firmware update + Mi Band: Fix update of second (HR) firmware on Mi1S (#234) Fix ordering issue of device infos being displayed From 5f72daa43a2f628c94ba192ddf3231b014fffd2f Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 29 Mar 2016 22:59:22 +0200 Subject: [PATCH 171/274] Add SVG launcher icon, closes #190 THANKS! --- app/src/main/res/drawable/ic_launcher.svg | 149 ++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 app/src/main/res/drawable/ic_launcher.svg diff --git a/app/src/main/res/drawable/ic_launcher.svg b/app/src/main/res/drawable/ic_launcher.svg new file mode 100644 index 00000000..03b47994 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + From cc7f5406efa1f24d5ca97267653ab1f1296e617a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 30 Mar 2016 21:48:42 +0200 Subject: [PATCH 172/274] Use low latency transfer mode for fw update #234 --- .../gadgetbridge/service/btle/AbstractBTLEOperation.java | 4 ++++ .../devices/miband/operations/UpdateFirmwareOperation.java | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java index b7a7e938..258199ad 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java @@ -113,6 +113,10 @@ public abstract class AbstractBTLEOperation return operationStatus == OperationStatus.RUNNING; } + public boolean isOperationFinished() { + return operationStatus == OperationStatus.FINISHED; + } + public T getSupport() { return mSupport; } 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 710f4a34..c953e663 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 @@ -60,6 +60,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } private void done() { + LOG.info("Operation done."); updateCoordinator = null; operationFinished(); unsetBusy(); @@ -273,6 +274,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { try { TransactionBuilder builder = performInitialized("send firmware packet"); + getSupport().setLowLatency(builder); for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); @@ -319,6 +321,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { public boolean sendFwInfo() { try { TransactionBuilder builder = performInitialized("send firmware info"); + getSupport().setLowLatency(builder); builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo()); builder.add(new FirmwareInfoSucceededAction()); @@ -440,7 +443,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { private class FirmwareInfoSucceededAction extends PlainAction { @Override public boolean run(BluetoothGatt gatt) { - firmwareInfoSent = true; + if (isOperationRunning()) { + firmwareInfoSent = true; + } return true; } } From ffc006c21cddc86b7f1df472c56845a781ffa583 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 30 Mar 2016 21:56:00 +0200 Subject: [PATCH 173/274] Fix ordering problem with firmwareInfoSent state variable #234 --- .../devices/miband/operations/UpdateFirmwareOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c953e663..35072d56 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 @@ -323,8 +323,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { TransactionBuilder builder = performInitialized("send firmware info"); getSupport().setLowLatency(builder); builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); + builder.add(new FirmwareInfoSentAction()); // Note: *before* actually sending the info, otherwise it's too late! builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo()); - builder.add(new FirmwareInfoSucceededAction()); builder.queue(getQueue()); return true; } catch (IOException e) { @@ -440,7 +440,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } } - private class FirmwareInfoSucceededAction extends PlainAction { + private class FirmwareInfoSentAction extends PlainAction { @Override public boolean run(BluetoothGatt gatt) { if (isOperationRunning()) { From 776a7432854c17f7be3ebb50d8e901c748bbc806 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 30 Mar 2016 22:06:03 +0200 Subject: [PATCH 174/274] Move svg file to another place to fix build --- app/src/main/res/drawable/ic_launcher.svg | 149 ---------------------- 1 file changed, 149 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_launcher.svg diff --git a/app/src/main/res/drawable/ic_launcher.svg b/app/src/main/res/drawable/ic_launcher.svg deleted file mode 100644 index 03b47994..00000000 --- a/app/src/main/res/drawable/ic_launcher.svg +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - From 4631df67ac27558f767c5da1dec473d28348b50e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 30 Mar 2016 22:53:08 +0200 Subject: [PATCH 175/274] Some more logging + add svg launcher again (somehow it was not added again) --- app/src/main/assets/ic_launcher.svg | 149 ++++++++++++++++++ .../gadgetbridge/devices/InstallHandler.java | 2 +- .../operations/UpdateFirmwareOperation.java | 2 + 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 app/src/main/assets/ic_launcher.svg diff --git a/app/src/main/assets/ic_launcher.svg b/app/src/main/assets/ic_launcher.svg new file mode 100644 index 00000000..03b47994 --- /dev/null +++ b/app/src/main/assets/ic_launcher.svg @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java index 1314dc0e..e83815e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java @@ -28,7 +28,7 @@ public interface InstallHandler { void validateInstallation(InstallActivity installActivity, GBDevice device); /** - * Allows device specivic code to be execute just before the installation starts + * Allows device specific code to be executed just before the installation starts */ void onStartInstall(GBDevice device); } 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 35072d56..87723639 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 @@ -107,6 +107,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { done(); } firmwareInfoSent = false; + } else { + LOG.warn("firmwareInfoSent is false -- not sending firmware data even though we got meta data success notification"); } break; case MiBandService.NOTIFY_FW_CHECK_FAILED: From 66c1b3f178080fcb475e02f9953ea55462a8b9d1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 31 Mar 2016 21:39:51 +0200 Subject: [PATCH 176/274] Relax check for Mi1S device detection #234 --- .../gadgetbridge/service/devices/miband/DeviceInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 21798878..1072ff50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -109,7 +109,7 @@ public class DeviceInfo extends AbstractInfo { public boolean isMili1S() { // TODO: this is probably not quite correct, but hopefully sufficient for early 1S support - return feature == 4 && appearance == 0 || feature == 4 && hwVersion == 4; + return (feature == 4 && appearance == 0) || hwVersion == 4; } public String getHwVersion() { From 6f97b8c1e507e3e6391c951ffe5738846bd33133 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 31 Mar 2016 21:54:09 +0200 Subject: [PATCH 177/274] Log the date that we receive from the Mi Band --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 733b89ac..e1656924 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 @@ -725,6 +725,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { handleBatteryInfo(characteristic.getValue(), status); } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { logHeartrate(characteristic.getValue()); + } else if (MiBandService.UUID_CHARACTERISTIC_DATE_TIME.equals(characteristicUUID)) { + logDate(characteristic.getValue()); } else { LOG.info("Unhandled characteristic read: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -756,6 +758,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + public void logDate(byte[] value) { + GregorianCalendar calendar = MiBandDateConverter.rawBytesToCalendar(value); + LOG.info("Got Mi Band Date: " + DateTimeUtils.formatDateTime(calendar.getTime())); + } + public void logHeartrate(byte[] value) { LOG.info("Got heartrate:"); if (value.length == 2 && value[0] == 6) { From ea5c6a0848f231129428a1802f9c5e1453cca953 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 31 Mar 2016 21:57:36 +0200 Subject: [PATCH 178/274] Log ignored notifications when updateCoordinator is null --- .../devices/miband/operations/UpdateFirmwareOperation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 87723639..0d8e26ec 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 @@ -94,7 +94,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { return; } if (updateCoordinator == null) { - LOG.error("received notification when updateCoordinator is null, ignoring!"); + LOG.error("received notification when updateCoordinator is null, ignoring (notification content follows):"); + getSupport().logMessageContent(value); return; } From 72258c178ca7e246b29824b39cd862b7d32fd6f0 Mon Sep 17 00:00:00 2001 From: Christian Fischer Date: Sat, 2 Apr 2016 16:08:36 +0200 Subject: [PATCH 179/274] fix in string represantation conversion --- .../gadgetbridge/activities/AbstractSettingsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index d12fee96..4d3c48a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -33,7 +33,7 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { } public void updateSummary(Preference preference, Object value) { - String stringValue = value.toString(); + String stringValue = String.valueOf(value); if (preference instanceof ListPreference) { // For list preferences, look up the correct display value in From 20aa7d9ad9cbe64c206b32369039e56470510a92 Mon Sep 17 00:00:00 2001 From: Christian Fischer Date: Sat, 2 Apr 2016 16:09:30 +0200 Subject: [PATCH 180/274] add preference to set hartrate sleep detection --- .../devices/miband/MiBandConst.java | 2 ++ .../devices/miband/MiBandCoordinator.java | 5 ++++ .../miband/MiBandPreferencesActivity.java | 2 ++ .../service/devices/miband/MiBandSupport.java | 25 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/miband_preferences.xml | 4 +++ 6 files changed, 40 insertions(+) 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 6f3aa77f..f3cc3182 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 @@ -15,6 +15,8 @@ public final class MiBandConst { public static final String PREF_MIBAND_FITNESS_GOAL = "mi_fitness_goal"; public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer"; public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar"; + public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection"; + public static final String ORIGIN_SMS = "sms"; 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 3ecf4c65..86ea9330 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 @@ -135,6 +135,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return location; } + public static boolean getHeartrateSleepSupport(String miBandAddress) throws IllegalArgumentException { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + return prefs.getBoolean(MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, false); + } + public static int getFitnessGoal(String miBandAddress) throws IllegalArgumentException { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); return Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_FITNESS_GOAL, "10000")); 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 3cb8343f..76cca5cb 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 @@ -18,6 +18,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_DONT_ACK_TRANSFER; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_FITNESS_GOAL; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_WEARSIDE; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_USER_ALIAS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VIBRATION_COUNT; @@ -54,6 +55,7 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { PREF_MIBAND_FITNESS_GOAL, PREF_MIBAND_DONT_ACK_TRANSFER, PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, + PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_INCOMING_CALL), 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 733b89ac..a72e23db 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 @@ -109,6 +109,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { .sendUserInfo(builder) .checkAuthenticationNeeded(builder, getDevice()) .setWearLocation(builder) + .setHeartrateSleepSupport(builder) .setFitnessGoal(builder) .enableFurtherNotifications(builder, true) .setCurrentTime(builder) @@ -368,6 +369,30 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return this; } + /** + * Part of device initialization process. Do not call manually. + * + * @param transaction + * @return + */ + private MiBandSupport setHeartrateSleepSupport(TransactionBuilder transaction) { + LOG.info("Attempting to set heartrate sleep support..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + if (characteristic != null) { + if(MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { + LOG.info("Enabling heartrate sleep support..."); + transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementSleep); + } + else { + LOG.info("Disabling heartrate sleep support..."); + transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); + } + } else { + LOG.info("Unable to set Wear Location"); + } + return this; + } + private void performDefaultNotification(String task, short repeat, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3bcbddd8..5c06b5a5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -213,6 +213,8 @@ Incompatible firmware This firmware is not compatible with the device Alarms to reserve for upcoming events + Use Heartrate Sensor to improve sleep detection + waiting for reconnect Reinstall diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 65997e95..042cd8e1 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -30,6 +30,10 @@ android:maxLength="1" android:digits="0123" android:title="@string/miband_prefs_reserve_alarm_calendar" /> + Date: Sat, 2 Apr 2016 16:11:51 +0200 Subject: [PATCH 181/274] test if heartrate is supported before writing preferences --- .../service/devices/miband/MiBandSupport.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) 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 a72e23db..a3367180 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 @@ -376,20 +376,24 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { * @return */ private MiBandSupport setHeartrateSleepSupport(TransactionBuilder transaction) { - LOG.info("Attempting to set heartrate sleep support..."); - BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); - if (characteristic != null) { - if(MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { - LOG.info("Enabling heartrate sleep support..."); - transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementSleep); + if (supportsHeartRate()) { + LOG.info("Attempting to set heartrate sleep support..."); + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + if (characteristic != null) { + if(MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { + LOG.info("Enabling heartrate sleep support..."); + transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementSleep); + } + else { + LOG.info("Disabling heartrate sleep support..."); + transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); + } + } else { + LOG.info("Unable to set Heartrate sleep support"); } - else { - LOG.info("Disabling heartrate sleep support..."); - transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); - } - } else { - LOG.info("Unable to set Wear Location"); - } + + } else + GB.toast(getContext(), "Heart rate is not supported on this device", Toast.LENGTH_LONG, GB.ERROR); return this; } From 0e495359663b84f87c63c976a1e0fd7d8443e5ff Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 2 Apr 2016 22:24:33 +0200 Subject: [PATCH 182/274] Fix progress during fw update #234 --- .../devices/miband/operations/UpdateFirmwareOperation.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 0d8e26ec..59b042db 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 @@ -272,10 +272,11 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { final int packetLength = 20; int packets = len / packetLength; - // going from 0 to len - int firmwareProgress = 0; try { + // going from 0 to len + int firmwareProgress = 0; + TransactionBuilder builder = performInitialized("send firmware packet"); getSupport().setLowLatency(builder); for (int i = 0; i < packets; i++) { @@ -284,7 +285,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk); firmwareProgress += packetLength; - int progressPercent = (int) (((float) firmwareProgress) / len) * 100; + int progressPercent = (int) ((((float) firmwareProgress) / len) * 100); if ((i > 0) && (i % 50 == 0)) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, progressPercent, getContext())); From 2d10c11005c242646ab31aafa4dcb7776ce48471 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 2 Apr 2016 22:35:37 +0200 Subject: [PATCH 183/274] Log the length of the bytes written --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e1656924..1210c108 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 @@ -896,7 +896,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { if (status != BluetoothGatt.GATT_SUCCESS) { LOG.warn("Could not write to the control point."); } - LOG.info("handleControlPoint write status:" + status); + LOG.info("handleControlPoint write status:" + status + "; length: " + (value != null ? value.length : "(null)")); if (value != null) { for (byte b : value) { From 7a224243a34c914ce4cb27e671d3e9bbf798e098 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 00:49:54 +0200 Subject: [PATCH 184/274] Try to quit Gadgetbridge by stopping the service --- .../gadgetbridge/externalevents/NotificationListener.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index d4f6ed63..079f0132 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; @@ -54,6 +55,9 @@ public class NotificationListener extends NotificationListenerService { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { + case GBApplication.ACTION_QUIT: + stopSelf(); + break; case ACTION_MUTE: case ACTION_OPEN: { StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications(); @@ -130,6 +134,7 @@ public class NotificationListener extends NotificationListenerService { public void onCreate() { super.onCreate(); IntentFilter filterLocal = new IntentFilter(); + filterLocal.addAction(GBApplication.ACTION_QUIT); filterLocal.addAction(ACTION_OPEN); filterLocal.addAction(ACTION_DISMISS); filterLocal.addAction(ACTION_DISMISS_ALL); From a4919789ca21342358be58e8f440791a3a18655a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 00:50:45 +0200 Subject: [PATCH 185/274] Add some progress to firmware updating #271 #234 Also: remove the low latency mode for firmware update, because my Mi1S simply disconnects then. Still missing in the view: device disconnects --- .../activities/FwAppInstallerActivity.java | 46 ++++++++++++++++--- .../adapter/ItemWithDetailsAdapter.java | 21 ++++++++- .../GBDeviceEventDisplayMessage.java | 21 +++++++++ .../service/AbstractDeviceSupport.java | 12 +++++ .../operations/UpdateFirmwareOperation.java | 26 +++++++---- .../freeyourgadget/gadgetbridge/util/GB.java | 6 ++- .../main/res/layout/activity_appinstaller.xml | 8 ++++ .../res/layout/item_with_details_small.xml | 42 +++++++++++++++++ 8 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java create mode 100644 app/src/main/res/layout/item_with_details_small.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index 0c7b88cd..7f8d507f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.adapter.ItemWithDetailsAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; public class FwAppInstallerActivity extends Activity implements InstallActivity { private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class); + private static final String ITEM_DETAILS = "details"; private TextView fwAppInstallTextView; private Button installButton; @@ -45,13 +47,22 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity private InstallHandler installHandler; private boolean mayConnect; + private ProgressBar mProgressBar; + private ListView itemListView; + private final List mItems = new ArrayList<>(); + private ItemWithDetailsAdapter mItemAdapter; + + private ListView detailsListView; + private ItemWithDetailsAdapter mDetailsItemAdapter; + private ArrayList mDetails = new ArrayList<>(); + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(GBApplication.ACTION_QUIT)) { + if (GBApplication.ACTION_QUIT.equals(action)) { finish(); - } else if (action.equals(GBDevice.ACTION_DEVICE_CHANGED)) { + } else if (GBDevice.ACTION_DEVICE_CHANGED.equals(action)) { device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE); if (device != null) { refreshBusyState(device); @@ -67,13 +78,13 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity validateInstallation(); } } + } else if (GB.ACTION_DISPLAY_MESSAGE.equals(action)) { + String message = intent.getStringExtra(GB.DISPLAY_MESSAGE_MESSAGE); + int severity = intent.getIntExtra(GB.DISPLAY_MESSAGE_SEVERITY, GB.INFO); + addMessage(message, severity); } } }; - private ProgressBar mProgressBar; - private ListView itemListView; - private final List mItems = new ArrayList<>(); - private ItemWithDetailsAdapter mItemAdapter; private void refreshBusyState(GBDevice dev) { if (dev.isConnecting() || dev.isBusy()) { @@ -107,6 +118,13 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity if (dev != null) { device = dev; } + if (savedInstanceState != null) { + mDetails = savedInstanceState.getParcelableArrayList(ITEM_DETAILS); + if (mDetails == null) { + mDetails = new ArrayList<>(); + } + } + mayConnect = true; itemListView = (ListView) findViewById(R.id.itemListView); mItemAdapter = new ItemWithDetailsAdapter(this, mItems); @@ -114,10 +132,15 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity fwAppInstallTextView = (TextView) findViewById(R.id.infoTextView); installButton = (Button) findViewById(R.id.installButton); mProgressBar = (ProgressBar) findViewById(R.id.installProgressBar); + detailsListView = (ListView) findViewById(R.id.detailsListView); + mDetailsItemAdapter = new ItemWithDetailsAdapter(this, mDetails); + mDetailsItemAdapter.setSize(ItemWithDetailsAdapter.SIZE_SMALL); + detailsListView.setAdapter(mDetailsItemAdapter); setInstallEnabled(false); IntentFilter filter = new IntentFilter(); filter.addAction(GBApplication.ACTION_QUIT); filter.addAction(GBDevice.ACTION_DEVICE_CHANGED); + filter.addAction(GB.ACTION_DISPLAY_MESSAGE); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); installButton.setOnClickListener(new View.OnClickListener() { @@ -145,6 +168,12 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity } } + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelableArrayList(ITEM_DETAILS, mDetails); + } + private InstallHandler findInstallHandlerFor(Uri uri) { for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) { InstallHandler handler = coordinator.findInstallHandler(uri, this); @@ -195,4 +224,9 @@ public class FwAppInstallerActivity extends Activity implements InstallActivity mItems.add(item); mItemAdapter.notifyDataSetChanged(); } + + private void addMessage(String message, int severity) { + mDetails.add(new GenericItem(message)); + mDetailsItemAdapter.notifyDataSetChanged(); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java index 92448d9c..dcfee994 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ItemWithDetailsAdapter.java @@ -18,8 +18,12 @@ import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails; */ public class ItemWithDetailsAdapter extends ArrayAdapter { + public static final int SIZE_SMALL = 1; + public static final int SIZE_MEDIUM = 2; + public static final int SIZE_LARGE = 3; private final Context context; private boolean horizontalAlignment; + private int size = SIZE_MEDIUM; public ItemWithDetailsAdapter(Context context, List items) { super(context, 0, items); @@ -42,7 +46,14 @@ public class ItemWithDetailsAdapter extends ArrayAdapter { if (horizontalAlignment) { view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false); } else { - view = inflater.inflate(R.layout.item_with_details, parent, false); + switch (size) { + case SIZE_SMALL: + view = inflater.inflate(R.layout.item_with_details_small, parent, false); + break; + default: + view = inflater.inflate(R.layout.item_with_details, parent, false); + break; + } } } ImageView iconView = (ImageView) view.findViewById(R.id.item_image); @@ -55,4 +66,12 @@ public class ItemWithDetailsAdapter extends ArrayAdapter { return view; } + + public void setSize(int size) { + this.size = size; + } + + public int getSize() { + return size; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java new file mode 100644 index 00000000..33250737 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java @@ -0,0 +1,21 @@ +package nodomain.freeyourgadget.gadgetbridge.deviceevents; + +public class GBDeviceEventDisplayMessage { + public String message; + public int duration; + public int severity; + + /** + * An event for displaying a message to the user. How the message is displayed + * is a detail of the current activity, which needs to listen to the Intent + * GB.ACTION_DISPLAY_MESSAGE. + * @param message + * @param duration + * @param severity + */ + public GBDeviceEventDisplayMessage(String message, int duration, int severity) { + this.message = message; + this.duration = duration; + this.severity = severity; + } +} 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 ad88c179..d33b9dd9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -1,5 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.service; +import android.app.Application; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -32,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot; @@ -280,4 +282,14 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { gbDevice.sendDeviceUpdateIntent(context); } + public void handleGBDeviceEvent(GBDeviceEventDisplayMessage message) { + GB.log(message.message, message.severity, null); + + Intent messageIntent = new Intent(GB.ACTION_DISPLAY_MESSAGE); + messageIntent.putExtra(GB.DISPLAY_MESSAGE_MESSAGE, message.message); + messageIntent.putExtra(GB.DISPLAY_MESSAGE_DURATION, message.duration); + messageIntent.putExtra(GB.DISPLAY_MESSAGE_SEVERITY, message.severity); + + LocalBroadcastManager.getInstance(context).sendBroadcast(messageIntent); + } } 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 59b042db..03c40517 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 @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; import android.net.Uri; import android.widget.Toast; @@ -13,6 +14,7 @@ import java.util.Arrays; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -53,7 +55,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { updateCoordinator.initNextOperation(); if (!updateCoordinator.sendFwInfo()) { - GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); + displayMessage(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); done(); } //the firmware will be sent by the notification listener if the band confirms that the metadata are ok. @@ -102,9 +104,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { switch (value[0]) { case MiBandService.NOTIFY_FW_CHECK_SUCCESS: if (firmwareInfoSent) { - GB.toast(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO); + displayMessage(getContext(), "Firmware metadata successfully sent.", Toast.LENGTH_LONG, GB.INFO); if (!updateCoordinator.sendFwData()) { - GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); + displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); done(); } firmwareInfoSent = false; @@ -113,20 +115,20 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } break; case MiBandService.NOTIFY_FW_CHECK_FAILED: - GB.toast(getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR); + displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_metadata_updateproblem), Toast.LENGTH_LONG, GB.ERROR); firmwareInfoSent = false; done(); break; case MiBandService.NOTIFY_FIRMWARE_UPDATE_SUCCESS: if (updateCoordinator.initNextOperation()) { - GB.toast(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO); + displayMessage(getContext(), "Heart Rate Firmware successfully updated, now updating Mi Band Firmware", Toast.LENGTH_LONG, GB.INFO); if (!updateCoordinator.sendFwInfo()) { - GB.toast(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); + displayMessage(getContext(), "Error sending firmware info, aborting.", Toast.LENGTH_LONG, GB.ERROR); done(); } break; } else if (updateCoordinator.needsReboot()) { - GB.toast(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO); + displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_update_complete_rebooting), Toast.LENGTH_LONG, GB.INFO); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_update_complete), false, 100, getContext()); getSupport().onReboot(); } else { @@ -136,7 +138,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { break; case MiBandService.NOTIFY_FIRMWARE_UPDATE_FAILED: //TODO: the firmware transfer failed, but the miband should be still functional with the old firmware. What should we do? - GB.toast(getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); + displayMessage(getContext(), getContext().getString(R.string.updatefirmwareoperation_updateproblem_do_not_reboot), Toast.LENGTH_LONG, GB.ERROR); GB.updateInstallNotification(getContext().getString(R.string.updatefirmwareoperation_write_failed), false, 0, getContext()); done(); break; @@ -147,6 +149,10 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { } } + private void displayMessage(Context context, String message, int duration, int severity) { + getSupport().handleGBDeviceEvent(new GBDeviceEventDisplayMessage(message, duration, severity)); + } + /** * Prepare the MiBand to receive the new firmware data. * Some information about the new firmware version have to be pushed to the MiBand before sending @@ -278,7 +284,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); - getSupport().setLowLatency(builder); +// getSupport().setLowLatency(builder); for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); @@ -325,7 +331,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { public boolean sendFwInfo() { try { TransactionBuilder builder = performInitialized("send firmware info"); - getSupport().setLowLatency(builder); +// getSupport().setLowLatency(builder); builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.updating_firmware), getContext())); builder.add(new FirmwareInfoSentAction()); // Note: *before* actually sending the info, otherwise it's too late! builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), getFirmwareInfo()); 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 c6f87cff..a20e0f2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -39,6 +39,10 @@ public class GB { public static final int INFO = 1; public static final int WARN = 2; public static final int ERROR = 3; + public static final String ACTION_DISPLAY_MESSAGE = "GB_Display_Message"; + public static final String DISPLAY_MESSAGE_MESSAGE = "message"; + public static final String DISPLAY_MESSAGE_DURATION = "duration"; + public static final String DISPLAY_MESSAGE_SEVERITY = "severity"; public static GBEnvironment environment; public static Notification createNotification(String text, Context context) { @@ -225,7 +229,7 @@ public class GB { } } - private static void log(String message, int severity, Throwable ex) { + public static void log(String message, int severity, Throwable ex) { switch (severity) { case INFO: LOG.info(message, ex); diff --git a/app/src/main/res/layout/activity_appinstaller.xml b/app/src/main/res/layout/activity_appinstaller.xml index 8846623b..eb13abc1 100644 --- a/app/src/main/res/layout/activity_appinstaller.xml +++ b/app/src/main/res/layout/activity_appinstaller.xml @@ -62,6 +62,14 @@ android:layout_below="@+id/installProgressBar" android:layout_marginTop="10dp" /> + + + + + + + + + + + + + + + \ No newline at end of file From 7ddfd35c35ae74598d1bd18eeb34538d8b69542f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Apr 2016 18:30:20 +0200 Subject: [PATCH 186/274] Pebble: auto connect on incoming notification or phone call if connection was lost unxpectedly before --- .../devices/pebble/PebbleIoThread.java | 11 +++-- .../service/devices/pebble/PebbleSupport.java | 44 ++++++++++++++++++- .../serial/AbstractSerialDeviceSupport.java | 4 +- 3 files changed, 53 insertions(+), 6 deletions(-) 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 b5bfe222..504a3629 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 @@ -200,11 +200,16 @@ public class PebbleIoThread extends GBDeviceIoThread { } mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false)); - gbDevice.setState(GBDevice.State.CONNECTED); - gbDevice.sendDeviceUpdateIntent(getContext()); mIsConnected = true; - write(mPebbleProtocol.encodeFirmwareVersionReq()); + if (originalState == GBDevice.State.WAITING_FOR_RECONNECT) { + gbDevice.setState(GBDevice.State.INITIALIZED); + } else { + gbDevice.setState(GBDevice.State.CONNECTED); + write(mPebbleProtocol.encodeFirmwareVersionReq()); + } + gbDevice.sendDeviceUpdateIntent(getContext()); + return true; } 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 66560c0d..2734a67c 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 @@ -10,7 +10,10 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; @@ -35,7 +38,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { @Override public boolean useAutoConnect() { - return false; + return true; } @Override @@ -71,6 +74,45 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { return (PebbleIoThread) super.getDeviceIOThread(); } + private boolean reconnect() { + if (!isConnected() && useAutoConnect()) { + if (getDevice().getState() == GBDevice.State.WAITING_FOR_RECONNECT) { + gbDeviceIOThread.interrupt(); + gbDeviceIOThread = null; + if (!connect()) { + return false; + } + try { + Thread.sleep(2000); // this is about the time the connect takes, so the notification can come though + } catch (InterruptedException ignored) { + } + } + } + return true; + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + if (reconnect()) { + super.onNotification(notificationSpec); + } + } + + @Override + public void onSetCallState(String number, String name, ServiceCommand command) { + if (reconnect()) { + super.onSetCallState(number, name, command); + } + } + + @Override + public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + if (reconnect()) { + super.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); + } + } + + @Override public void onSetAlarms(ArrayList alarms) { //nothing to do ATM diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 5563de59..9801b8cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -29,8 +29,8 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport private static final Logger LOG = LoggerFactory.getLogger(AbstractDeviceSupport.class); - private GBDeviceProtocol gbDeviceProtocol; - private GBDeviceIoThread gbDeviceIOThread; + protected GBDeviceProtocol gbDeviceProtocol; + protected GBDeviceIoThread gbDeviceIOThread; /** * Factory method to create the device specific GBDeviceProtocol instance to be used. From 4389c1cca3dc8110a1e1169e19c783e3a36ff970 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 3 Apr 2016 18:36:30 +0200 Subject: [PATCH 187/274] Pebble: wait 4 seconds instead of 2 when notifications/calls trigger reconnection --- .../gadgetbridge/service/devices/pebble/PebbleSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2734a67c..f9fc5de8 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 @@ -83,7 +83,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { return false; } try { - Thread.sleep(2000); // this is about the time the connect takes, so the notification can come though + Thread.sleep(4000); // this is about the time the connect takes, so the notification can come though } catch (InterruptedException ignored) { } } From 804a85d31fcb74a3a8df5cff16688af64c0d8be3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 21:41:52 +0200 Subject: [PATCH 188/274] Small refactoring of BtLE actions --- .../btle/AbstractBTLEDeviceSupport.java | 1 + .../{ => actions}/CheckInitializedAction.java | 3 +- .../btle/actions/ConditionalWriteAction.java | 29 +++++++++++++++++++ .../service/btle/actions/WriteAction.java | 18 +++++++++--- .../CheckAuthenticationNeededAction.java | 12 ++++---- 5 files changed, 50 insertions(+), 13 deletions(-) rename app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/{ => actions}/CheckInitializedAction.java (85%) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java 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 051c8ce6..6361431a 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 @@ -16,6 +16,7 @@ import java.util.Set; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction; /** * Abstract base class for all devices connected through Bluetooth Low Energy (LE) aka diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/CheckInitializedAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java similarity index 85% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/CheckInitializedAction.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java index 21bc0be8..414d983f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/CheckInitializedAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java @@ -1,10 +1,9 @@ -package nodomain.freeyourgadget.gadgetbridge.service.btle; +package nodomain.freeyourgadget.gadgetbridge.service.btle.actions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; /** * A special action that is executed at the very front of the initialization diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java new file mode 100644 index 00000000..a7cb39ce --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java @@ -0,0 +1,29 @@ +package nodomain.freeyourgadget.gadgetbridge.service.btle.actions; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; + +public abstract class ConditionalWriteAction extends WriteAction { + public ConditionalWriteAction(BluetoothGattCharacteristic characteristic) { + super(characteristic, null); + } + + @Override + protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) { + byte[] conditionalValue = checkCondition(); + if (conditionalValue != null) { + return super.writeValue(gatt, characteristic, conditionalValue); + } + return true; + } + + /** + * Checks the condition whether the write shall happen or not. + * Returns the actual value to be written or null in case nothing shall be written. + * + * Note that returning null will not cause run() to return false, in other words, + * the rest of the queue will still be executed. + * @return the value to be written or null to not write anything + */ + protected abstract byte[] checkCondition(); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java index b4ec9548..5cecee06 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java @@ -22,16 +22,26 @@ public class WriteAction extends BtLEAction { @Override public boolean run(BluetoothGatt gatt) { - int properties = getCharacteristic().getProperties(); + BluetoothGattCharacteristic characteristic = getCharacteristic(); + int properties = characteristic.getProperties(); //TODO: expectsResult should return false if PROPERTY_WRITE_NO_RESPONSE is true, but this yelds to timing issues if ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0 || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0)) { - if (getCharacteristic().setValue(value)) { - return gatt.writeCharacteristic(getCharacteristic()); - } + return writeValue(gatt, characteristic, value); } return false; } + protected boolean writeValue(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, byte[] value) { + if (characteristic.setValue(value)) { + return gatt.writeCharacteristic(characteristic); + } + return false; + } + + protected final byte[] getValue() { + return value; + } + @Override public boolean expectsResult() { return true; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java index 6f25b163..8415c695 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java @@ -1,11 +1,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; -import android.bluetooth.BluetoothGatt; - import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.PlainAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction; -public class CheckAuthenticationNeededAction extends PlainAction { +public class CheckAuthenticationNeededAction extends AbortTransactionAction { private final GBDevice mDevice; public CheckAuthenticationNeededAction(GBDevice device) { @@ -14,14 +12,14 @@ public class CheckAuthenticationNeededAction extends PlainAction { } @Override - public boolean run(BluetoothGatt gatt) { + protected boolean shouldAbort() { // the state is set in MiBandSupport.handleNotificationNotif() switch (mDevice.getState()) { case AUTHENTICATION_REQUIRED: // fall through case AUTHENTICATING: - return false; // abort the whole thing + return true; // abort the whole thing default: - return true; + return false; } } } From b129844169e54283fd72bd57491a5cdeba35ebd4 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 22:38:06 +0200 Subject: [PATCH 189/274] Small fixes to PR 273 #232 - dynamically toggle hr sleep support when preference changes - check hr support dynaically after device info is available to avoid false error message --- .../gadgetbridge/devices/EventHandler.java | 1 + .../miband/MiBandPreferencesActivity.java | 10 +++- .../gadgetbridge/impl/GBDeviceService.java | 9 +++- .../gadgetbridge/model/DeviceService.java | 3 +- .../service/DeviceCommunicationService.java | 13 +++-- .../service/ServiceDeviceSupport.java | 8 +++ .../service/devices/miband/MiBandSupport.java | 52 +++++++++++-------- .../serial/AbstractSerialDeviceSupport.java | 6 +++ .../service/serial/GBDeviceProtocol.java | 2 + .../service/TestDeviceSupport.java | 5 ++ 10 files changed, 82 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index f03413af..af6f9a5e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -48,4 +48,5 @@ public interface EventHandler { void onScreenshotReq(); + void onEnableHeartRateSleepSupport(boolean enable); } 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 76cca5cb..7ef19b80 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 @@ -5,6 +5,7 @@ import android.os.Bundle; import android.preference.Preference; import android.support.v4.content.LocalBroadcastManager; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractSettingsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; @@ -44,6 +45,14 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { }); + final Preference enableHeartrateSleepSupport = findPreference(PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION); + enableHeartrateSleepSupport.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + GBApplication.deviceService().onEnableHeartRateSleepSupport(Boolean.TRUE.equals(newVal)); + return true; + } + }); } @Override @@ -55,7 +64,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { PREF_MIBAND_FITNESS_GOAL, PREF_MIBAND_DONT_ACK_TRANSFER, PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, - PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_INCOMING_CALL), 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 62d7f15c..17b8614f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -205,7 +205,14 @@ public class GBDeviceService implements DeviceService { @Override public void onEnableRealtimeSteps(boolean enable) { Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_STEPS) - .putExtra(EXTRA_ENABLE_REALTIME_STEPS, enable); + .putExtra(EXTRA_BOOLEAN_ENABLE, enable); + invokeService(intent); + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + Intent intent = createIntent().setAction(ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT) + .putExtra(EXTRA_BOOLEAN_ENABLE, enable); invokeService(intent); } } 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 8217f6d4..7c38088f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -33,6 +33,7 @@ public interface DeviceService extends EventHandler { String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms"; String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps"; String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps"; + String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support"; String EXTRA_DEVICE_ADDRESS = "device_address"; String EXTRA_NOTIFICATION_BODY = "notification_body"; String EXTRA_NOTIFICATION_FLAGS = "notification_flags"; @@ -58,7 +59,7 @@ public interface DeviceService extends EventHandler { String EXTRA_URI = "uri"; String EXTRA_ALARMS = "alarms"; String EXTRA_PERFORM_PAIR = "perform_pair"; - String EXTRA_ENABLE_REALTIME_STEPS = "enable_realtime_steps"; + String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; String EXTRA_REALTIME_STEPS = "realtime_steps"; String EXTRA_TIMESTAMP = "timestamp"; 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 a9261f7b..68ecefee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -44,6 +44,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CA import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE; @@ -66,7 +67,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ENABLE_REALTIME_STEPS; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOOLEAN_ENABLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; @@ -331,10 +332,16 @@ public class DeviceCommunicationService extends Service { ArrayList alarms = intent.getParcelableArrayListExtra(EXTRA_ALARMS); mDeviceSupport.onSetAlarms(alarms); break; - case ACTION_ENABLE_REALTIME_STEPS: - boolean enable = intent.getBooleanExtra(EXTRA_ENABLE_REALTIME_STEPS, false); + case ACTION_ENABLE_REALTIME_STEPS: { + boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false); mDeviceSupport.onEnableRealtimeSteps(enable); break; + } + case ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT: { + boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false); + mDeviceSupport.onEnableHeartRateSleepSupport(enable); + break; + } } return START_STICKY; 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 993b0319..a3994b47 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -241,4 +241,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onEnableRealtimeSteps(enable); } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + if (checkBusy("enable heartrate sleep support: " + enable)) { + return; + } + delegate.onEnableHeartRateSleepSupport(enable); + } } 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 136cb5f1..f6dd7ce9 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 @@ -44,6 +44,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.actions.WriteAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.FetchActivityOperation; @@ -369,31 +370,40 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { return this; } + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + try { + TransactionBuilder builder = performInitialized("enable heart rate sleep support: " + enable); + setHeartrateSleepSupport(builder); + builder.queue(getQueue()); + } catch (IOException e) { + GB.toast(getContext(), "Error toggling heart rate sleep support: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + /** * Part of device initialization process. Do not call manually. - * - * @param transaction - * @return + * @param builder */ - private MiBandSupport setHeartrateSleepSupport(TransactionBuilder transaction) { - if (supportsHeartRate()) { - LOG.info("Attempting to set heartrate sleep support..."); - BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); - if (characteristic != null) { - if(MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { - LOG.info("Enabling heartrate sleep support..."); - transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementSleep); + private MiBandSupport setHeartrateSleepSupport(TransactionBuilder builder) { + BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT); + if (characteristic != null) { + builder.add(new ConditionalWriteAction(characteristic) { + @Override + protected byte[] checkCondition() { + if (!supportsHeartRate()) { + return null; + } + if (MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress())) { + LOG.info("Enabling heartrate sleep support..."); + return startHeartMeasurementSleep; + } else { + LOG.info("Disabling heartrate sleep support..."); + return stopHeartMeasurementSleep; + } } - else { - LOG.info("Disabling heartrate sleep support..."); - transaction.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); - } - } else { - LOG.info("Unable to set Heartrate sleep support"); - } - - } else - GB.toast(getContext(), "Heart rate is not supported on this device", Toast.LENGTH_LONG, GB.ERROR); + }); + } return this; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 9801b8cb..a871cac6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -178,4 +178,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeSteps(enable); sendToDevice(bytes); } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable); + sendToDevice(bytes); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index b08b105d..5cd9ef82 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -60,6 +60,8 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeEnableHeartRateSleepSupport(boolean enable) { return null; } + public GBDeviceEvent[] decodeResponse(byte[] responseData) { return null; } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 6528b613..b3604199 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -124,4 +124,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport { public void onEnableRealtimeSteps(boolean enable) { } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } } From 59c3970008734cf9104b461cebee2a864540c9ca Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 23:01:58 +0200 Subject: [PATCH 190/274] Reuse characteristic objects #234 --- .../miband/operations/UpdateFirmwareOperation.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 03c40517..4bb7d6ba 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 @@ -278,7 +278,8 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { final int packetLength = 20; int packets = len / packetLength; - + BluetoothGattCharacteristic characteristicControlPoint = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); + BluetoothGattCharacteristic characteristicFWData = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA); try { // going from 0 to len int firmwareProgress = 0; @@ -288,23 +289,23 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), fwChunk); + builder.write(characteristicFWData, fwChunk); firmwareProgress += packetLength; int progressPercent = (int) ((((float) firmwareProgress) / len) * 100); if ((i > 0) && (i % 50 == 0)) { - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); + builder.write(characteristicControlPoint, new byte[]{MiBandService.COMMAND_SYNC}); builder.add(new SetProgressAction(getContext().getString(R.string.updatefirmwareoperation_update_in_progress), true, progressPercent, getContext())); } } if (firmwareProgress < len) { byte[] lastChunk = Arrays.copyOfRange(fwbytes, packets * packetLength, len); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_FIRMWARE_DATA), lastChunk); + builder.write(characteristicFWData, lastChunk); firmwareProgress = len; } - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_SYNC}); + builder.write(characteristicControlPoint, new byte[]{MiBandService.COMMAND_SYNC}); builder.queue(getQueue()); } catch (IOException ex) { From 3e3cf462a693a11d33b6b92fc43586ecfaed5888 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 3 Apr 2016 23:32:15 +0200 Subject: [PATCH 191/274] Attempt to re-enable automatic reconnect (autosensing) #249 (now that initializing device works again) --- .../freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b74cdbfd..b31ba1d2 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 @@ -158,7 +158,7 @@ public final class BtLEQueue { mBluetoothAdapter.cancelDiscovery(); BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress()); synchronized (mGattMonitor) { - mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback); + mBluetoothGatt = remoteDevice.connectGatt(mContext, true, internalGattCallback); // result = mBluetoothGatt.connect(); } boolean result = mBluetoothGatt != null; From a15b327ff1c90a5133c1fd40eeb01bd03e738cdc Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 4 Apr 2016 20:08:34 +0200 Subject: [PATCH 192/274] Refactoring: get rid of ServiceCommand, use new CallSpec and MusicSpec to pass Call and Music info --- .../activities/DebugActivity.java | 46 ++++++++++--------- .../GBDeviceEventDisplayMessage.java | 1 + .../gadgetbridge/devices/EventHandler.java | 8 ++-- .../devices/miband/MiBandConst.java | 1 - .../externalevents/MusicPlaybackReceiver.java | 12 +++-- .../externalevents/NotificationListener.java | 1 - .../externalevents/PhoneCallReceiver.java | 21 +++++---- .../gadgetbridge/impl/GBDeviceService.java | 23 +++++----- .../gadgetbridge/model/CallSpec.java | 15 ++++++ .../gadgetbridge/model/MusicSpec.java | 17 +++++++ .../gadgetbridge/model/ServiceCommand.java | 22 --------- .../service/AbstractDeviceSupport.java | 1 - .../service/DeviceCommunicationService.java | 34 +++++++++----- .../service/ServiceDeviceSupport.java | 11 +++-- .../btle/actions/ConditionalWriteAction.java | 3 +- .../service/devices/miband/MiBandSupport.java | 12 +++-- .../devices/pebble/PebbleProtocol.java | 14 +++--- .../service/devices/pebble/PebbleSupport.java | 11 +++-- .../serial/AbstractSerialDeviceSupport.java | 11 +++-- .../service/serial/GBDeviceProtocol.java | 7 +-- .../service/TestDeviceSupport.java | 8 ++-- 21 files changed, 158 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ServiceCommand.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 3d438bcd..dc7eb2f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -29,9 +29,10 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -118,20 +119,20 @@ public class DebugActivity extends Activity { incomingCallButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GBApplication.deviceService().onSetCallState( - editContent.getText().toString(), - null, - ServiceCommand.CALL_INCOMING); + CallSpec callSpec = new CallSpec(); + callSpec.command = CallSpec.CALL_INCOMING; + callSpec.number = editContent.getText().toString(); + GBApplication.deviceService().onSetCallState(callSpec); } }); outgoingCallButton = (Button) findViewById(R.id.outgoingCallButton); outgoingCallButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GBApplication.deviceService().onSetCallState( - editContent.getText().toString(), - null, - ServiceCommand.CALL_OUTGOING); + CallSpec callSpec = new CallSpec(); + callSpec.command = CallSpec.CALL_OUTGOING; + callSpec.number = editContent.getText().toString(); + GBApplication.deviceService().onSetCallState(callSpec); } }); @@ -139,20 +140,18 @@ public class DebugActivity extends Activity { startCallButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GBApplication.deviceService().onSetCallState( - null, - null, - ServiceCommand.CALL_START); + CallSpec callSpec = new CallSpec(); + callSpec.command = CallSpec.CALL_START; + GBApplication.deviceService().onSetCallState(callSpec); } }); endCallButton = (Button) findViewById(R.id.endCallButton); endCallButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GBApplication.deviceService().onSetCallState( - null, - null, - ServiceCommand.CALL_END); + CallSpec callSpec = new CallSpec(); + callSpec.command = CallSpec.CALL_END; + GBApplication.deviceService().onSetCallState(callSpec); } }); @@ -199,10 +198,15 @@ public class DebugActivity extends Activity { setMusicInfoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - GBApplication.deviceService().onSetMusicInfo( - editContent.getText().toString() + "(artist)", - editContent.getText().toString() + "(album)", - editContent.getText().toString() + "(track)", 20, 10, 2); + MusicSpec musicSpec = new MusicSpec(); + musicSpec.artist = editContent.getText().toString() + "(artist)"; + musicSpec.album = editContent.getText().toString() + "(album)"; + musicSpec.track = editContent.getText().toString() + "(track)"; + musicSpec.duration = 10; + musicSpec.trackCount = 5; + musicSpec.trackNr = 2; + + GBApplication.deviceService().onSetMusicInfo(musicSpec); } }); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java index 33250737..29e5ed12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java @@ -9,6 +9,7 @@ public class GBDeviceEventDisplayMessage { * An event for displaying a message to the user. How the message is displayed * is a detail of the current activity, which needs to listen to the Intent * GB.ACTION_DISPLAY_MESSAGE. + * * @param message * @param duration * @param severity diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index af6f9a5e..5ddad49e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -1,14 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import android.net.Uri; -import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; /** * Specifies all events that GadgetBridge intends to send to the gadget device. @@ -22,9 +22,9 @@ public interface EventHandler { void onSetAlarms(ArrayList alarms); - void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command); + void onSetCallState(CallSpec callSpec); - void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr); + void onSetMusicInfo(MusicSpec musicSpec); void onEnableRealtimeSteps(boolean enable); 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 f3cc3182..511de9dd 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 @@ -18,7 +18,6 @@ public final class MiBandConst { public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection"; - public static final String ORIGIN_SMS = "sms"; public static final String ORIGIN_INCOMING_CALL = "incoming_call"; public static final String ORIGIN_K9MAIL = "k9mail"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 1b452030..d910e75e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -8,12 +8,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; public class MusicPlaybackReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(MusicPlaybackReceiver.class); - private static String mLastSource; - @Override public void onReceive(Context context, Intent intent) { String artist = intent.getStringExtra("artist"); @@ -24,11 +23,16 @@ public class MusicPlaybackReceiver extends BroadcastReceiver { for (String key : bundle.keySet()) { Object value = bundle.get(key); LOG.info(String.format("%s %s (%s)", key, - value.toString(), value.getClass().getName())); + value != null ? value.toString() : "null", value != null ? value.getClass().getName() : "no class")); } */ LOG.info("Current track: " + artist + ", " + album + ", " + track); - GBApplication.deviceService().onSetMusicInfo(artist, album, track, 0, 0, 0); + MusicSpec musicSpec = new MusicSpec(); + musicSpec.artist = artist; + musicSpec.artist = album; + musicSpec.artist = track; + + GBApplication.deviceService().onSetMusicInfo(musicSpec); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 079f0132..124c0b99 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -26,7 +26,6 @@ import org.slf4j.LoggerFactory; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; 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 26513a06..010b5fb8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -8,7 +8,7 @@ import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; public class PhoneCallReceiver extends BroadcastReceiver { @@ -40,35 +40,38 @@ public class PhoneCallReceiver extends BroadcastReceiver { return; } - ServiceCommand callCommand = null; + int callCommand = CallSpec.CALL_UNDEFINED; switch (state) { case TelephonyManager.CALL_STATE_RINGING: mSavedNumber = number; - callCommand = ServiceCommand.CALL_INCOMING; + callCommand = CallSpec.CALL_INCOMING; break; case TelephonyManager.CALL_STATE_OFFHOOK: if (mLastState == TelephonyManager.CALL_STATE_RINGING) { - callCommand = ServiceCommand.CALL_START; + callCommand = CallSpec.CALL_START; } else { - callCommand = ServiceCommand.CALL_OUTGOING; + callCommand = CallSpec.CALL_OUTGOING; mSavedNumber = number; } break; case TelephonyManager.CALL_STATE_IDLE: if (mLastState == TelephonyManager.CALL_STATE_RINGING) { //missed call would be correct here - callCommand = ServiceCommand.CALL_END; + callCommand = CallSpec.CALL_END; } else { - callCommand = ServiceCommand.CALL_END; + callCommand = CallSpec.CALL_END; } break; } - if (callCommand != null) { + if (callCommand != CallSpec.CALL_UNDEFINED) { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); if ("never".equals(sharedPrefs.getString("notification_mode_calls", "always"))) { return; } - GBApplication.deviceService().onSetCallState(mSavedNumber, null, callCommand); + CallSpec callSpec = new CallSpec(); + callSpec.number = mSavedNumber; + callSpec.command = callCommand; + GBApplication.deviceService().onSetCallState(callSpec); } mLastState = state; } 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 17b8614f..add9f676 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -10,9 +10,10 @@ import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; public class GBDeviceService implements DeviceService { @@ -115,23 +116,23 @@ public class GBDeviceService implements DeviceService { } @Override - public void onSetCallState(String number, String name, ServiceCommand command) { + public void onSetCallState(CallSpec callSpec) { // name is actually ignored and provided by the service itself... Intent intent = createIntent().setAction(ACTION_CALLSTATE) - .putExtra(EXTRA_CALL_PHONENUMBER, number) - .putExtra(EXTRA_CALL_COMMAND, command); + .putExtra(EXTRA_CALL_PHONENUMBER, callSpec.number) + .putExtra(EXTRA_CALL_COMMAND, callSpec.command); invokeService(intent); } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + public void onSetMusicInfo(MusicSpec musicSpec) { Intent intent = createIntent().setAction(ACTION_SETMUSICINFO) - .putExtra(EXTRA_MUSIC_ARTIST, artist) - .putExtra(EXTRA_MUSIC_ALBUM, album) - .putExtra(EXTRA_MUSIC_TRACK, track) - .putExtra(EXTRA_MUSIC_DURATION, duration) - .putExtra(EXTRA_MUSIC_TRACKCOUNT, trackCount) - .putExtra(EXTRA_MUSIC_TRACKNR, trackNr); + .putExtra(EXTRA_MUSIC_ARTIST, musicSpec.artist) + .putExtra(EXTRA_MUSIC_ALBUM, musicSpec.album) + .putExtra(EXTRA_MUSIC_TRACK, musicSpec.track) + .putExtra(EXTRA_MUSIC_DURATION, musicSpec.duration) + .putExtra(EXTRA_MUSIC_TRACKCOUNT, musicSpec.trackCount) + .putExtra(EXTRA_MUSIC_TRACKNR, musicSpec.trackNr); invokeService(intent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java new file mode 100644 index 00000000..1d088a49 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java @@ -0,0 +1,15 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +public class CallSpec { + public static final int CALL_UNDEFINED = 1; + public static final int CALL_ACCEPT = 1; + public static final int CALL_INCOMING = 2; + public static final int CALL_OUTGOING = 3; + public static final int CALL_REJECT = 4; + public static final int CALL_START = 5; + public static final int CALL_END = 6; + + public String number; + public String name; + public int command; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java new file mode 100644 index 00000000..af52f02f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java @@ -0,0 +1,17 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +public class MusicSpec { + public static final int MUSIC_UNDEFINED = 0; + public static final int MUSIC_PLAY = 1; + public static final int MUSIC_PAUSE = 2; + public static final int MUSIC_PLAYPAUSE = 3; + public static final int MUSIC_NEXT = 4; + public static final int MUSIC_PREVIOUS = 5; + + public String artist; + public String album; + public String track; + public int duration; + public int trackCount; + public int trackNr; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ServiceCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ServiceCommand.java deleted file mode 100644 index 5cb94511..00000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ServiceCommand.java +++ /dev/null @@ -1,22 +0,0 @@ -package nodomain.freeyourgadget.gadgetbridge.model; - -public enum ServiceCommand { - - UNDEFINED, - - CALL_ACCEPT, - CALL_END, - CALL_INCOMING, - CALL_OUTGOING, - CALL_REJECT, - CALL_START, - - MUSIC_PLAY, - MUSIC_PAUSE, - MUSIC_PLAYPAUSE, - MUSIC_NEXT, - MUSIC_PREVIOUS, - - APP_INFO_NAME, - VERSION_FIRMWARE -} 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 d33b9dd9..d103011a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.service; -import android.app.Application; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; 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 68ecefee..6ade401d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -33,9 +33,10 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.SMSReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.TimeChangeReceiver; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -64,10 +65,10 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_ALA import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_CONFIG; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_APP_UUID; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOOLEAN_ENABLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_COMMAND; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALL_PHONENUMBER; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_DEVICE_ADDRESS; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOOLEAN_ENABLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ALBUM; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST; @@ -278,26 +279,32 @@ public class DeviceCommunicationService extends Service { break; } case ACTION_CALLSTATE: - ServiceCommand command = (ServiceCommand) intent.getSerializableExtra(EXTRA_CALL_COMMAND); + int command = intent.getIntExtra(EXTRA_CALL_COMMAND, CallSpec.CALL_UNDEFINED); String phoneNumber = intent.getStringExtra(EXTRA_CALL_PHONENUMBER); String callerName = null; if (phoneNumber != null) { callerName = getContactDisplayNameByNumber(phoneNumber); } - mDeviceSupport.onSetCallState(phoneNumber, callerName, command); + + CallSpec callSpec = new CallSpec(); + callSpec.command = command; + callSpec.number = phoneNumber; + callSpec.name = callerName; + mDeviceSupport.onSetCallState(callSpec); break; case ACTION_SETTIME: mDeviceSupport.onSetTime(); break; case ACTION_SETMUSICINFO: - String artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST); - String album = intent.getStringExtra(EXTRA_MUSIC_ALBUM); - String track = intent.getStringExtra(EXTRA_MUSIC_TRACK); - int duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0); - int trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0); - int trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); - mDeviceSupport.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); + MusicSpec musicSpec = new MusicSpec(); + musicSpec.artist = intent.getStringExtra(EXTRA_MUSIC_ARTIST); + musicSpec.album = intent.getStringExtra(EXTRA_MUSIC_ALBUM); + musicSpec.track = intent.getStringExtra(EXTRA_MUSIC_TRACK); + musicSpec.duration = intent.getIntExtra(EXTRA_MUSIC_DURATION, 0); + musicSpec.trackCount = intent.getIntExtra(EXTRA_MUSIC_TRACKCOUNT, 0); + musicSpec.trackNr = intent.getIntExtra(EXTRA_MUSIC_TRACKNR, 0); + mDeviceSupport.onSetMusicInfo(musicSpec); break; case ACTION_REQUEST_APPINFO: mDeviceSupport.onAppInfoReq(); @@ -424,7 +431,10 @@ public class DeviceCommunicationService extends Service { } if (mMusicPlaybackReceiver == null) { mMusicPlaybackReceiver = new MusicPlaybackReceiver(); - registerReceiver(mMusicPlaybackReceiver, new IntentFilter("com.android.music.metachanged")); + IntentFilter filter = new IntentFilter(); + filter.addAction("com.android.music.metachanged"); + //filter.addAction("com.android.music.playstatechanged"); + registerReceiver(mMusicPlaybackReceiver, filter); } if (mTimeChangeReceiver == null) { mTimeChangeReceiver = new TimeChangeReceiver(); 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 a3994b47..e54e61bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -13,8 +13,9 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; /** * Wraps another device support instance and supports busy-checking and throttling of events. @@ -131,19 +132,19 @@ public class ServiceDeviceSupport implements DeviceSupport { // No throttling for the other events @Override - public void onSetCallState(String number, String name, ServiceCommand command) { + public void onSetCallState(CallSpec callSpec) { if (checkBusy("set call state")) { return; } - delegate.onSetCallState(number, name, command); + delegate.onSetCallState(callSpec); } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + public void onSetMusicInfo(MusicSpec musicSpec) { if (checkBusy("set music info")) { return; } - delegate.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); + delegate.onSetMusicInfo(musicSpec); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java index a7cb39ce..6d373faf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java @@ -20,9 +20,10 @@ public abstract class ConditionalWriteAction extends WriteAction { /** * Checks the condition whether the write shall happen or not. * Returns the actual value to be written or null in case nothing shall be written. - * + *

* Note that returning null will not cause run() to return false, in other words, * the rest of the queue will still be executed. + * * @return the value to be written or null to not write anything */ protected abstract byte[] checkCondition(); 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 f6dd7ce9..37b22591 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 @@ -34,10 +34,11 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; @@ -383,6 +384,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { /** * Part of device initialization process. Do not call manually. + * * @param builder */ private MiBandSupport setHeartrateSleepSupport(TransactionBuilder builder) { @@ -558,8 +560,8 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void onSetCallState(String number, String name, ServiceCommand command) { - if (ServiceCommand.CALL_INCOMING.equals(command)) { + public void onSetCallState(CallSpec callSpec) { + if (callSpec.command == CallSpec.CALL_INCOMING) { telephoneRinging = true; AbortTransactionAction abortAction = new AbortTransactionAction() { @Override @@ -568,7 +570,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } }; performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, abortAction); - } else if (ServiceCommand.CALL_START.equals(command) || ServiceCommand.CALL_END.equals(command)) { + } else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) { telephoneRinging = false; } } @@ -579,7 +581,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + public void onSetMusicInfo(MusicSpec musicSpec) { // not supported } 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 fc00f327..2f6f9236 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -33,9 +33,9 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleColor; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleIconID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; public class PebbleProtocol extends GBDeviceProtocol { @@ -495,7 +495,7 @@ public class PebbleProtocol extends GBDeviceProtocol { @Override public byte[] encodeFindDevice(boolean start) { - return encodeSetCallState("Where are you?", "Gadgetbridge", start ? ServiceCommand.CALL_INCOMING : ServiceCommand.CALL_END); + return encodeSetCallState("Where are you?", "Gadgetbridge", start ? CallSpec.CALL_INCOMING : CallSpec.CALL_END); } private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) { @@ -1044,20 +1044,20 @@ public class PebbleProtocol extends GBDeviceProtocol { } @Override - public byte[] encodeSetCallState(String number, String name, ServiceCommand command) { + public byte[] encodeSetCallState(String number, String name, int command) { String[] parts = {number, name}; byte pebbleCmd; switch (command) { - case CALL_START: + case CallSpec.CALL_START: pebbleCmd = PHONECONTROL_START; break; - case CALL_END: + case CallSpec.CALL_END: pebbleCmd = PHONECONTROL_END; break; - case CALL_INCOMING: + case CallSpec.CALL_INCOMING: pebbleCmd = PHONECONTROL_INCOMINGCALL; break; - case CALL_OUTGOING: + case CallSpec.CALL_OUTGOING: // pebbleCmd = PHONECONTROL_OUTGOINGCALL; /* * HACK/WORKAROUND for non-working outgoing call display. 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 f9fc5de8..74e4736d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -12,8 +12,9 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; @@ -99,16 +100,16 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { } @Override - public void onSetCallState(String number, String name, ServiceCommand command) { + public void onSetCallState(CallSpec callSpec) { if (reconnect()) { - super.onSetCallState(number, name, command); + super.onSetCallState(callSpec); } } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + public void onSetMusicInfo(MusicSpec musicSpec) { if (reconnect()) { - super.onSetMusicInfo(artist, album, track, duration, trackCount, trackNr); + super.onSetMusicInfo(musicSpec); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index a871cac6..5d3bd610 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -8,8 +8,9 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; /** @@ -120,14 +121,14 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport } @Override - public void onSetCallState(String number, String name, ServiceCommand command) { - byte[] bytes = gbDeviceProtocol.encodeSetCallState(number, name, command); + public void onSetCallState(CallSpec callSpec) { + byte[] bytes = gbDeviceProtocol.encodeSetCallState(callSpec.number, callSpec.name, callSpec.command); sendToDevice(bytes); } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { - byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(artist, album, track, duration, trackCount, trackNr); + public void onSetMusicInfo(MusicSpec musicSpec) { + byte[] bytes = gbDeviceProtocol.encodeSetMusicInfo(musicSpec.artist, musicSpec.album, musicSpec.track, musicSpec.duration, musicSpec.trackCount, musicSpec.trackNr); sendToDevice(bytes); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 5cd9ef82..2aa1fb66 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -4,7 +4,6 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; public abstract class GBDeviceProtocol { @@ -16,7 +15,7 @@ public abstract class GBDeviceProtocol { return null; } - public byte[] encodeSetCallState(String number, String name, ServiceCommand command) { + public byte[] encodeSetCallState(String number, String name, int command) { return null; } @@ -60,7 +59,9 @@ public abstract class GBDeviceProtocol { return null; } - public byte[] encodeEnableHeartRateSleepSupport(boolean enable) { return null; } + public byte[] encodeEnableHeartRateSleepSupport(boolean enable) { + return null; + } public GBDeviceEvent[] decodeResponse(byte[] responseData) { return null; diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index b3604199..19fd1810 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -3,15 +3,15 @@ package nodomain.freeyourgadget.gadgetbridge.service; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.net.Uri; -import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.ServiceCommand; public class TestDeviceSupport extends AbstractDeviceSupport { @@ -61,12 +61,12 @@ public class TestDeviceSupport extends AbstractDeviceSupport { } @Override - public void onSetCallState(@Nullable String number, @Nullable String name, ServiceCommand command) { + public void onSetCallState(CallSpec callSpec) { } @Override - public void onSetMusicInfo(String artist, String album, String track, int duration, int trackCount, int trackNr) { + public void onSetMusicInfo(MusicSpec musicSpec) { } From 403f74e59bfd5e7f5a694ce5ebf52773a0099396 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 4 Apr 2016 23:05:00 +0200 Subject: [PATCH 193/274] Enable heart rate charts #232 (And fix some charting issues) --- .../charts/AbstractChartFragment.java | 29 ++++++++++++++----- .../charts/ActivitySleepChartFragment.java | 7 +++++ .../activities/charts/SleepChartFragment.java | 13 +++++++-- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 1 + 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 21f7774f..13bbc2c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -79,7 +79,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } }; private boolean mChartDirty = true; - private boolean supportsHeartrateChart = false; + private boolean supportsHeartrateChart = true; public boolean isChartDirty() { return mChartDirty; @@ -119,6 +119,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected int AK_LIGHT_SLEEP_COLOR; protected int AK_NOT_WORN_COLOR; + protected String HEARTRATE_LABEL; + protected AbstractChartFragment(String... intentFilterActions) { mIntentFilterActions = new HashSet<>(); if (intentFilterActions != null) { @@ -153,6 +155,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light); AK_NOT_WORN_COLOR = getResources().getColor(R.color.chart_not_worn_light); + HEARTRATE_LABEL = getContext().getString(R.string.charts_legend_heartrate); + akActivity = new ActivityConfig(ActivityKind.TYPE_ACTIVITY, getString(R.string.abstract_chart_fragment_kind_activity), AK_ACTIVITY_COLOR); akLightSleep = new ActivityConfig(ActivityKind.TYPE_LIGHT_SLEEP, getString(R.string.abstract_chart_fragment_kind_light_sleep), AK_LIGHT_SLEEP_COLOR); akDeepSleep = new ActivityConfig(ActivityKind.TYPE_DEEP_SLEEP, getString(R.string.abstract_chart_fragment_kind_deep_sleep), AK_DEEP_SLEEP_COLOR); @@ -445,7 +449,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { colors.add(akActivity.color); } activityEntries.add(createBarEntry(value, i)); - if (hr) { + if (hr && isValidHeartRateValue(sample.getCustomValue())) { heartrateEntries.add(createLineEntry(sample.getCustomValue(), i)); } @@ -488,7 +492,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { barData.setGroupSpace(0); combinedData.setData(barData); - if (hr) { + if (hr && heartrateEntries.size() > 0) { LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate"); LineData lineData = new LineData(xLabels, heartrateSet); combinedData.setData(lineData); @@ -507,6 +511,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } } + protected boolean isValidHeartRateValue(int value) { + return value > 0 && value < 255; + } + /** * Implement this to supply the samples to be displayed. * @@ -550,14 +558,19 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { LineDataSet set1 = new LineDataSet(values, label); set1.setColor(HEARTRATE_COLOR); // set1.setColors(colors); -// set1.setDrawCubic(true); -// set1.setCubicIntensity(0.2f); + set1.setDrawCubic(true); + set1.setCubicIntensity(0.1f); // //set1.setDrawFilled(true); // set1.setDrawCircles(false); - set1.setLineWidth(2f); -// set1.setCircleSize(5f); +// set1.setLineWidth(2f); + + set1.setDrawCircles(false); +// set1.setCircleRadius(2f); +// set1.setDrawFilled(true); + + set1.setLineWidth(0.8f); // set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setDrawValues(false); + set1.setDrawValues(true); // set1.setHighLightColor(Color.rgb(128, 0, 255)); // set1.setColor(Color.rgb(89, 178, 44)); set1.setValueTextColor(CHART_TEXT_COLOR); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java index 608a6f2a..cd45452d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java @@ -70,6 +70,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment { // y.setDrawLabels(false); // TODO: make fixed max value optional y.setAxisMaxValue(1f); + y.setAxisMinValue(0); y.setDrawTopYLabelEntry(false); y.setTextColor(CHART_TEXT_COLOR); @@ -82,6 +83,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment { yAxisRight.setDrawLabels(true); yAxisRight.setDrawTopYLabelEntry(true); yAxisRight.setTextColor(CHART_TEXT_COLOR); + yAxisRight.setAxisMaxValue(250); + yAxisRight.setAxisMinValue(0); // refresh immediately instead of use refreshIfVisible(), for perceived performance refresh(); @@ -125,6 +128,10 @@ public class ActivitySleepChartFragment extends AbstractChartFragment { legendLabels.add(akDeepSleep.label); legendColors.add(akNotWorn.color); legendLabels.add(akNotWorn.label); + if (supportsHeartrate()) { + legendColors.add(HEARTRATE_COLOR); + legendLabels.add(HEARTRATE_LABEL); + } chart.getLegend().setCustom(legendColors, legendLabels); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index 6b9a7561..b0836eeb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -151,6 +151,7 @@ public class SleepChartFragment extends AbstractChartFragment { // y.setDrawLabels(false); // TODO: make fixed max value optional y.setAxisMaxValue(1f); + y.setAxisMinValue(0); y.setDrawTopYLabelEntry(false); y.setTextColor(CHART_TEXT_COLOR); @@ -159,10 +160,12 @@ public class SleepChartFragment extends AbstractChartFragment { YAxis yAxisRight = mActivityChart.getAxisRight(); yAxisRight.setDrawGridLines(false); - yAxisRight.setEnabled(false); - yAxisRight.setDrawLabels(false); - yAxisRight.setDrawTopYLabelEntry(false); + yAxisRight.setEnabled(supportsHeartrate()); + yAxisRight.setDrawLabels(true); + yAxisRight.setDrawTopYLabelEntry(true); yAxisRight.setTextColor(CHART_TEXT_COLOR); + yAxisRight.setAxisMaxValue(250); + yAxisRight.setAxisMinValue(0); } protected void setupLegend(Chart chart) { @@ -172,6 +175,10 @@ public class SleepChartFragment extends AbstractChartFragment { legendLabels.add(akLightSleep.label); legendColors.add(akDeepSleep.color); legendLabels.add(akDeepSleep.label); + if (supportsHeartrate()) { + legendColors.add(HEARTRATE_COLOR); + legendLabels.add(HEARTRATE_LABEL); + } chart.getLegend().setCustom(legendColors, legendLabels); chart.getLegend().setTextColor(LEGEND_TEXT_COLOR); } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f5a0bc4f..8d5f07fd 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -11,7 +11,7 @@ #ff808080 #1f000000 - #b40000 + #ffab40 #0071b7 #4c5aff diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5c06b5a5..407d428f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -239,5 +239,6 @@ "HR: " Firmware update in progress Firmware not sent + Heart Rate From 34600e085e861083fa8326a200fdaecb712bca14 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 4 Apr 2016 23:13:57 +0200 Subject: [PATCH 194/274] Fix wrong assignment, needed to properly deal with datalog messages longer than 255 --- .../gadgetbridge/service/devices/pebble/PebbleProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2f6f9236..d79f3d9f 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 @@ -1876,7 +1876,7 @@ public class PebbleProtocol extends GBDeviceProtocol { int timestamp = buf.getInt(); int log_tag = buf.getInt(); byte item_type = buf.get(); - short item_size = buf.get(); + short item_size = buf.getShort(); LOG.info("DATALOG OPENSESSION. id=" + (id & 0xff) + ", App UUID=" + uuid.toString() + ", log_tag=" + log_tag + ", item_type=" + item_type + ", itemSize=" + item_size); if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { From 51def0d497c3cf05d6a1098fb8d5922e0fd720fb Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Mon, 4 Apr 2016 23:33:17 +0200 Subject: [PATCH 195/274] Add light intensity to the known steps datalog message. Add support for record version 6 introduced with firmware 3.11. There are more data in each record now, but we still do not know what they mean. Close #270 --- .../devices/pebble/DatalogSessionHealthSteps.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 81bb4319..f89f0363 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -46,7 +46,7 @@ public class DatalogSessionHealthSteps extends DatalogSession { recordVersion = datalogMessage.getShort(); - if (recordVersion != 5) + if ((recordVersion != 5) && (recordVersion != 6)) return false; //we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it timestamp = datalogMessage.getInt(); @@ -59,8 +59,7 @@ public class DatalogSessionHealthSteps extends DatalogSession { for (int recordIdx = 0; recordIdx < recordNum; recordIdx++) { datalogMessage.position(beginOfRecordPosition + recordIdx * recordLength); //we may not consume all the bytes of a record - stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get() & 0xff, datalogMessage.get() & 0xff, datalogMessage.getShort() & 0xffff); - datalogMessage.getShort(); // skip + stepsRecords[recordIdx] = new StepsRecord(timestamp, datalogMessage.get() & 0xff, datalogMessage.get() & 0xff, datalogMessage.getShort() & 0xffff, datalogMessage.get() & 0xff); timestamp += 60; } @@ -102,12 +101,14 @@ public class DatalogSessionHealthSteps extends DatalogSession { int steps; int orientation; int intensity; + int light_intensity; - public StepsRecord(int timestamp, int steps, int orientation, int intensity) { + public StepsRecord(int timestamp, int steps, int orientation, int intensity, int light_intensity) { this.timestamp = timestamp; this.steps = steps; this.orientation = orientation; this.intensity = intensity; + this.light_intensity = light_intensity; } } From e42a04144896c29c3aed9768d86635a05cf7b1c7 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Wed, 6 Apr 2016 12:37:23 +0200 Subject: [PATCH 196/274] Pebble: Smarter reconnection attempts Sleep several seconds between reconnection attempts: One second after first attempt, two after the second, and so on. refs #89 --- .../service/devices/pebble/PebbleIoThread.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 504a3629..1c8b6606 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 @@ -365,12 +365,19 @@ public class PebbleIoThread extends GBDeviceIoThread { LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = Integer.valueOf(sharedPrefs.getString("pebble_reconnect_attempts", "10")); + int maxReconnectAttempts = reconnectAttempts; if (reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) { LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")"); mIsConnected = connect(gbDevice.getAddress()); + if (!mIsConnected) { + try { + Thread.sleep((maxReconnectAttempts-reconnectAttempts)*1000); + } catch (InterruptedException ignored) { + } + } } } if (!mIsConnected && !mQuit) { From d2af3468f085effb1d75ed937711f43da0bc2df6 Mon Sep 17 00:00:00 2001 From: danielegobbetti Date: Wed, 6 Apr 2016 21:48:16 +0200 Subject: [PATCH 197/274] Add support for new datalog message added in pebble firmware 3.11 This adds support for storing deep sleep data. --- CHANGELOG.md | 3 + .../pebble/DatalogSessionHealthSleep.java | 93 +++++++++++++++++-- .../devices/pebble/PebbleProtocol.java | 2 +- app/src/main/res/xml/changelog_master.xml | 5 + 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c691eebf..13335a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ###Changelog +####Version (next) +* Pebble: support pebble health datalog messages of firmware 3.11 (this adds the support for deep sleep!) + ####Version 0.9.3 * Pebble: Fix Pebble Health activation (was not available in the App Manager) * Simplify connection state display (only connecting->connected) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 8528287f..aaa67ec3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -21,22 +21,33 @@ class DatalogSessionHealthSleep extends DatalogSession { public DatalogSessionHealthSleep(byte id, UUID uuid, int tag, byte item_type, short item_size) { super(id, uuid, tag, item_type, item_size); - taginfo = "(health - sleep)"; + taginfo = "(health - sleep " + tag + " )"; } @Override public boolean handleMessage(ByteBuffer datalogMessage, int length) { LOG.info("DATALOG " + taginfo + GB.hexdump(datalogMessage.array(), datalogMessage.position(), length)); + switch (this.tag) { + case 83: + return handleMessage83(datalogMessage, length); + case 84: + return handleMessage84(datalogMessage, length); + default: + return false; + } + } + private boolean handleMessage84(ByteBuffer datalogMessage, int length) { int initialPosition = datalogMessage.position(); int beginOfRecordPosition; short recordVersion; //probably + short recordType; //probably: 1=sleep, 2=deep sleep if (0 != (length % itemSize)) return false;//malformed message? int recordCount = length / itemSize; - SleepRecord[] sleepRecords = new SleepRecord[recordCount]; + SleepRecord84[] sleepRecords = new SleepRecord84[recordCount]; for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { beginOfRecordPosition = initialPosition + recordIdx * itemSize; @@ -45,23 +56,73 @@ class DatalogSessionHealthSleep extends DatalogSession { if (recordVersion != 1) return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it - sleepRecords[recordIdx] = new SleepRecord(datalogMessage.getInt(), + datalogMessage.getShort();//throwaway, unknown + recordType = datalogMessage.getShort(); + + sleepRecords[recordIdx] = new SleepRecord84(recordType, datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); + } + + return store84(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. + } + + private boolean store84(SleepRecord84[] sleepRecords) { + DBHandler dbHandler = null; + SampleProvider sampleProvider = new HealthSampleProvider(); + try { + dbHandler = GBApplication.acquireDB(); + int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider); + for (SleepRecord84 sleepRecord : sleepRecords) { + if (latestTimestamp < (sleepRecord.timestampStart + sleepRecord.durationSeconds)) + return false; + int activityType = sleepRecord.type == 2 ? sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP) : sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP); + + dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), activityType, sampleProvider); + } + } catch (Exception ex) { + LOG.debug(ex.getMessage()); + } finally { + if (dbHandler != null) { + dbHandler.release(); + } + } + return true; + } + + private boolean handleMessage83(ByteBuffer datalogMessage, int length) { + int initialPosition = datalogMessage.position(); + int beginOfRecordPosition; + short recordVersion; //probably + + if (0 != (length % itemSize)) + return false;//malformed message? + + int recordCount = length / itemSize; + SleepRecord83[] sleepRecords = new SleepRecord83[recordCount]; + + for (int recordIdx = 0; recordIdx < recordCount; recordIdx++) { + beginOfRecordPosition = initialPosition + recordIdx * itemSize; + datalogMessage.position(beginOfRecordPosition);//we may not consume all the bytes of a record + recordVersion = datalogMessage.getShort(); + if (recordVersion != 1) + return false;//we don't know how to deal with the data TODO: this is not ideal because we will get the same message again and again since we NACK it + + sleepRecords[recordIdx] = new SleepRecord83(datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt(), datalogMessage.getInt()); } - return store(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. + return store83(sleepRecords);//NACK if we cannot store the data yet, the watch will send the sleep records again. } - private boolean store(SleepRecord[] sleepRecords) { + private boolean store83(SleepRecord83[] sleepRecords) { DBHandler dbHandler = null; SampleProvider sampleProvider = new HealthSampleProvider(); - GB.toast("We don't know how to store deep sleep from the pebble yet.", Toast.LENGTH_LONG, GB.INFO); + GB.toast("Deep sleep is supported only from firmware 3.11 onwards.", Toast.LENGTH_LONG, GB.INFO); try { dbHandler = GBApplication.acquireDB(); int latestTimestamp = dbHandler.fetchLatestTimestamp(sampleProvider); - for (SleepRecord sleepRecord : sleepRecords) { + for (SleepRecord83 sleepRecord : sleepRecords) { if (latestTimestamp < sleepRecord.bedTimeEnd) return false; dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); @@ -76,17 +137,31 @@ class DatalogSessionHealthSleep extends DatalogSession { return true; } - private class SleepRecord { + private class SleepRecord83 { int offsetUTC; //probably int bedTimeStart; int bedTimeEnd; int deepSleepSeconds; - public SleepRecord(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { + public SleepRecord83(int offsetUTC, int bedTimeStart, int bedTimeEnd, int deepSleepSeconds) { this.offsetUTC = offsetUTC; this.bedTimeStart = bedTimeStart; this.bedTimeEnd = bedTimeEnd; this.deepSleepSeconds = deepSleepSeconds; } } + + private class SleepRecord84 { + int type; //1=sleep, 2=deep sleep + int offsetUTC; //probably + int timestampStart; + int durationSeconds; + + public SleepRecord84(int type, int offsetUTC, int timestampStart, int durationSeconds) { + this.type = type; + this.offsetUTC = offsetUTC; + this.timestampStart = timestampStart; + this.durationSeconds = durationSeconds; + } + } } \ No newline at end of file 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 d79f3d9f..5d261e6a 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 @@ -1881,7 +1881,7 @@ public class PebbleProtocol extends GBDeviceProtocol { if (!mDatalogSessions.containsKey(id)) { if (uuid.equals(UUID_ZERO) && log_tag == 81) { mDatalogSessions.put(id, new DatalogSessionHealthSteps(id, uuid, log_tag, item_type, item_size)); - } else if (uuid.equals(UUID_ZERO) && log_tag == 83) { + } else if (uuid.equals(UUID_ZERO) && (log_tag == 83 || log_tag == 84)) { mDatalogSessions.put(id, new DatalogSessionHealthSleep(id, uuid, log_tag, item_type, item_size)); } else { mDatalogSessions.put(id, new DatalogSession(id, uuid, log_tag, item_type, item_size)); diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 0c25965e..8527cad6 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,10 @@ + + Pebble: support pebble health datalog messages of firmware 3.11 (this adds the + support for deep sleep!) + + Pebble: Fix Pebble Health activation (was not available in the App Manager) Simplify connection state display (only connecting->connected) From 4055cc1173834b375ea404cf766f1024b929e320 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Apr 2016 22:44:50 +0200 Subject: [PATCH 198/274] bump version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a1f7b997..d0646fe5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.3" - versionCode 47 + versionName "0.9.4" + versionCode 48 } buildTypes { release { From e91b5a07bd8b7e9db97abf989174825c6b32d4b6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Apr 2016 22:55:04 +0200 Subject: [PATCH 199/274] Pebble: change delay between reconects to 1,2,4,8,16,32,64 (max) seconds --- .../service/devices/pebble/PebbleIoThread.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 1c8b6606..b2a42630 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 @@ -365,18 +365,21 @@ public class PebbleIoThread extends GBDeviceIoThread { LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = Integer.valueOf(sharedPrefs.getString("pebble_reconnect_attempts", "10")); - int maxReconnectAttempts = reconnectAttempts; if (reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); + int delaySeconds = 1; while (reconnectAttempts-- > 0 && !mQuit && !mIsConnected) { LOG.info("Trying to reconnect (attempts left " + reconnectAttempts + ")"); mIsConnected = connect(gbDevice.getAddress()); if (!mIsConnected) { try { - Thread.sleep((maxReconnectAttempts-reconnectAttempts)*1000); + Thread.sleep(delaySeconds * 1000); } catch (InterruptedException ignored) { } + if (delaySeconds < 64) { + delaySeconds *= 2; + } } } } From 10be21e07b813d0c02264e0035998f4043b5f408 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Apr 2016 23:00:18 +0200 Subject: [PATCH 200/274] add Pebble stuff to CHANGELOG.md (Mi Band is still missing) --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13335a92..6d4d3b66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ###Changelog ####Version (next) -* Pebble: support pebble health datalog messages of firmware 3.11 (this adds the support for deep sleep!) +* Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) +* Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly +* Pebble: delay between reconnection attempts (from 1 up to 64 seconds) ####Version 0.9.3 * Pebble: Fix Pebble Health activation (was not available in the App Manager) From c7b64b6da72802aec736d591210c8772ca3944ec Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Apr 2016 23:03:56 +0200 Subject: [PATCH 201/274] update Japanese translation from transifex (thanks!) --- app/src/main/res/values-ja/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 1ca2b0ed..6cf3ba01 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -102,7 +102,7 @@ 有効なユーザーデータはありません。今のところ、ダミーのユーザーデータを使用します。 お使いのMi Bandが振動と点滅したとき、それを連続して数回タップしてください。 インストール - お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。 + お使いのデバイスを検出可能にしてください。現在、接続されたデバイスは、おそらく検出されません。お使いのデバイスが 2 分しても表示されない場合は、モバイルデバイスを再起動した後にもう一度試してください。 注: デバイスイメージ 名前/別名 @@ -197,6 +197,7 @@ 非互換性のファームウェア このファームウェアは、デバイスと互換性がありません 今後のイベントのために予約するアラーム + 睡眠の検出を改善するために心拍センサーを使用する 再接続の待機中 再インストール あなたについて @@ -217,4 +218,6 @@ FW: %1$s ログファイルのディレクトリを作成中にエラー: %1$s HR: + ファームウェアを更新しています + ファームウェアを送信しませんでした From 6895c5b77643f94efd7b29d095db2018b04f23f3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 6 Apr 2016 23:29:59 +0200 Subject: [PATCH 202/274] fix xml changelog --- app/src/main/res/xml/changelog_master.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 8527cad6..dddd9bab 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,15 +1,15 @@ - - Pebble: support pebble health datalog messages of firmware 3.11 (this adds the - support for deep sleep!) - + + Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) + Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly + Pebble: delay between reconnection attempts (from 1 up to 64 seconds) - Pebble: Fix Pebble Health activation (was not available in the App Manager) - Simplify connection state display (only connecting->connected) - Small improvements to the pairing activity - Mi Band 1S: Fix for mi band firmware update + Pebble: Fix Pebble Health activation (was not available in the App Manager) + Simplify connection state display (only connecting->connected) + Small improvements to the pairing activity + Mi Band 1S: Fix for mi band firmware update Mi Band: Fix update of second (HR) firmware on Mi1S (#234) From b1a93c430d006abde8932af955e7ae773f1cff41 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 7 Apr 2016 00:21:21 +0200 Subject: [PATCH 203/274] interrupt thread instead of joining to fix ANR --- .../service/serial/AbstractSerialDeviceSupport.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 5d3bd610..aa78a393 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -48,11 +48,7 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport // currently only one thread allowed if (gbDeviceIOThread != null) { gbDeviceIOThread.quit(); - try { - gbDeviceIOThread.join(); - } catch (InterruptedException e) { - e.printStackTrace(); - } + gbDeviceIOThread.interrupt(); gbDeviceIOThread = null; } } From a49335fa67188aa55d429f04d59a9b1a5f59c102 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 7 Apr 2016 17:52:15 +0200 Subject: [PATCH 204/274] Allow to change stored samples converting only certain old types --- .../database/ActivityDatabaseHandler.java | 18 ++++++++++++++++++ .../gadgetbridge/database/DBHandler.java | 2 ++ .../pebble/DatalogSessionHealthSleep.java | 9 ++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java index f8812b30..ce9e1ae4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/ActivityDatabaseHandler.java @@ -275,6 +275,24 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper implements DBHandl } } + @Override + public void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind, SampleProvider provider) { + try (SQLiteDatabase db = this.getReadableDatabase()) { + String sql = "UPDATE " + TABLE_GBACTIVITYSAMPLES + " SET " + KEY_TYPE + "= ? WHERE " + + KEY_TYPE + " = ? AND " + + KEY_PROVIDER + " = ? AND " + + KEY_TIMESTAMP + " >= ? AND " + KEY_TIMESTAMP + " < ? ;"; //do not use BETWEEN because the range is inclusive in that case! + + SQLiteStatement statement = db.compileStatement(sql); + statement.bindLong(1, toKind); + statement.bindLong(2, fromKind); + statement.bindLong(3, provider.getID()); + statement.bindLong(4, timestampFrom); + statement.bindLong(5, timestampTo); + statement.execute(); + } + } + @Override public int fetchLatestTimestamp(SampleProvider provider) { try (SQLiteDatabase db = this.getReadableDatabase()) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 42aa64de..49dbbd40 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -32,6 +32,8 @@ public interface DBHandler { void changeStoredSamplesType(int timestampFrom, int timestampTo, int kind, SampleProvider provider); + void changeStoredSamplesType(int timestampFrom, int timestampTo, int fromKind, int toKind, SampleProvider provider); + int fetchLatestTimestamp(SampleProvider provider); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index aaa67ec3..7d06876f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -74,9 +74,12 @@ class DatalogSessionHealthSleep extends DatalogSession { for (SleepRecord84 sleepRecord : sleepRecords) { if (latestTimestamp < (sleepRecord.timestampStart + sleepRecord.durationSeconds)) return false; - int activityType = sleepRecord.type == 2 ? sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP) : sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP); + if (sleepRecord.type == 2) { + dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_DEEP_SLEEP), sampleProvider); + } else { + dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); + } - dbHandler.changeStoredSamplesType(sleepRecord.timestampStart, (sleepRecord.timestampStart + sleepRecord.durationSeconds), activityType, sampleProvider); } } catch (Exception ex) { LOG.debug(ex.getMessage()); @@ -125,7 +128,7 @@ class DatalogSessionHealthSleep extends DatalogSession { for (SleepRecord83 sleepRecord : sleepRecords) { if (latestTimestamp < sleepRecord.bedTimeEnd) return false; - dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); + dbHandler.changeStoredSamplesType(sleepRecord.bedTimeStart, sleepRecord.bedTimeEnd, sampleProvider.toRawActivityKind(ActivityKind.TYPE_ACTIVITY), sampleProvider.toRawActivityKind(ActivityKind.TYPE_LIGHT_SLEEP), sampleProvider); } } catch (Exception ex) { LOG.debug(ex.getMessage()); From 1e5dbb6a239895983c59fa76ac7ff916681f9067 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 7 Apr 2016 20:52:26 +0200 Subject: [PATCH 205/274] OK, just connect(true) is not sufficient #249 (we again get connection problems. Let's try this.) --- .../freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 b31ba1d2..4839a992 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 @@ -157,11 +157,12 @@ public final class BtLEQueue { LOG.info("Attempting to connect to " + mGbDevice.getName()); mBluetoothAdapter.cancelDiscovery(); BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress()); + boolean result; synchronized (mGattMonitor) { mBluetoothGatt = remoteDevice.connectGatt(mContext, true, internalGattCallback); -// result = mBluetoothGatt.connect(); + result = mBluetoothGatt.connect(); } - boolean result = mBluetoothGatt != null; +// boolean result = mBluetoothGatt != null; if (result) { setDeviceConnectionState(State.CONNECTING); } From 3953e4232d2bb36d600e440f519f18bfe0f6f72b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 7 Apr 2016 20:52:55 +0200 Subject: [PATCH 206/274] Update gradle build tools to 2.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 6d40ae25..cce93864 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files From 059c7d0b15ec17c38db29dc97f08b70d5cf35487 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Apr 2016 22:28:06 +0200 Subject: [PATCH 207/274] Hopefully fix travis jdk7 build (permgen space issue) --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index a73f784f..38045ed0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,12 @@ language: android + jdk: - oraclejdk8 - oraclejdk7 + +env: + - GRADLE_OPTS="-XX:MaxPermSize=256m" + android: components: # Uncomment the lines below if you want to From 42dda911e46f72bb3f5ddd59d1a752a745f452e2 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 8 Apr 2016 23:02:50 +0200 Subject: [PATCH 208/274] Fix crash in charts activity, closes #277 --- .../activities/charts/AbstractChartFragment.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 13bbc2c0..f163ba65 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; @@ -80,6 +81,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { }; private boolean mChartDirty = true; private boolean supportsHeartrateChart = true; + private AsyncTask refreshTask; public boolean isChartDirty() { return mChartDirty; @@ -367,7 +369,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { if (chartsHost.getDevice() != null) { mChartDirty = false; updateDateInfo(getStartDate(), getEndDate()); - createRefreshTask("Visualizing data", getActivity()).execute(); + if (refreshTask != null && refreshTask.getStatus() != AsyncTask.Status.FINISHED) { + refreshTask.cancel(true); + } + refreshTask = createRefreshTask("Visualizing data", getActivity()).execute(); } } } From 802e9a82359cc2fea311d8112c4464711971e391 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 9 Apr 2016 09:53:03 +0200 Subject: [PATCH 209/274] OK, revert to connectGatt(false), connect often does not work with true #249 --- .../gadgetbridge/service/btle/BtLEQueue.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 4839a992..71d1beaf 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 @@ -157,12 +157,13 @@ public final class BtLEQueue { LOG.info("Attempting to connect to " + mGbDevice.getName()); mBluetoothAdapter.cancelDiscovery(); BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(mGbDevice.getAddress()); - boolean result; +// boolean result; synchronized (mGattMonitor) { - mBluetoothGatt = remoteDevice.connectGatt(mContext, true, internalGattCallback); - result = mBluetoothGatt.connect(); + // connectGatt with true doesn't really work ;( too often connection problems + mBluetoothGatt = remoteDevice.connectGatt(mContext, false, internalGattCallback); +// result = mBluetoothGatt.connect(); } -// boolean result = mBluetoothGatt != null; + boolean result = mBluetoothGatt != null; if (result) { setDeviceConnectionState(State.CONNECTING); } From 57ecba16f3eefd4906e9a5842b1c53ddd707ab19 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sat, 9 Apr 2016 10:05:27 +0200 Subject: [PATCH 210/274] Update Changelog for 0.9.4 --- CHANGELOG.md | 7 ++++++- app/src/main/res/xml/changelog_master.xml | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d4d3b66..0e58d589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ ###Changelog -####Version (next) +####Version (0.9.4) * Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) * Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly * Pebble: delay between reconnection attempts (from 1 up to 64 seconds) +* Fix crash in charts activities when changing the date, quickly (#277) +* Mi Band: preference to enable heart rate measurement during sleep (#232, thanks computerlyrik!) +* Mi Band: display measured heart rate in charts (#232) +* Mi Band 1S: full support for firmware upgrade/downgrade (both for Mi Band and heart rate sensor) (#234) +* Mi Band 1S: fix device detection for certain versions ####Version 0.9.3 * Pebble: Fix Pebble Health activation (was not available in the App Manager) diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index dddd9bab..b9dbba00 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,9 +1,14 @@ + Fix crash in charts activities when changing the date, quickly (#277) Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly Pebble: delay between reconnection attempts (from 1 up to 64 seconds) + Mi Band: preference to enable heart rate measurement during sleep (#232, thanks computerlyrik!) + Mi Band: display measured heart rate in charts (#232) + Mi Band 1S: full support for firmware upgrade/downgrade (both for Mi Band and heart rate sensor) (#234) + Mi Band 1S: fix device detection for certain versions Pebble: Fix Pebble Health activation (was not available in the App Manager) From 3ef942b5d399f0da2a9308bd2066160744735060 Mon Sep 17 00:00:00 2001 From: Lem Dulfo Date: Sat, 9 Apr 2016 09:12:40 +0800 Subject: [PATCH 211/274] Do not crash on null BT adapter, allows UI work on emulator. --- .../gadgetbridge/activities/DiscoveryActivity.java | 5 +++++ 1 file changed, 5 insertions(+) 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 81a5a2f6..fc8e727e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -290,6 +290,11 @@ public class DiscoveryActivity extends Activity implements AdapterView.OnItemCli return false; } BluetoothAdapter adapter = bluetoothService.getAdapter(); + if (adapter == null) { + LOG.warn("No bluetooth available"); + this.adapter = null; + return false; + } if (!adapter.isEnabled()) { LOG.warn("Bluetooth not enabled"); Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); From 5a3004cbceb3365a420d64f33a770c34e88430c3 Mon Sep 17 00:00:00 2001 From: Lem Dulfo Date: Sat, 9 Apr 2016 11:41:31 +0800 Subject: [PATCH 212/274] AppCompat and FAB, more Material Design --- app/build.gradle | 1 + .../activities/AbstractSettingsActivity.java | 104 +++++++++++++++++- .../gadgetbridge/activities/AlarmDetails.java | 4 +- .../activities/AndroidPairingActivity.java | 4 +- .../activities/AppBlacklistActivity.java | 4 +- .../activities/AppManagerActivity.java | 4 +- .../activities/ControlCenter.java | 3 +- .../activities/DebugActivity.java | 4 +- .../activities/DiscoveryActivity.java | 3 +- .../activities/ExternalPebbleJSActivity.java | 4 +- .../activities/FwAppInstallerActivity.java | 4 +- app/src/main/res/drawable/ic_add_black.png | Bin 0 -> 201 bytes .../res/layout/activity_controlcenter.xml | 19 +++- app/src/main/res/values-v21/styles.xml | 5 +- app/src/main/res/values/colors.xml | 6 +- app/src/main/res/values/styles.xml | 5 +- 16 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 app/src/main/res/drawable/ic_add_black.png diff --git a/app/build.gradle b/app/build.gradle index d0646fe5..b87fb8a0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,7 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:support-v4:23.1.1' + compile 'com.android.support:design:23.3.0' compile 'com.github.tony19:logback-android-classic:1.1.1-4' compile 'org.slf4j:slf4j-api:1.7.7' compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index 4d3c48a1..d52abe1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -1,12 +1,21 @@ package nodomain.freeyourgadget.gadgetbridge.activities; +import android.content.res.Configuration; import android.os.Bundle; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.widget.Toolbar; +import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,6 +29,7 @@ import org.slf4j.LoggerFactory; public abstract class AbstractSettingsActivity extends PreferenceActivity { private static final Logger LOG = LoggerFactory.getLogger(AbstractSettingsActivity.class); + private AppCompatDelegate delegate; /** * A preference value change listener that updates the preference's summary @@ -56,15 +66,15 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { } private static class ExtraSetSummaryOnChangeListener extends SimpleSetSummaryOnChangeListener { - private final Preference.OnPreferenceChangeListener delegate; + private final Preference.OnPreferenceChangeListener prefChangeListener; - public ExtraSetSummaryOnChangeListener(Preference.OnPreferenceChangeListener delegate) { - this.delegate = delegate; + public ExtraSetSummaryOnChangeListener(Preference.OnPreferenceChangeListener prefChangeListener) { + this.prefChangeListener = prefChangeListener; } @Override public boolean onPreferenceChange(Preference preference, Object value) { - boolean result = delegate.onPreferenceChange(preference, value); + boolean result = prefChangeListener.onPreferenceChange(preference, value); if (result) { return super.onPreferenceChange(preference, value); } @@ -74,11 +84,19 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { private static final SimpleSetSummaryOnChangeListener sBindPreferenceSummaryToValueListener = new SimpleSetSummaryOnChangeListener(); + @Override + protected void onCreate(Bundle savedInstanceState) { + getDelegate().installViewFactory(); + getDelegate().onCreate(savedInstanceState); + super.onCreate(savedInstanceState); + } + @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); + getDelegate().onPostCreate(savedInstanceState); - getActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); for (String prefKey : getPreferenceKeysWithSummary()) { final Preference pref = findPreference(prefKey); @@ -90,6 +108,67 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { } } + + @Override + protected void onPostResume() { + super.onPostResume(); + getDelegate().onPostResume(); + } + + @Override + protected void onTitleChanged(CharSequence title, int color) { + super.onTitleChanged(title, color); + getDelegate().setTitle(title); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + getDelegate().onConfigurationChanged(newConfig); + } + + @Override + protected void onStop() { + super.onStop(); + getDelegate().onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + getDelegate().onDestroy(); + } + + @Override + public MenuInflater getMenuInflater() { + return getDelegate().getMenuInflater(); + } + + @Override + public void setContentView(@LayoutRes int layoutResID) { + getDelegate().setContentView(layoutResID); + } + + @Override + public void setContentView(View view) { + getDelegate().setContentView(view); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().setContentView(view, params); + } + + @Override + public void addContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().addContentView(view, params); + } + + public void invalidateOptionsMenu() { + getDelegate().invalidateOptionsMenu(); + } + + /** * Subclasses should reimplement this to return the keys of those * preferences which should print its values as a summary below the @@ -141,4 +220,19 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { } return super.onOptionsItemSelected(item); } + + public ActionBar getSupportActionBar() { + return getDelegate().getSupportActionBar(); + } + + public void setSupportActionBar(@Nullable Toolbar toolbar) { + getDelegate().setSupportActionBar(toolbar); + } + + private AppCompatDelegate getDelegate() { + if (delegate == null) { + delegate = AppCompatDelegate.create(this, null); + } + return delegate; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index a137ad0c..9bfe1295 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -1,8 +1,8 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.os.Bundle; import android.os.Parcelable; +import android.support.v7.app.AppCompatActivity; import android.text.format.DateFormat; import android.view.MenuItem; import android.widget.CheckBox; @@ -12,7 +12,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; -public class AlarmDetails extends Activity { +public class AlarmDetails extends AppCompatActivity { private GBAlarm alarm; private TimePicker timePicker; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java index 284f6791..f85d3ca0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java @@ -1,11 +1,11 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; import nodomain.freeyourgadget.gadgetbridge.R; -public class AndroidPairingActivity extends Activity { +public class AndroidPairingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index 630e9c04..c2767f56 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -12,6 +11,7 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -34,7 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -public class AppBlacklistActivity extends Activity { +public class AppBlacklistActivity extends AppCompatActivity { private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 6b566f3e..59732098 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -11,6 +10,7 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; @@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; -public class AppManagerActivity extends Activity { +public class AppManagerActivity extends AppCompatActivity { public static final String ACTION_REFRESH_APPLIST = "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist"; private static final Logger LOG = LoggerFactory.getLogger(AppManagerActivity.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 187c5c95..82e82fa5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -19,6 +19,7 @@ import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; @@ -45,7 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class ControlCenter extends Activity { +public class ControlCenter extends AppCompatActivity { private static final Logger LOG = LoggerFactory.getLogger(ControlCenter.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index dc7eb2f5..1fa09724 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.app.AlertDialog; import android.app.NotificationManager; import android.app.PendingIntent; @@ -14,6 +13,7 @@ import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.app.NotificationCompat; import android.support.v4.app.RemoteInput; +import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; import android.widget.Button; @@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DebugActivity extends Activity { +public class DebugActivity extends AppCompatActivity { private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class); private static final String EXTRA_REPLY = "reply"; 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 fc8e727e..82f2c7e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -12,6 +12,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcelable; +import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView; import android.widget.Button; @@ -32,7 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DiscoveryActivity extends Activity implements AdapterView.OnItemClickListener { +public class DiscoveryActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class); private static final long SCAN_DURATION = 60000; // 60s diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 2f86bfaf..4ddc013c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -1,10 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; import android.webkit.ConsoleMessage; @@ -32,7 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; -public class ExternalPebbleJSActivity extends Activity { +public class ExternalPebbleJSActivity extends AppCompatActivity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index 7f8d507f..d74250aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -1,6 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.activities; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -9,6 +8,7 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; import android.widget.Button; @@ -35,7 +35,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class FwAppInstallerActivity extends Activity implements InstallActivity { +public class FwAppInstallerActivity extends AppCompatActivity implements InstallActivity { private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class); private static final String ITEM_DETAILS = "details"; diff --git a/app/src/main/res/drawable/ic_add_black.png b/app/src/main/res/drawable/ic_add_black.png new file mode 100644 index 0000000000000000000000000000000000000000..a633259ea45269cb387ef27f1301009e05578617 GIT binary patch literal 201 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6m;}-aSX}0_x6e-F9QPyv*DlFZTf5+ieJ(etatU8q%v3gb^{FTYb;7|aB!&OfB~R< b=31t&4=&kA&-A(iG>pO1)z4*}Q$iB}b3r%* literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_controlcenter.xml b/app/src/main/res/layout/activity_controlcenter.xml index 39a5d2c8..832b2e5c 100644 --- a/app/src/main/res/layout/activity_controlcenter.xml +++ b/app/src/main/res/layout/activity_controlcenter.xml @@ -1,6 +1,9 @@ + + diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml index dfd096b5..80bee978 100644 --- a/app/src/main/res/values-v21/styles.xml +++ b/app/src/main/res/values-v21/styles.xml @@ -1,10 +1,13 @@ - - - - - - From f15a97d9947ea60548bb058c9683c6a91576d5cc Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 12 Apr 2016 23:12:15 +0200 Subject: [PATCH 223/274] Initial live heartrate measurement in the live activity tab #178 --- .../activities/HeartRateUtils.java | 6 + .../charts/AbstractChartFragment.java | 5 +- .../charts/ActivitySleepChartFragment.java | 5 +- .../charts/LiveActivityFragment.java | 118 ++++++++++++++---- .../activities/charts/SleepChartFragment.java | 5 +- .../gadgetbridge/devices/EventHandler.java | 2 + .../gadgetbridge/impl/GBDeviceService.java | 7 ++ .../gadgetbridge/model/DeviceService.java | 3 + .../gadgetbridge/model/Measurement.java | 33 +++++ .../service/DeviceCommunicationService.java | 6 + .../service/ServiceDeviceSupport.java | 8 ++ .../service/devices/miband/MiBandSupport.java | 31 ++++- .../serial/AbstractSerialDeviceSupport.java | 6 + .../service/serial/GBDeviceProtocol.java | 2 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 1 + .../service/TestDeviceSupport.java | 5 + 17 files changed, 213 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java new file mode 100644 index 00000000..9ac085d7 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java @@ -0,0 +1,6 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + +public class HeartRateUtils { + public static final int MAX_HEART_RATE_VALUE = 250; + public static final int MIN_HEART_RATE_VALUE = 0; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index c32752d3..75a3c13e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -39,6 +39,7 @@ import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment; +import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.database.DBAccess; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; @@ -116,6 +117,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected int CHART_TEXT_COLOR; protected int LEGEND_TEXT_COLOR; protected int HEARTRATE_COLOR; + protected int HEARTRATE_FILL_COLOR; protected int AK_ACTIVITY_COLOR; protected int AK_DEEP_SLEEP_COLOR; protected int AK_LIGHT_SLEEP_COLOR; @@ -152,6 +154,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext); LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext); HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate); + HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill); AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light); AK_DEEP_SLEEP_COLOR = getResources().getColor(R.color.chart_light_sleep_light); AK_LIGHT_SLEEP_COLOR = getResources().getColor(R.color.chart_deep_sleep_light); @@ -518,7 +521,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } protected boolean isValidHeartRateValue(int value) { - return value > 0 && value < 255; + return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE; } /** diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java index 7c58693e..7260a205 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; @@ -83,8 +84,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment { yAxisRight.setDrawLabels(true); yAxisRight.setDrawTopYLabelEntry(true); yAxisRight.setTextColor(CHART_TEXT_COLOR); - yAxisRight.setAxisMaxValue(250); - yAxisRight.setAxisMinValue(0); + yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE); + yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE); // refresh immediately instead of use refreshIfVisible(), for perceived performance refresh(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index f69e2ae5..cd014362 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -23,6 +23,7 @@ import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.BarDataSet; import com.github.mikephil.charting.data.BarEntry; +import com.github.mikephil.charting.data.ChartData; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; @@ -39,10 +40,12 @@ import java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; +import nodomain.freeyourgadget.gadgetbridge.model.Measurement; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class LiveActivityFragment extends AbstractChartFragment { @@ -63,6 +66,9 @@ public class LiveActivityFragment extends AbstractChartFragment { private final Steps mSteps = new Steps(); private ScheduledExecutorService pulseScheduler; private int maxStepsResetCounter; + private List heartRateValues; + private LineDataSet mHeartRateSet; + private int mHeartRate; private class Steps { private int initialSteps; @@ -145,16 +151,36 @@ public class LiveActivityFragment extends AbstractChartFragment { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); switch (action) { - case DeviceService.ACTION_REALTIME_STEPS: + case DeviceService.ACTION_REALTIME_STEPS: { int steps = intent.getIntExtra(DeviceService.EXTRA_REALTIME_STEPS, 0); long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); - refreshCurrentSteps(steps, timestamp); + addEntries(steps, timestamp); break; + } + case DeviceService.ACTION_HEARTRATE_MEASUREMENT: { + int heartRate = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, 0); + long timestamp = intent.getLongExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + if (isValidHeartRateValue(heartRate)) { + setCurrentHeartRate(heartRate, timestamp); + } + break; + } } } }; - private void refreshCurrentSteps(int steps, long timestamp) { + private void setCurrentHeartRate(int heartRate, long timestamp) { + addHistoryDataSet(true); + mHeartRate = heartRate; + } + + private int getCurrentHeartRate() { + int result = mHeartRate; + mHeartRate = -1; + return result; + } + + private void addEntries(int steps, long timestamp) { mSteps.updateCurrentSteps(steps, timestamp); if (++maxStepsResetCounter > RESET_COUNT) { maxStepsResetCounter = 0; @@ -163,10 +189,10 @@ public class LiveActivityFragment extends AbstractChartFragment { // Or: count down the steps until goal reached? And then flash GOAL REACHED -> Set stretch goal LOG.info("Steps: " + steps + ", total: " + mSteps.getTotalSteps() + ", current: " + mSteps.getStepsPerMinute(false)); -// refreshCurrentSteps(); +// addEntries(); } - private void refreshCurrentSteps() { + private void addEntries() { mTotalStepsChart.setSingleEntryYValue(mSteps.getTotalSteps()); YAxis stepsPerMinuteCurrentYAxis = mStepsPerMinuteCurrentChart.getAxisLeft(); int maxStepsPerMinute = mSteps.getMaxStepsPerMinute(); @@ -180,24 +206,36 @@ public class LiveActivityFragment extends AbstractChartFragment { int stepsPerMinute = mSteps.getStepsPerMinute(true); mStepsPerMinuteCurrentChart.setSingleEntryYValue(stepsPerMinute); - if (mStepsPerMinuteHistoryChart.getData() == null) { - if (mSteps.getTotalSteps() == 0) { - return; // ignore the first default value to keep the "no-data-description" visible - } - LineData data = new LineData(); - mStepsPerMinuteHistoryChart.setData(data); - data.addDataSet(mHistorySet); + if (!addHistoryDataSet(false)) { + return; } - LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData(); - historyData.addXValue(""); - historyData.addEntry(new Entry(stepsPerMinute, mHistorySet.getEntryCount()), 0); + ChartData data = mStepsPerMinuteHistoryChart.getData(); + data.addXValue(""); + if (stepsPerMinute < 0) { + stepsPerMinute = 0; + } + mHistorySet.addEntry(new Entry(stepsPerMinute, data.getXValCount() - 1)); + int hr = getCurrentHeartRate(); + if (hr < 0) { + hr = 0; + } + mHeartRateSet.addEntry(new Entry(hr, data.getXValCount() - 1)); + } - mTotalStepsData.notifyDataSetChanged(); - mStepsPerMinuteData.notifyDataSetChanged(); - mStepsPerMinuteHistoryChart.notifyDataSetChanged(); - - renderCharts(); + private boolean addHistoryDataSet(boolean force) { + if (mStepsPerMinuteHistoryChart.getData() == null) { + // ignore the first default value to keep the "no-data-description" visible + if (force || mSteps.getTotalSteps() > 0) { + LineData data = new LineData(); + data.addDataSet(mHistorySet); + data.addDataSet(mHeartRateSet); + mStepsPerMinuteHistoryChart.setData(data); + return true; + } + return false; + } + return true; } @Nullable @@ -205,6 +243,8 @@ public class LiveActivityFragment extends AbstractChartFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { IntentFilter filterLocal = new IntentFilter(); filterLocal.addAction(DeviceService.ACTION_REALTIME_STEPS); + filterLocal.addAction(DeviceService.ACTION_HEARTRATE_MEASUREMENT); + heartRateValues = new ArrayList<>(); View rootView = inflater.inflate(R.layout.fragment_live_activity, container, false); @@ -262,7 +302,22 @@ public class LiveActivityFragment extends AbstractChartFragment { * Called in the UI thread. */ private void pulse() { - refreshCurrentSteps(); + addEntries(); + + LineData historyData = (LineData) mStepsPerMinuteHistoryChart.getData(); + if (historyData == null) { + return; + } + + historyData.notifyDataChanged(); + mTotalStepsData.notifyDataSetChanged(); + mStepsPerMinuteData.notifyDataSetChanged(); + mStepsPerMinuteHistoryChart.notifyDataSetChanged(); + + renderCharts(); + + // have to enable it again and again to keep it measureing + GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true); } private long getPulseIntervalMillis() { @@ -272,6 +327,7 @@ public class LiveActivityFragment extends AbstractChartFragment { @Override protected void onMadeVisibleInActivity() { GBApplication.deviceService().onEnableRealtimeSteps(true); + GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(true); super.onMadeVisibleInActivity(); if (getActivity() != null) { getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); @@ -281,6 +337,7 @@ public class LiveActivityFragment extends AbstractChartFragment { @Override protected void onMadeInvisibleInActivity() { GBApplication.deviceService().onEnableRealtimeSteps(false); + GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(false); if (getActivity() != null) { getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } @@ -346,6 +403,7 @@ public class LiveActivityFragment extends AbstractChartFragment { private void setupHistoryChart(BarLineChartBase chart) { configureBarLineChartDefaults(chart); + chart.setTouchEnabled(false); // no zooming or anything, because it's updated all the time chart.setBackgroundColor(BACKGROUND_COLOR); chart.setDescriptionColor(DESCRIPTION_COLOR); chart.setDescription(getString(R.string.live_activity_steps_per_minute_history)); @@ -367,22 +425,34 @@ public class LiveActivityFragment extends AbstractChartFragment { y.setDrawGridLines(false); y.setDrawTopYLabelEntry(false); y.setTextColor(CHART_TEXT_COLOR); - y.setEnabled(true); + y.setAxisMinValue(0); YAxis yAxisRight = chart.getAxisRight(); yAxisRight.setDrawGridLines(false); - yAxisRight.setEnabled(false); - yAxisRight.setDrawLabels(false); + yAxisRight.setEnabled(true); + yAxisRight.setDrawLabels(true); yAxisRight.setDrawTopYLabelEntry(false); yAxisRight.setTextColor(CHART_TEXT_COLOR); + yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE); + yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE); mHistorySet = new LineDataSet(new ArrayList(), getString(R.string.live_activity_steps_history)); + mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT); mHistorySet.setColor(akActivity.color); mHistorySet.setDrawCircles(false); mHistorySet.setDrawCubic(true); mHistorySet.setDrawFilled(true); mHistorySet.setDrawValues(false); + + mHeartRateSet = new LineDataSet(new ArrayList(), getString(R.string.live_activity_heart_rate)); + mHeartRateSet.setAxisDependency(YAxis.AxisDependency.RIGHT); + mHeartRateSet.setColor(HEARTRATE_COLOR); + mHeartRateSet.setDrawCircles(false); + mHeartRateSet.setDrawCubic(true); + mHeartRateSet.setDrawFilled(false); +// mHeartRateSet.setFillColor(HEARTRATE_FILL_COLOR); + mHeartRateSet.setDrawValues(false); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index 4f18495d..48ff18f8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount; @@ -174,8 +175,8 @@ public class SleepChartFragment extends AbstractChartFragment { yAxisRight.setDrawLabels(true); yAxisRight.setDrawTopYLabelEntry(true); yAxisRight.setTextColor(CHART_TEXT_COLOR); - yAxisRight.setAxisMaxValue(250); - yAxisRight.setAxisMinValue(0); + yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE); + yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE); } protected void setupLegend(Chart chart) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 5ddad49e..a6dd21bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -44,6 +44,8 @@ public interface EventHandler { void onHeartRateTest(); + void onEnableRealtimeHeartRateMeasurement(boolean enable); + void onFindDevice(boolean start); void onScreenshotReq(); 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 add9f676..61af3fcb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -216,4 +216,11 @@ public class GBDeviceService implements DeviceService { .putExtra(EXTRA_BOOLEAN_ENABLE, enable); invokeService(intent); } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + Intent intent = createIntent().setAction(ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT) + .putExtra(EXTRA_BOOLEAN_ENABLE, enable); + invokeService(intent); + } } 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 7c38088f..2cf979cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -33,7 +33,9 @@ public interface DeviceService extends EventHandler { String ACTION_SET_ALARMS = PREFIX + ".action.set_alarms"; String ACTION_ENABLE_REALTIME_STEPS = PREFIX + ".action.enable_realtime_steps"; String ACTION_REALTIME_STEPS = PREFIX + ".action.realtime_steps"; + String ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT = PREFIX + ".action.realtime_hr_measurement"; String ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT = PREFIX + ".action.enable_heartrate_sleep_support"; + String ACTION_HEARTRATE_MEASUREMENT = PREFIX + ".action.hr_measurement"; String EXTRA_DEVICE_ADDRESS = "device_address"; String EXTRA_NOTIFICATION_BODY = "notification_body"; String EXTRA_NOTIFICATION_FLAGS = "notification_flags"; @@ -62,6 +64,7 @@ public interface DeviceService extends EventHandler { String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; String EXTRA_REALTIME_STEPS = "realtime_steps"; String EXTRA_TIMESTAMP = "timestamp"; + String EXTRA_HEART_RATE_VALUE = "hr_value"; void start(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java new file mode 100644 index 00000000..f841da00 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java @@ -0,0 +1,33 @@ +package nodomain.freeyourgadget.gadgetbridge.model; + +public class Measurement { + private final int value; + private final long timestamp; + + public Measurement(int value, long timestamp) { + this.value = value; + this.timestamp = timestamp; + } + + public int getValue() { + return value; + } + + public long getTimestamp() { + return timestamp; + } + + @Override + public int hashCode() { + return (int) (71 ^ value ^ timestamp); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Measurement) { + Measurement m = (Measurement) o; + return timestamp == m.timestamp && value == m.value; + } + return super.equals(o); + } +} 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 6ade401d..85e3ceb7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -46,6 +46,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CO import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DELETEAPP; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DISCONNECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE; @@ -349,6 +350,11 @@ public class DeviceCommunicationService extends Service { mDeviceSupport.onEnableHeartRateSleepSupport(enable); break; } + case ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT: { + boolean enable = intent.getBooleanExtra(EXTRA_BOOLEAN_ENABLE, false); + mDeviceSupport.onEnableRealtimeHeartRateMeasurement(enable); + break; + } } return START_STICKY; 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 e54e61bd..2a0de20e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -250,4 +250,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onEnableHeartRateSleepSupport(enable); } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + if (checkBusy("enable realtime heart rate measurement: " + enable)) { + return; + } + delegate.onEnableRealtimeHeartRateMeasurement(enable); + } } 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 37b22591..9eec7cf5 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 @@ -603,7 +603,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = performInitialized("HeartRateTest"); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementManual); builder.queue(getQueue()); } catch (IOException ex) { @@ -614,6 +613,25 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + if (supportsHeartRate()) { + try { + TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); + if (enable) { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); + } else { + builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); + } + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to enable realtime heart rate measurement in MI1S", ex); + } + } + } + public boolean supportsHeartRate() { return getDeviceInfo() != null && getDeviceInfo().supportsHeartrate(); } @@ -745,7 +763,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) { handleRealtimeSteps(characteristic.getValue()); } else if (MiBandService.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) { - logHeartrate(characteristic.getValue()); + handleHeartrate(characteristic.getValue()); } else { LOG.info("Unhandled characteristic changed: " + characteristicUUID); logMessageContent(characteristic.getValue()); @@ -814,6 +832,15 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } + private void handleHeartrate(byte[] value) { + if (value.length == 2 && value[0] == 6) { + int hrValue = (value[1] & 0xff); + Intent intent = new Intent(DeviceService.ACTION_HEARTRATE_MEASUREMENT) + .putExtra(DeviceService.EXTRA_HEART_RATE_VALUE, hrValue) + .putExtra(DeviceService.EXTRA_TIMESTAMP, System.currentTimeMillis()); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + } private void handleRealtimeSteps(byte[] value) { int steps = 0xff & value[0] | (0xff & value[1]) << 8; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index aa78a393..5dfa8b2a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -181,4 +181,10 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport byte[] bytes = gbDeviceProtocol.encodeEnableHeartRateSleepSupport(enable); sendToDevice(bytes); } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + byte[] bytes = gbDeviceProtocol.encodeEnableRealtimeHeartRateMeasurement(enable); + sendToDevice(bytes); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 2aa1fb66..bdf1b101 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -63,6 +63,8 @@ public abstract class GBDeviceProtocol { return null; } + public byte[] encodeEnableRealtimeHeartRateMeasurement(boolean enable) { return null; } + public GBDeviceEvent[] decodeResponse(byte[] responseData) { return null; } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 82744a0c..5f53994c 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,6 +12,7 @@ #1f000000 #ffab40 + #fadab1 #0071b7 #4c5aff diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index beac2a5d..fa365a9b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -240,5 +240,6 @@ Firmware update in progress Firmware not sent Heart Rate + Heart Rate diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java index 19fd1810..9d486bc0 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/TestDeviceSupport.java @@ -129,4 +129,9 @@ public class TestDeviceSupport extends AbstractDeviceSupport { public void onEnableHeartRateSleepSupport(boolean enable) { } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } } From 78bf51689752d7db755e43406b6f082efee40977 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 12 Apr 2016 23:25:12 +0200 Subject: [PATCH 224/274] Disabling sleep measurement for continuous measurement is not necessary Looks like they don't interfere, after all. #178 --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 1 - 1 file changed, 1 deletion(-) 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 9eec7cf5..97357ccf 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 @@ -620,7 +620,6 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = performInitialized("EnableRealtimeHeartRateMeasurement"); if (enable) { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementManual); - builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementSleep); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), startHeartMeasurementContinuous); } else { builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT), stopHeartMeasurementContinuous); From ae5d9089d884fbc9c1ec24d4b82e79930a1e1304 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 13 Apr 2016 21:21:10 +0200 Subject: [PATCH 225/274] Slight improvement to hr charts #178 --- .../activities/charts/AbstractChartFragment.java | 12 ++++-------- .../activities/charts/LiveActivityFragment.java | 8 +------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 75a3c13e..60c6dfa0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -565,23 +565,19 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected LineDataSet createHeartrateSet(List values, String label) { LineDataSet set1 = new LineDataSet(values, label); + set1.setLineWidth(0.8f); set1.setColor(HEARTRATE_COLOR); -// set1.setColors(colors); set1.setDrawCubic(true); set1.setCubicIntensity(0.1f); -// //set1.setDrawFilled(true); -// set1.setDrawCircles(false); -// set1.setLineWidth(2f); - set1.setDrawCircles(false); // set1.setCircleRadius(2f); // set1.setDrawFilled(true); - - set1.setLineWidth(0.8f); +// set1.setColor(getResources().getColor(android.R.color.background_light)); +// set1.setCircleColor(HEARTRATE_COLOR); // set1.setFillColor(ColorTemplate.getHoloBlue()); - set1.setDrawValues(true); // set1.setHighLightColor(Color.rgb(128, 0, 255)); // set1.setColor(Color.rgb(89, 178, 44)); + set1.setDrawValues(true); set1.setValueTextColor(CHART_TEXT_COLOR); set1.setAxisDependency(YAxis.AxisDependency.RIGHT); return set1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index cd014362..ef1dcd07 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -445,13 +445,7 @@ public class LiveActivityFragment extends AbstractChartFragment { mHistorySet.setDrawFilled(true); mHistorySet.setDrawValues(false); - mHeartRateSet = new LineDataSet(new ArrayList(), getString(R.string.live_activity_heart_rate)); - mHeartRateSet.setAxisDependency(YAxis.AxisDependency.RIGHT); - mHeartRateSet.setColor(HEARTRATE_COLOR); - mHeartRateSet.setDrawCircles(false); - mHeartRateSet.setDrawCubic(true); - mHeartRateSet.setDrawFilled(false); -// mHeartRateSet.setFillColor(HEARTRATE_FILL_COLOR); + mHeartRateSet = createHeartrateSet(new ArrayList(), getString(R.string.live_activity_heart_rate)); mHeartRateSet.setDrawValues(false); } From f52126ed36013970c8e0e423c18ae36f8bbf46f3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 13 Apr 2016 21:21:25 +0200 Subject: [PATCH 226/274] Update dependencies --- app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b87fb8a0..b58658fa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,12 +46,12 @@ dependencies { testCompile "org.mockito:mockito-core:1.9.5" compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:23.1.1' - compile 'com.android.support:support-v4:23.1.1' + compile 'com.android.support:appcompat-v7:23.3.0' + compile 'com.android.support:support-v4:23.3.0' compile 'com.android.support:design:23.3.0' compile 'com.github.tony19:logback-android-classic:1.1.1-4' compile 'org.slf4j:slf4j-api:1.7.7' - compile 'com.github.PhilJay:MPAndroidChart:v2.2.3' + compile 'com.github.PhilJay:MPAndroidChart:v2.2.4' compile 'com.github.pfichtner:durationformatter:0.1.1' compile 'de.cketti.library.changelog:ckchangelog:1.2.2' } From e87a357bede59fe35439c2c86eeaa0dc610efd57 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 13 Apr 2016 21:38:35 +0200 Subject: [PATCH 227/274] Show separate curves when the time between two measurements is too long #273 --- .../gadgetbridge/activities/HeartRateUtils.java | 8 ++++++++ .../activities/charts/AbstractChartFragment.java | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java index 9ac085d7..a4d39925 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java @@ -3,4 +3,12 @@ package nodomain.freeyourgadget.gadgetbridge.activities; public class HeartRateUtils { public static final int MAX_HEART_RATE_VALUE = 250; public static final int MIN_HEART_RATE_VALUE = 0; + /** + * The maxiumum gap between two hr measurements in which + * we interpolate between the measurements. Otherwise, two + * distinct measurements will be shown. + * + * Value is in minutes + */ + public static final int MAX_HR_MEASUREMENTS_GAP_MINUTES = 10; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 60c6dfa0..c8cb4d7c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -421,6 +421,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { boolean hr = supportsHeartrate(); List heartrateEntries = hr ? new ArrayList(numEntries) : null; List colors = new ArrayList<>(numEntries); // this is kinda inefficient... + int lastHrSampleIndex = -1; for (int i = 0; i < numEntries; i++) { ActivitySample sample = samples.get(i); @@ -463,7 +464,13 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } activityEntries.add(createBarEntry(value, i)); if (hr && isValidHeartRateValue(sample.getCustomValue())) { + if (lastHrSampleIndex > -1 && i - lastHrSampleIndex > HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) { + heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1)); + heartrateEntries.add(createLineEntry(0, i - 1)); + } + heartrateEntries.add(createLineEntry(sample.getCustomValue(), i)); + lastHrSampleIndex = i; } String xLabel = ""; From b25a47c3987dc63bd86b08535076f78a67c1ab86 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Wed, 13 Apr 2016 23:36:14 +0200 Subject: [PATCH 228/274] Immediately disable hr reading and activity tracking when leaving the tab #273 --- .../activities/charts/LiveActivityFragment.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index ef1dcd07..7b201372 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -267,16 +267,12 @@ public class LiveActivityFragment extends AbstractChartFragment { @Override public void onPause() { super.onPause(); - if (pulseScheduler != null) { - pulseScheduler.shutdownNow(); - pulseScheduler = null; - } + stopActivityPulse(); } @Override public void onResume() { super.onResume(); - pulseScheduler = startActivityPulse(); } private ScheduledExecutorService startActivityPulse() { @@ -298,6 +294,13 @@ public class LiveActivityFragment extends AbstractChartFragment { return service; } + private void stopActivityPulse() { + if (pulseScheduler != null) { + pulseScheduler.shutdownNow(); + pulseScheduler = null; + } + } + /** * Called in the UI thread. */ @@ -332,10 +335,12 @@ public class LiveActivityFragment extends AbstractChartFragment { if (getActivity() != null) { getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } + pulseScheduler = startActivityPulse(); } @Override protected void onMadeInvisibleInActivity() { + stopActivityPulse(); GBApplication.deviceService().onEnableRealtimeSteps(false); GBApplication.deviceService().onEnableRealtimeHeartRateMeasurement(false); if (getActivity() != null) { From 24cc3725d2b985748f250d54a62b059fb7d75b6e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 11:07:44 +0200 Subject: [PATCH 229/274] equalize size of connected and disconnected device icons --- .../ic_device_miband_disabled.png | Bin 971 -> 1281 bytes .../ic_device_pebble_disabled.png | Bin 1008 -> 1316 bytes .../ic_device_miband_disabled.png | Bin 584 -> 961 bytes .../ic_device_pebble_disabled.png | Bin 650 -> 1021 bytes .../ic_device_miband_disabled.png | Bin 1415 -> 1706 bytes .../ic_device_pebble_disabled.png | Bin 1351 -> 1653 bytes .../ic_device_miband_disabled.png | Bin 2316 -> 2593 bytes .../ic_device_pebble_disabled.png | Bin 2273 -> 2417 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/app/src/main/res/drawable-hdpi/ic_device_miband_disabled.png b/app/src/main/res/drawable-hdpi/ic_device_miband_disabled.png index 43cabe9b459b62ea347e293f62499b12728a780a..f9f7be9c705f2a0cffb739ef23c7ae77b9f3434f 100644 GIT binary patch literal 1281 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawSkfJR9T^xl_H+M9WCik>lDyqr z82-2SpV<%OaTa()7BeuY`~qRdEuGimfr9KMp1!W^4_Np(*<_kr7ffehU=H_maSX}0 z_jcClED=YMw*SGQca6_xINe&CH^oJ9LWN@g!jA3^y`!x=1V4&?5mn-9QqYKF;(8>V zBC4vQu;V263du!&mtMYGWu=#-X1o1@ac!s9|JARSt@!t9&aQ8k4okgmx-{SW&A=qJ zY-QpT!wW1HvI0LjIFi^DB00|aoBS}A=G!5@QoN(aB`GMc+rz}g@LS-YX6a>#*K+hH zy;}F?@j=zfgFB*}3$=vTR?d6~Jec?BE&YvAF+1q?%6b@bcscEVledvGk9J2}i(`HOr z7_G(MFgGgZ_bZ?GN~(=avxH`A>7~t+UvOjNyw`cFGzCi{IvCnt394&serlhf!`tk|cN%_m9_*~Hn4=M$vQu-3RHn4Wt_X+Nr6#I*-pdymI2UzW9ja$MTtMgmr`Oo-xc1e3`G$JQw4Xvp0q(x*!m80O2W=50ZGeC*O;Zu z$J1){>k3758m8fD!gV@5VMQa!{v`JaxIoU_4H;oi2zn zG}$IDzD_OqQthK}a(DOWJTJMcHBF~WQQ12zLvqH`<@vF^NWTsrm9}w`LXe zZc+MgP=06&5|E|yzi^8o8*jj%9*pat!UiC{FCi~nADy_Myb2}q*vW&S*F(T$$Zs+ z$Nh}U7wMlD{&_Ka(+8XAWf7+9H_SQ%Pq8yHy`807--H$NpatrE9}Qoc5!lIK`O{~v)|cB0Tl^?6oq7#G9D5|b0PQw#;4FXP*7h$?^F~NobCE$0v;$v zCy6t|TJV#b$zE%JS$pk0uvZES*x13q4&1SPbtEi}W($LWG=n|;h!I_(zt6f-FXqug!ZJwTkO_p)<)8`_cUTd`y9q)l4$Y z%*@nAqtThvwZoA$78io84r27Fv^~Y`yiA0f(Oi3zd3zAg?A55>vJi8wty&51BJe83lc>H<(S8Wo$Hn?jc5yb3g!LxgNd$*1Y z--ZE{OeSvtT3ube&~-p~HWNVm`}@4abic$Nsz{%mor#r|72f9mr;UvbeQfjTgW}rS zTHX$d@THrBBEM?z%PIq6Kn#chF(3xSfEW-1Vt+skhympdkP$C&W(^Ro0UHTWAQ0$w zI2<<0qW8Gvs_GgruE=7Lfq?;+Q7)1PgTYRM^QEe)>MdFlQ~|=5aT`4niL@96P;YN< zJ1&u9WuVPMO)CP-j4T=)9BeX5uSPf=ZmzDbexbGv;YveGC=^07+OggN4G#|=Q4OkB zntxa<*=p6ifJAFoqFQuJ*(8&M&Lkst`k`al1ark28yhVh9Ub*PpRdK^@wB_$?oOA> z)!o|K8dCI6Lkc;a&LGK8vHHATuZ!aTp{6h6PVG@t{omjmf!%;UzoL6aYeHP*<>jB# z1zBxxSjI&gjsOVlDyqr z82-2SpV<%OaTa()7BeuY`~qRdEuGimfr9KMp1!W^4_Np(*<_kr7ffehV6O6XaSX}0 z_jcCV>?;lu$L5!(O}e||k^7No!9N-Ltz307wy#|;%6;L#(9G`W@}I@FQiO%SDSc~z z7hc--RoY>!>u#92S&If-STkSVJY}h?w0mXBE6*j!f7d%P`PfyVrp{u+-WidSqMvR@>o@Z~wvzYgpVxl5P``Nd@uuca zXU?9M+kS3Yn8K&R?=^2-Lm1A?im4FK+dpX!-&Q+spy64^yN@s`UGeu>p&2RsFRN&l zgI!iqOk3VU8-_5i!k6vFHyyHAxiXw|R$9!rdcXL_#(A&xR&ok%N$p^0KP6--`s>&x zuPH6T)A&p^W~Jt9sOjc>bO>7E!MV|??LhA903W60rOVfMxX#qM_N?pHm4ulcoEuWY zg9CTC?k$L_^_;lSdB>(I)9zN5kH-r#zrNDkr&j01rO>$5RqN(0h1nl7b8Zx!wlcl2 znStixTsw#R1@>3#V?8AA zUzir{CcAsNF8iyNEXJY;1JR2+Ur*SeC~SR)H6>x^gs`OQvNdKYGkH_0nf;4q%4%O0 zaWVUOG4Dy%`8V6&N+$KkN@sh8U7X3J_(a2?&6aI@%8IVa*AlTg?ZORw|JTj9VfcC5 z#$W5#JHJW1$RZ!IkXNbq!`}##;~8ZiZvRexa`9{Wy)90HxyEhBKAkzNz1H@nTy1s# zy4WepOK)mT)A^*R>>ZXNIb*wjTI}sQwk59{?Rrno_dB^&^`TUz>$lujGg?EX6kGki zc$Uj;dojx(^vuU4kzJxw5*;n4%uRZ9!Q$4dF4ybr+vTSH5b4zX=GZUE!Ah=_uXDfHU$&pNOi#-A_{SN* zjLB2t8d2g{T9T1p#86R?kylaw;0-8`CxgVD^9w4AGSf3k7@VCI97{@yGLuS6GV}9v z{qpmi^K)}ki;^=Fb993<5{ptDb27726*5Xn3as??lZ#RlOESw+lYxrz^NRKIi_-OT zGLuvDic^dAlPY!dl68v{^^Nrm^zX4qJq22)3bHOGu_Vwjs#p0K+wpEkZh(S$E>LXx8R8&+XiEkv|!UsN9?gcjrD!9;{8{MfO$Q|b&Zi106 zj1%SF$vN;R+1$BjewjHlckVgK$RHylBO@atBO^=33|vMolYdTRRDCu)6dY4&Y3bwK z+}x+x+1bytva&L@DEMYJo1YaH7Cx!0tbBA0)cwJknVBh@o13eioSdu~A0M{`gF#0q z6sm{D#>O-w_@nw}=YG~sPf!1vnwl!_>+3V#8=Oca667~K=Qmckn9ihd-_K@eXM-xF zl#V3B#Kc6|$bZPl*}%X6)3eZra5!9kBQ}Z0oS~tipZo@j-T|Qx{r&yF5t|T4MIl93 zD+IZoo}NFl0P5`QbXY8w*9hd-Al(fRqCZxHKp9~Uq%=8Wj$D}weU0GQXlC(#z zbZAe2=*1zudL}@W{z3^guw~l1T3LdYd_%FU7^hZ&8vU1PB)+8$A#R)XD;=y}i8wN#vLrD6MUtUo?qcy>=f`Wq2v4AXE8+PlUjT-?HHjn^? zW^e=eXGJqQ>wGBPqUGVRzEflIQ| T(_a&O00000NkvXXu0mjfAsyC3 diff --git a/app/src/main/res/drawable-mdpi/ic_device_miband_disabled.png b/app/src/main/res/drawable-mdpi/ic_device_miband_disabled.png index c1d9e92abf1cb4639867084c5f34e3ff9f64d5d5..9d9f007a908867cf9a80bd5a060241938917228d 100644 GIT binary patch literal 961 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|}ByV>Y zhX3vTXZ8bmoCO|{#S9D*%R!jY)6RA;P>{XE)7O>#0Sg}|t2IxOn>|pIxTlL_NW|f{ z(;d4nJBS?rpFgQ*ZeHtBZtZugHY^C@=c;S^`bhkPa>d6duJ(=h9c(z8()Ay)-{`Jm zQdFpp;MeNaxU@kp`gV%iXPYISm#<#g!s)c|z0sLH5C6~po;GPF`noI_W7ts zpwsqj2D^eo%6ds%NlR8d=d*cwVG>8$`7GfV!Pc%jg1>*R{x$RN%8BzjJJ)}D*laGT z6s@gU^~rKyNW2_F%KxwP@-K&UmUsl7zskGD&UA0_qIDTCh%qmJTUqGjn!di6rY4S_l85u1Q;0c4 z$>4r$ORi$m_nsXme;wc{-7;~-oEJ}6E$;?hw$S5f(u+rC0E=o--$t+7v1}e(WE7r>|O4rZHOis-! zPA%3?s?^O()-6udH`X)IzsDl=6lk3)$hwrol2j|O)>H-~14C0?0}EY4;}8P_D^n9I zLkn#KBP#=g-RB(&Q8eV{r(~v8;?{6#(vP!14U!-mg7ec#%7Kn$C@(M9%goCzPEIUH x1-WkaI~ysWB0-R%kj#>tR0iKnr^LJz1<&OCyrnUEz}&{b;OXk;vd$@?2>^Y~g<${y delta 570 zcmV-A0>%Bo2gn4F8Gi-<00374`G)`i0uM<yy|H!0Yv13xd$14Zv|+mjJvvoz7b*Cffo) z)wxL~lkFn_=zqCfE(@tlcL;z02!H?xa9jYjT21Qr`_gbY+)1z&d{6-xkH^wtv5=O_ z%w{ubGMW6I%yc?kN#1Gr4c^(b7JMSW z$y_1OXf#soBL-hsM)98*0T2KI5C8%GHnxrgpzC(K-+z=gwisx26M!ad0G{VN1mFoa zR7q=5&$8^fD2jF30D>SiVE17@p}0dGjYi|CREk>i5IYu&@$1esqf=^;NW>Tphk5dB zhC-oEAQ0#UgTZgDMPhb|+9YPe@AsSb9Wfc;{*fwhPmc1LJk46l;c&cIt=31VI&h8E zn3b)D9WyX5fF3=-Xeo#R1!SE(qf^P`sUdSfsWzoVL;KJ60~_X3&B}UO`2YX_07*qo IM6N<$g0NZzy8r+H diff --git a/app/src/main/res/drawable-mdpi/ic_device_pebble_disabled.png b/app/src/main/res/drawable-mdpi/ic_device_pebble_disabled.png index 675a26a7f67a2a73f579abc02303abac38ecec79..924fcb587ec11408245283bfa955275dcdc9c497 100644 GIT binary patch literal 1021 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tmUKs7M+SzC{oH>NS%G|}ByV>Y zhX3vTXZ8bmoCO|{#S9D*%R!jY)6RA;P>{XE)7O>#0Sg}|t2IxOn>|odu&0Y-NW|f* z)6ZuMJBl3NpPyEKMoo8$!oL+tZx>#g{J_;FV%I{vZF+uNajaIrf7qxb-w z2>vQTrg>E>FNqx4aV2S1N_zRc_P2RKx9|FF5?**e*eL(`@0pePMzh)Hludj(W9^PR zJxpFNs#p(%`|UWsct)TxSH{)fPuV9eb20esc0Bj*aUQ{Y27f-St4kEqUh+I3DcrW^ zgPwCr_R@xVb=`X5+u0emelFXYdMTu{L}NjyzjZ{d*4`cRF@eQQQy5>&y3nP4i|?qw z=M`&DC@`BSu6%F(nLUB&;s0q4aZlu~uAcMnF~dnE-@X%VLdzM{mR-51U7f?5&0zPi z*71*79@CEwepR`p44j+}eVyG#-9-+oKJN$=*qJargG@TwT1}g(r!N$#c4C@O6 z1Tr5?fMxVHqX*rGw;+@$Mj8{lbNnJ1;u>s$|*ZR1kX z7JF~SeSp74uj5QOtBMDMO7f$p&sU0U=sbJrr1TAm-OYa#Jj(yqG6uYx#{4Uf(QSr^ z5`#jE(Ug$QHd=B=3h(y3dBCk)bW{FFtZjyh;A{fc)-9Z$E+nZIa4 z;y3y0^De8hzs~2Mm-BkfOxc%Qv$nkYyJj`pe8VEE?98OMvPbT|57hr(DD!gi)QE2j zlh&JsE7d^H~@?I#(ZBf8jOD9ivk%taJZ8{Qm6AKaR_5|M>r9)jqTC z{^Y+M-+(EOr^Gd)#Idv_Bfp5Dq97x$qW-~~GeGfVkhpVxL1j^9dPWI@v$KL@Nl8&= zQfWzMex9yhex7rFZfrx_(Y(a%x_2YO#J&rEXrbZgHZ%v7UkcJr=2_KSs57YKJQS7q9HdwB{QuOw}w-bew+nrkObKfoS#-w4sBu34#=bWR~QlG6bY1CFP_lcqZrPb?s;P3zTH=boFyt I=akR{0LcWN7ytkO delta 636 zcmV-?0)zei2Z{xd8Gi-<00374`G)`i0#QjsK~#9!?U~7L;y@6F!I_QBa?3)j5^vUINeQ`RSj$;aacz?a#&yh&v!;2v*m5QU) zY6*=-`3P#_}oT`rdeh=nt{8g^Vl zxXBf%;GZPR@(s`Pr$)CFhkFqw_ecX|v)Ox($MX{*_A8+eARG>Vl_cqwG=LxoBLKMY z`~BZBndk+;tbg+@m&@Hz0H6c{0T*PZAEpC9mQbhDsisb+qdTZ}yM34kS&kgQU@%a3 zyPc--c&r;hQIx|xyayEki^W3icDuTSW2pmpuBZUmZnx@sy;fJNl`i2}oI?%3X0uUC zrIPM<0LS7S>Hsji%;)oe3dc|jFdPp5yXSEXGXS&MOn(!z0F%i?6Vm|rwZp1OwIE6TJkrDc}AWqSk{ W{?KvS332-X0000T-OgI#DO{ z5zazH>70C=4k{m!SH3!=^)7J;rxVlG@y`3#d;WO;c%J9F@9Vy=`}ym6E(Q2`8S0zq z0{~#?O(X=VXY^L-LDZdC)OlY$AaOWf8~`*G7^tY)YOERK<3#{Aa|;GK)d|AHhrP-`o z>(*Y_T^MdVs#WznWj}*b)c|i@7?b#rg&&4n?~xZqBpy9ulAzE-*?l?Lu!Qo0W3z7v5wCc zss@_B=s#0aIyQ0}$O-un{yoWf41yh1a`n>MH(OviRN1}MxwP0QmHrd`&UnTVM# z|LC+l^rcjjOiDtXE{0692)Na@~5`A%ql`&m6Ts*`k)VP zB-63*#r|d4mWBE?@y5x@c-6UY!g_=5yd9s>YR)HP2it`2hqV0BoBhxEvu;IXP0wH` z%=ch`<5l;UA^!_hmf9hw`gCI8lV81e4p-+7O)Jn}OHv_(+7|t5PJ221cgiyj-d`0f znr%rXC4atN2U?ma6I8@M!o#gJ0sO$T?R!sy!v`?MwTv!{vcKNwK*EP*O-~!1-&@8S zeQc9BNN&hQF&!B@fUv6v_uk+?*q^gNoVc_w_U^Q-e`Xg9+D*E0{qCf_3)}o9#bk>4 z%jlBpYIUfsOPbCBBRkQtwulZv*Tkbwl$L0V$@c-|Hy_#qp~Ho($PL+I#r@@*p^1vwcnNwrqfjZ@{9#LlN1&j-9B zS6%$$tXn(naD}J$>z}Z3W#*ZxV4son<`z8l;i64nyANMGp!pf4^yZn++g}32(?6!{ z=)dr}M!E~{Gey%FsWilnmz$^}Uk|N>oL`MZyONaiRWa8$0m+N0edKo(d-ZwT26~2p zI3dX7@_{5ClarJ=yEvfcX>nt741VNq)5=rCJsURznPE(7x9DzNTABSYKXmpN%E0gcL|Z*5oh5o@Lv?c5TDus}8)@MYnt aoLIOwjmxP%W3PTI0O0NEM`*xDpZgETcJ=N6 literal 1415 zcmai!{Xf$Q0LQ{&Jmuj$l!wZ_ zQVv&n$d+t6=AjrVA{XXb<2*D%=U(^2{dV_yeIEXR&+GHfc0K1HBdID00Dz3+8C$|$ zlm9kcd@tXZUNQ#&@SLNqH8F8gP}zTezn3EWN*y2Xa%Ssx5ykl-BwL2yZs%Wv3wLra zX!JYOQyjLf0MjCi>5Gn&rQpZXz%w)a9r212KM+Bq#FMLP zdU&hMH^BZE_-aQnrmM>5@zyGwU)Mu^5spMMDTk@MEl<6c6N4FxL&;YiRPM+ zuyLQ{Ie~;TSe)aSQ3blRv8m~LdR<*zUTkcvrLrvjHpoKjT3BrMtgWqmo(+ZDu0bCA zHFaoeY9ccj2US(KsAMwcogm?qKq&lbLpN9y2$Gfi(~zibxvi=FiRs*_dFS)4Hbd6x6HaM{?#{QW9x;l_$PUIKrUS~vu6d?D}h@? zWx?u!K9<8}-pI&`t+dr=0F}<;@$P7d9=Yl1A*`!*bXl%9UJEwp(0rxV*O4hIxooVh z@>@jlyGKR3?sGjY>TO1M6_|+B_V#uSqt(I;O9vgYg(Q_VCT5j0tC5?VI~4OBbP?fm zpLa8)#lpjR;6Xk}I;2r@>t_&X4-XHoHZ?Ihc{o)87#ka-QV6OpI_`wCXMgj5i*S`> za*w4pG&Gz-%)qQXJUlLYdU__=+q2Cm8;7A*iwPOVKz;~pStoU1U_b(f>eB8z4Zy7a zOO0YPaVP3uyl@PsQtfTOjsiL&F z$E~ieZ^9XjfP)nc5ON83aPVwqW@aOTLE5LlPRMr7QUhvhYY7;XtN(J(vx#j+cLL<7 zfq^53!)XLln+*&NFQA!AOG_Eh*tj^YsHiB%j*gD#D?LMaJpPwKnv9S|TTwwj(P7{9 z2GITt-uSq79!Jx~#pT6*>hBS7y6Efr%Fb&SCE-d^S8Ut&Bh)NF1Hfc0K$xMMbW3k9 z1W?_ptxG{nPERuy6saCuqT$cFV%x;~V% z+fs3%HQH`~%XKuo=@e)-7o&OIY%URBo`S>x(ErnVyi-tjjTvNgbkxtc8}o#!f)D8e zXNuvB^1ieOO3~ipfKDO*)%^TC6eqw#Tz!3gkEDIAWRXSkh3;;^VtE$t`p0q+Hg#)j z3%!UA#Z?G{Vkre|z0qHdA>TG5Cj=XTd!qv-!7%kHEu$~Rk@~IhEzbfpj&+Kh%;Xtq)krgPY@( zm6h!>mUOgHsUa5IjYelUR6KtZiL1SPHQ-t#ytS z_~7N~X^GX>r@8t0`5hs85{bkB5-BV@o|RWnP~cTxU*D^I?Sn8UCkN`5w5!Xw6v+QD zpWFjyd;u=;XA%!)Y!XvATj+}}e>QCVN?0KMLc+h5XfXGp#Wu!;I) zsx68iY@rZ?f6O!LycKkBNiXSZv7C?czew|++=Y;HH43d>R;lj+7I3sXXWMj|oc0&J CCupqz diff --git a/app/src/main/res/drawable-xhdpi/ic_device_pebble_disabled.png b/app/src/main/res/drawable-xhdpi/ic_device_pebble_disabled.png index 705aad25e230438695bf0cdb5eae70775ca696c5..f231da8ec84dcb4df0b68be1c7e45c3d319c61ba 100644 GIT binary patch literal 1653 zcmZ`(ZCujl8h#*rK~s-TYr1Jnt4#$#&GHaxBnH+`%~F1*mLdKHM8M#WqEJJRuaxgg zOOKkZrJ~c!UPeiJ&X!76Dyb}OW~Q6Ad^?+E=H{Sp`*1$o&-J^n=eq9udcHgbheN3r zW)5Zm0E=K6<%k|n8p;%g?}iX?~U^h3#4tjw791;u6Qx~Yy1W>h<#}#?#uVhe~<@k%^#}FrpwQ)PFYqu ziuW6Ie|s7so6{QYw~>#T(w;1jkC<%(*dnJEZWt!)e&uARoeg05g*9Fthw`eg z&Zg?bDosphuYa!T^1V`|)>i3qT)k!ja-U$V7BWv9t;lux*P)vCRT)iwtSRQ!?LQ5k zm|h)I;>>1SHYtq8dpuPM5mAvKtHIV~GC%Bbh;RsLJGdoi_(-kCUM<#D=Sz>lO=|}! zl}US@|8d@H!Jhh>O;=JsJ-ehh{^^AWNH!AfQ((Gu9VBP9McW`VZRHc`=k8m2io&mF zaZO8xhmY@k@#M!*E8m)H>qvhE?{pqVP`d}Pdv5I%^d-ETieQoM!<8pX1Y(GaM0b+5=W^ z`E8P`at_S-PlO~7T;5yb+U-VXrAt7Y+#u =@dqcisE$0cPkcck?A`YkgtESWM#^ zpo*HUaow8czNw_wdH#m-L!hI3GJKZ<=G_`LMuf;!bnizM>=)&+k7GZ)Zgf~DzqD<< zYMpU0;oT8%Pd!u+CWt!II2JP-i8>q^{0Yrk^OS{>x5BF=)2?)<89QLD#AZQ{a;3sA z_DPKP$)0|+bFqPR#tQscZuiN>q>TLfHJdkISFA{=GF4TUb7s`slESCFsL(td zw$0rg?S*?EA+x`#*cr1A*?ztMKQCY1M7*5VsM#7)M-7Gke5Snw#qK{lAdxbjYUYRf zst=w_$(WS2_eaYRQ}(BuF3NbaYxP}Z@eymQ8MOb#^6ku1HgB8G#U8BNJrdBGL(;LpjqJTa60j*%zK(7~G|DX}4XOu02Lw(rByQ zwDn0%G;6zG>KKj+cDS~~13zZjpxfyC3a-QQ!hLB{h1p_REaEF{$9YTEFC)b=*Y0C< zbzY%%?TH_{9vbE%4&-f-wvqq&zr2aMsc((T=e_qvrlxx7F8*mU&DV6+Q~#XM|8$$- zpeUFuhS>rkkfgBrk}oG;Kh*O~10N_zNf&ZhY#0OvV#qKoClzAqMFPIaLm*_~lQ~R?FM>q)_;ehfi4!sK z-W~+}1j=DXzix+NT>=A!h##yF@FIBn;0V4rPwxlP(FOj{UxMJEP)h6mlUaWOG8EQV delta 1343 zcmV-F1;G0C495zP8Gi-<0082ccQ^n51rA9>K~#9!?VZa{8&?#@v6H6nG$HAWQi3tX z1|&8}O%-*4+N3Q_Nvc-i;l?&EgIJh{02#3`2oNuUE%6eFe}DxmX8{{nutUHC1RHj& z5C{}lu}M9>-*`-o`;^JGvHczC69%D~Ip=rJopU`iXHrsVG=Cb6Mx)VaG#ZUYqtW8h zbei-DE?tPORPb?d*NTdYu0w@|g>OLl`S~~ESwTM__yr%sKwO>R;~+CLGv7)}OM9oF zpx|BMzCcV&UM z^#TChtbeSm4;mXApAtX-Kut}JRSZg_$z)2)$jG>jZvtcixE=)1+}!*#0R#Y4S6AEe z^768Ca&kTsqdye@w681V06>}%z(oMLxw&5uKhD1v92^ipGy#NbZf=eM2q3`$;EsmSX@^q~7h(iJ00ck) z1V8`;KmfcT!%LF9B*}~N1V8{0+?a)%rxq6%rHD6X@zz8FAOHg30~!RtN0bPF00!Y05F+M z@3*wHl(1=E06<+`okaxyp21-F7=u$P0CY`6YesfW{1ZS!L&G!li!+w5wS0KjyN;G>%)M!(GV_G%Vj2OS+94|{rgk{*DF!K>ER)?)NhMSt)^ znzrg?0Z<``Dd@i2?N0jU-694rMFuV`$3n@t=KYWJoh(SsWU+$x3g2Ul>AXHpcRrS~?RNXGm6eqxqV)V){Ozvn>E?e`Mwt7s`a=zj zF5*}bijz?W`YDUD%nzX diff --git a/app/src/main/res/drawable-xxhdpi/ic_device_miband_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_device_miband_disabled.png index db5603a27cc7b117ec5792ced887e53da4d2d627..94dd2c1282ceceec85460c6f1b062b2630b3b136 100644 GIT binary patch literal 2593 zcmb_edpOg58~^3dm>d$qqZ%U7m~)n7Y%_;pn`265#^$hTR&%ICDk`>+gqSm7=wa;RsX_pkSl_n-HA|G2;Rb$_nU=eqCfd)?Rdx#Jw|t;E5KU;qHb ztuaVEKXZ0NM2P=l9W?d%Nyra@MF2oKTWp&s%&!H!ZLN^NuiNa-dVVDujByVE0PEG= z*h4uket-{xnATVnXi`W*bgxMpg4qiIA}nhp!YOoY;c*XJ)k?Od{^^t2h370kBZ+D? zFo%qim#3QF$EYX^h^ch!i4*LIkA!@7n3QZ4J$`JmK~^|U5bvUbkaBC-b8~;~DNGyM z)=?zG_p*M)H!2$>pq_w!SMN8Vl0_-Xs@Fv^Kq)k%04mG?T%w#<>P`mk zhM@3N=~7&}i$qRQtyJ2q;Qr#poMMl(#@W0(>N0~P6^Il)%_45}zEI`Q^CV}ffYS1U z_L^crl&8?4nKmrqd{VlscTN<+`^|AVpT~V$89VTV<|oARPxBI1`@93e)Z8cudM!|FxNrV+Hg?)%dJ_52%bDc zG$-pc(X%Cu8`D{fKdc+;1*u;7w!P7qdulFMX#bK;e|X`!ag^CN#jfj$-Hq>le$hYE z22xdYRBpfbz!F;dY?D#<{HppYa5^w|yWxP2E;$)*Q+DmoW6}nx>pn37>jjS<>d4=T zc=n+_a5z4{u0YQGwwkjjJXMaD4tm(l%dE@kRx|p;-S}Zw4Y3_PRiqbvwzai?@3cC- zvld)Y5WzdNFdEdMZu}dQJ0pVN{}uX=ukxAet366+=$+ib%Rzo_eTaR|NJq43Z_;Sk zg`d_Oq|$0m&4NRWaV&fOMv*WiDnFsE%S8Xj$O%oGFuU^jrUD~l;V9{S#uYZg0m(Q7 zw$XM!LOmhJQ^w;tc0kueT#m8#qRie$7Q6fapz6nVvg_ez_=`u58%o|Gz`d$p!ct_*T#*VLAS;V_NNR5e!x!$;AL%!$Z zB$d?feZtpNb65j~zNd8WLR)WSi;;>&$gI`jyK&MOYYZl?nA>@&* zXS&ZNz0XE3`T>Y$UV`?Ulhad0DbhkFBGF1h2#IJ22*J4lX+=nVhKMkv{s$Pz_C(5N zz4BX@@BVaj;v;GPy~smzwUnH~SqoKXnO;3oLj#ppypdvdug%kc+Na9y&df0Y}sqccz6 z*X_W5u6h)4V6f@n7f5c}rH%2t&)8l+`@W2U$$`Ti-P3fY;TR^_~vv_ zBUlc7^azo~aIKDXt^}mS%-mNBbJBd=zJ&uly3$ zdun66t)}SZE$q|jFi*yx2@;uE<1Y4Z#JBrG9!-qoeP}pzB|zft+N=6ykL{6&V*Su3 z%SW~a*q`*Yy{2OhZ_Q7cO`P5)*N=v-1>aNGa$k=Dxi8S1moTlD&e9vPO%x_l{e+OgP}7=Yjhg zHXg}wr{x@)E+LfoSzUPuJ(J(3=s?=evEpA;77Mr*A zb2LF2wZ3Q*mK_-T0BN*ND3v5?_B2S~t7?kh1T5%3F0Q^Y<;p=WE=4d1^L3fon(T3w zUhDAI!dOxyb~vr@v#ZH=110yx>Z0q!{OavhC(z@S?ATx*G5W``LjU^gDv>q2d=dd> zGbtCj{^ge_6ORbul;4oG_IFd#~z{8Zx-@~>@Id*SrJ4iyuQ*M z*s>a{dcX>&P!V4yujW_@xX)v3&8_2Q0!LHMC6f~mzR^6!AV{#?b6;HEnvuwVmPTn; zz{hg}RPtqiOH%aG{hiVb=Ex|9G?^UBhn~vte`36aoomHWq8A*C%^(!ZRBz1}w=A4V zL`z5b&a&XX6eq(JybbT-Yu#i0Qo~AL2CL45=j?d9R0mtn^%uYoYE-EQZicBHBr&y! z$;pO^ndbYd^N$x#WKeLUPz*~0m5`RG-j5;>-@=X$eLY#vg-OboyIN=8Z|YA$dXu|| zkHrIjSHz1c=mt5`z_arC&YE!tPhiV%CHJqD=85xNuWZM?LK3lKtvayRy}iiRLU_;K zm#{mf;OlPj!&Q;LIU5F;@wmz;(cvPjR;wJk3-)iXCE-seu~Y<6z^i1y>ueV5YT=Z6 zses8ET}ypH|GbW^xtLq;%xK|e!d$D3!yAhZlgW8)N51dtH~#{T8hN7OBc>4kRkDwX za$_PcF)4Hg5E@LOg+@-!ck*%4E^bK=4r5ULC``c85`thd8C35}Oe&oQwWrf8=|Mqc z28l`xgc2x31{o1Z4Io1(OlB}#M~B296PeV@WD=i5r-f+K8Gbr}R1%pMLJraK4ujH2 z&=8`IzBWu}LP&9%-%f3}oez;ohW{fa19~uBLn!PdR9D{#27?{oJ2{6b`z2iX zEP}5ja7hg@!NNyi;eH`^k}tHTb6`vC2D5Hwen`*)-zX~G{X4^@%+XN`GI$ii_@xjJf^V`%8kRgdg$`w$+ zs$~clG}EbXk%X`l8GNM%oP+y28Wo~gVCe^C`U1i% zX)GtQxLELSQc_Yd^HZ3zwzf7;TlX z6i~dXaUsH2%GSwAKk07X7SI0gBiS9102`vSeyyGzz2m8%pb(XsntIXN;NIfb(TX&~ zVJ&U#GNS9d(B106d7A!oecGJY&W8|cN0e+2LP@-unDpduN zNEuU+OY$)_&K#)A1n288!b6fH%diLzsc0SCTFacz<|@9o>{nwZG{A{MT**_Kz=B=* zQQ=_AlAhnA<&m<+IjCpKzafer;~!6!9fbKK+GrDWHlr~0>uxKKtU1!9t0T8gLv>y z;#j#s<*eL>I2a0yJ|2#V|4;Vv5=6BFsO(5`HRjR8hb9klEW6m0sF>}fP{)0WBQAy=7V z4pH-?6~?{tNOleoc$KwFMM@i4upN~fr5K{be@0rM$!lwK^wF`&hZ9kfAFO2;r(#jqq#}z>w%1F^Z59uqbwXetK)$a$DE%)chy|OxI zXOH#coUpXVrip6z%GJ39SHnebIMzs`_z$me{m-ulfc3wg9VumqlL7ZokEQ#sU;M|y z{gV6_(u7n!ed@;sQWYdzmKQjCk|4opg~4E;AXPEMj^@B+oJ2;SH`rAGb#!(oM@Fn9 zxi2Ns00hVrE5l#t&5u$g@CIL#1sJM9GyvH!VR}`f(V~t&C&^uyIi2IHx{!&VhDd4Z zu5j90Xu2k#o7H-G!36TG-9n{rCC^1 zl(eiNFb_slar5KdIoR-$C?n9OZR3zm2Y$I?z+M?9-;4JJqd**81+fr3mSK)Mr`HE!@(MtzqJ!cZ3^c-(U{~*0 z^1jiHohjeN=TqAyae;s$=5HkuZwsZbgLR&8D>$^sH@lMxqew;Sa#yU*~gY%E(hvTX?FK!iVRjy3Ix3Lkg zMTyVN><~@_KN7m_T|x=U&2520t?8DR*CM5k!Ma+VK>pIf)%8wgL2*TLn?0iGr(m@H zoc*@CVZ3HN)iiV>%3VMz3FBN$qQ<-@x?3rvvSR*X*6(y$D_YUte###a8#(8(tv$tQ zFr22fW=r6TvXBvW$lr`+X78s0d&%yKA_kR$`;boG*3gAz<>`YGSDUoAS@I9lv^Il< zq>_eYP>o9JIW`6CBOSg&zcq!IxHr1Ig!cso#fje65bw&0hPr&lK~FSH^g&*0Y7cI; z-}8w7pbl{2P4dp*VnR4OLEJ;0RhwhPG2*x}pQ$JfC)S@Qhqg#Zn3wLJ^%T?UP@x4$ z6mc^bD|-^E1{X9kWz7B5=H!CLN{Xy!`hHL!G9|7X{o)@%lHB8(ZmAM`l`@0`j-IKZ L1^S7>_2|C=UW6<| diff --git a/app/src/main/res/drawable-xxhdpi/ic_device_pebble_disabled.png b/app/src/main/res/drawable-xxhdpi/ic_device_pebble_disabled.png index c1eebfd5974537b27e16dd341de28826c8d03ab9..24c8f2f56eaec676c83fb31d0b1c9826bd379829 100644 GIT binary patch literal 2417 zcmZ`)c{tSV8va#em`G)bkTN3s7Dn_L``Bj8*dq)xW|)PUX(XhOG9QsjvW_Lp#8ec@ zR#A$CLYqBH_DZ%gIpciibiQ-0>-_P&&-LE#eLv6rK7YJ#64KdLR76$;002?AofV3o z(>4Pnz+asZYWn;n5DasI0l?i<@OpqCf4>#uU~2_zTu6P{#NP>r*?G|b0KT*tTkwZP z_wYd>I^4-xXjni@csuk3jNZjZTH#hO*Ha(na-AqjaGBN(gj>W%QYYlOidMVEhk&Q5 zs;D&2bG1s4v|3%C)$K(*Q1n@|19QU0;a2S=Wr%zK*=yq3N*#_Z?a3Lw;d$kT`i!Fk zJkwY)hUu#oXK0e&rysfL3UwuU`O|EUR^1|S*$%Qh$?D+8Bp=>VuG-e z5o%{x++hFu=txBV-0J#~oj^HsY!S}3h5{gALM z3A{4UUT5UdY3{PXefWB=u4bSSefU*MQIb+wyhXj7-Bep2$I{b)cYI*%eC&MhwRv+J zrj|Hln^_d3QvVjqWiB;m2c}RrBvp zpmrvJ-l?evuLr1A8%Eh8HyxFilGggKb9dV%el^H0^GEOpKSx)$NprX|27x#HsGfO% zN)&=NhhMG#_07WdVW9=vClG8SK77Sn&$V3GcE(}*HIJxlVG+IAM?oIL(-q>yNh|kD zDaI-_PX+YOgk(8J9oJL~yP8t#+ayJwAh0ttGlAgAYe|8{Y4)zF+2NVtzV$_rS2-zt zw~4cUrH3xFVy03}4flv7U@#Ug>|)SBNLkK_3C7;t%;tOXDQsF=o0Q`pmI8)Lbh>&U zTU;7zFCn2QB9i9Qlmw3!TBv7?`>pciK5i(?H!#L2tBV!~z0z3Q$qkH~(6`IklOH;{ zJb5I+=*GqpB~4~Xnr|0)!=)MwQyq1 zrG8RfTmr4Z@AR))k0E9$3diJreYd>lF>qs7D5UIJRX1YB((&$ZPkiPbn_KVHPvopU zWBW}NHf6+}iJ7RmJzT0j>+o;hBiRmE%~$`?rQTz`H8ybqGpnsI3{#Nr!wUBI3;wpp zhuy=pmsQ3*qJ!_Qzv5bSeQSC`mH)fOA**;{q@6|%_fi(hfZ(C2q8f4kGcLjkCVxbm zMx2wce^7iz+o=m|nj09p!X0W!Zjk`NYVM~4d#DJgU(YC_&)Lr#clhz{y+4Z* z_t+qvJ1yOb?_WrP4stjyk#nKkQc2b#7@chUoW0(o7wYx?_(|Z;lIU=hw0Ex)$qRR3 zvaKAE6Th9QpjY*)D%&ja;xCn>OV_6}BHL$22uVx2{VfqAAgI~?${(z#BK4(ZBu^^i zJ(`zlrWeZz))*sTV-tH%Hx1P;z9rsn7@f3{jxYRk`fF1Y@4}VGm3cH{Xuiy@b0H)0 z{jpVF3ucwS7;)tG6_lN8{ZZr>V_1TFA{u&UFj`2cf+xiA8%)o2yKux7)*79-Gp~N5 zwJ{jxb9ZnZi98VI7t#3!SD55*rP#T*bWlz(ooFt3G(Z&jxg2%7v5&*8s^l<^RM|2Z z9VUp+Xi){YSZDGBspmIXLAd)y`6BXnWZVqUTQQPor+Yo+%O?@EiD(163b03k%(3Mc zYp6#V>|zvQo8*h09%0p)KhB&TxvYl1RneE|o)v=AL}%1K?ONQgAEPx9d9jY@?UChO zfNSm%owg@Gn|5z@DRZ1{@prE4tIkQqnYfC9M=y`5cC&P(UC!G)@IM`n?)sXm&jti~Rz6fxDE4J~R~WyZ8IecX+50d47vEWcP!B_5Ht$ zG*!84Ib!@@eh1y!lMXvc$5W`lsW3eGRP6BVOFkaBiCa>_BB_L6JRPvKRD{v#R08HC zoj@ULI#bA&6cPzb4I~5*HPQG0Di%g0gklx(bb6SHwss&D8$c&8uz`FMg-p|;P=mFJ zgg`8rhNWp^A~nf@nzR6IeJvgBK>^uuemmvOc0mDjtjRy6SU^ul_lTyBv8JxRtB#Jz z5d#xlV|5)p6CItpPV?~p5|AiC1YG3*7qq3XcJKu7Kn(5&?1mSX@LP@ u*iCoY^QN+V(ymRCD}heL0!S5=&-k-lXsdJ%cgR`c7kO^=Hm`9F2#+X<77%&0xykv?jLQ3I^6sah6(!)Q=^ zc6K&R$M1t~-Zqx=L5}K!OO+;Aky^AdoSst0&b~edMi{OdRQ=$=oX)`DR26?$iO)zS>rqqDQJ(z>b~CU$)>mPxio+7ugUL1dgX7%3Q1_*R69 z*r}eLo|8}98n2Hp4hb--s=}3K{b&kob9ym;8xX_cos(T8W8!RSG+JGWU0F|j#7im+ z2Fs0a8eP!Mv3UA5JMgeh&3Ffk6U;tU7v{nU$OpAmj>QNwcDdgojV_M29)sOs(ew&g zxxbrt)P zxa;<3lX3HKvI5cuMaXT(zpW7Bd1Hy!s)Q$D4K6NEnuGUFPTL}rIwQMBJ%TrSTdvKfmz4k_p7Qs_)GCscTkUvx-f;C4aVDUaM52kYP5Oqt zYUL^CZ~0zw_fbsX>MvT*>%f_T$mgS(kfd%UdC3bzN^zs=>Ac_70v(vOAY73%j_^GQ zY6{Q`8r~{~@_R^#_PVDwoOP4N={oR@Fao(d1yw|2Ulyn(KDw&yx~H+(0Q@T$W;Xp+ zewYBwp}^oeT?~J1kp3m?MlgR*k1NLBn9>F@qsQ`~XP5zb$0*)BgxzRsV0wPu&e+)a zEQ!?j?!x@gA6~4Sg<4V!SbjQgw?~6LoEQzpHI400HY+MF-e*^6voCaviR=JT($bIJ zo5hrwlyYG@4@3)tp);AxZmO)I)7Hj{Z|Kq|%MOs|WsH(-PV?z56atjTL1O?BG!KCS zCmH}3mp&y{lQ%0T3_Y(%2_wM$niPYm?Q^pMxd!CvViAbD9PS8=Sf23%A-n@uaRJDi ztCr8AzBxaOSYyEzBwi9IaZq}5^U=@=*4|J~&q0NR#%{YZXA!zP=p7OwW+>#>)p5^J z<3DD;)i|e2B&(yL)#EdBLPSvQ5<>rght9|7jX$z=%u9@41aJ^+NdE50QOA7o4 zmMdN^0Yhd7v{zPV9XVSeQCl0SfCzmCpy>v_xD21iCJy#>JcCHZJWHq z6L+qFbE%6wzBV_Q{p55YzXA>mE^lpKK*&GM_8j6KpUk~~dC9f(9mXZ=C)dK@DD}79 z=t~Tq9SVO+iEiKvzEh)D4t|l#hQ`P)nN!w{#=g=6{|mk#$r7Re1;o@ZrT`1+bN{k) z7b`<`)XVu15G5pMh?cId_p6kBRtcbo9F7-ORA29cL?VkvM%?VifJ0kvY}6B^gw5~i zYQPe%0pe2VEa1(KGjx5P?ty{o3?LIhRF#O&5FlQI6_u8j#sQf^Ns)j0sNzNfNaCO- zf^McL>g(gDi7CBc_UKftq}DsOR2u0Re#! znfnqq`wKs4>}gv%ClQ1XVZToH;wBZ7gMgr)`onXXP-~Nz)!FxJG2DeC*5y)kgUOdBBG}K@+M87>33=el;k8gP#x;)$_<;{+%P{8 zWZdp9(f1k~RPWW1%T7D!c-g0O-6Avmk($^0%JZA%lX;Vqlf*{_#ao+eB@jNr&R}o0 zRFw%!Y3=Ajo==Uc$Me8J+WDbc>|>9no6ZY{dKc!T#g)fb7?Cu}Q~!^-L8rF;Qdo1# zIa5vp;g;y$#Y?h5{^|~O?$ON9U%M<#W}wp?PWqip@kEo}zS0#;uC0jnSc4F=S>yYY zg9xu!lEfJ#+p%?wZ&VcxH(%_I_LP9d+q&hn4`1!O)P`1xg}>=f)R%L4olP@jm!rau zTuSz0zvsZ*hNy=F{-6s*TdANNrIt5VIKD~1W`ZYD6z`?82vzVWt+w>LRW8+Z7RXpT zd&-f!^E+BG_8}%<%2t4_HmwFkE`FN9yr;fZ4La9LekItEa7R#?z_7u%?vlog0BPG; z1%H-c#gT`#-)2smfc3K^Oii T{b5;OfLo3pu_8RcyI%eW$RF$t From a9b4ea8edac1e77a46dd2056a9e43467e9a159f9 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 15:17:08 +0200 Subject: [PATCH 230/274] bump buildToolsVersion --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b58658fa..f8f96f42 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ tasks.withType(Test) { systemProperty 'MiFirmwareDir', System.getProperty('MiFir android { compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { applicationId "nodomain.freeyourgadget.gadgetbridge" From 4bcebca7445cef5cc9037489023e10864e96c0cc Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 15:21:25 +0200 Subject: [PATCH 231/274] Work towards dark theme, remove -v21 specific theme definition --- .../gadgetbridge/GBApplication.java | 4 +++ .../gadgetbridge/activities/AlarmDetails.java | 3 +- .../activities/AndroidPairingActivity.java | 3 +- .../activities/AppBlacklistActivity.java | 3 +- .../activities/AppManagerActivity.java | 3 +- .../activities/ConfigureAlarms.java | 3 +- .../activities/ControlCenter.java | 3 +- .../activities/DebugActivity.java | 3 +- .../activities/DiscoveryActivity.java | 3 +- .../activities/ExternalPebbleJSActivity.java | 3 +- .../activities/FwAppInstallerActivity.java | 3 +- .../gadgetbridge/activities/GBActivity.java | 22 +++++++++++++ .../charts/AbstractChartFragment.java | 4 +-- app/src/main/res/values-v21/styles.xml | 32 ------------------- app/src/main/res/values/colors.xml | 3 +- app/src/main/res/values/styles.xml | 23 +++++++++++-- 16 files changed, 61 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java delete mode 100644 app/src/main/res/values-v21/styles.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 20e79bed..a9ffd273 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -305,4 +305,8 @@ public class GBApplication extends Application { public static LimitedQueue getIDSenderLookup() { return mIDSenderLookup; } + + public static boolean isDarkThemeEnabled() { + return false; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index 9bfe1295..7b3342ae 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.os.Bundle; import android.os.Parcelable; -import android.support.v7.app.AppCompatActivity; import android.text.format.DateFormat; import android.view.MenuItem; import android.widget.CheckBox; @@ -12,7 +11,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; -public class AlarmDetails extends AppCompatActivity { +public class AlarmDetails extends GBActivity { private GBAlarm alarm; private TimePicker timePicker; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java index f85d3ca0..e7900e44 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java @@ -1,11 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import nodomain.freeyourgadget.gadgetbridge.R; -public class AndroidPairingActivity extends AppCompatActivity { +public class AndroidPairingActivity extends GBActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index 06021c08..21e8393a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -11,7 +11,6 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -34,7 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; -public class AppBlacklistActivity extends AppCompatActivity { +public class AppBlacklistActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(AppBlacklistActivity.class); private final BroadcastReceiver mReceiver = new BroadcastReceiver() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index f5182ee3..5be4f6f0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -10,7 +10,6 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; @@ -37,7 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; -public class AppManagerActivity extends AppCompatActivity { +public class AppManagerActivity extends GBActivity { public static final String ACTION_REFRESH_APPLIST = "nodomain.freeyourgadget.gadgetbridge.appmanager.action.refresh_applist"; private static final Logger LOG = LoggerFactory.getLogger(AppManagerActivity.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index d04338cc..49f8e7a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -4,7 +4,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; -import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; import android.widget.ListView; @@ -21,7 +20,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS; -public class ConfigureAlarms extends AppCompatActivity { +public class ConfigureAlarms extends GBActivity { private static final int REQ_CONFIGURE_ALARM = 1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index cb188ba4..b9ad4d98 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -20,7 +20,6 @@ import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.app.AppCompatActivity; import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; @@ -48,7 +47,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class ControlCenter extends AppCompatActivity { +public class ControlCenter extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(ControlCenter.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 524522e2..a6eee775 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -13,7 +13,6 @@ import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.app.NotificationCompat; import android.support.v4.app.RemoteInput; -import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; import android.widget.Button; @@ -37,7 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DebugActivity extends AppCompatActivity { +public class DebugActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class); private static final String EXTRA_REPLY = "reply"; 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 82f2c7e5..f2ab531b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -12,7 +12,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Parcelable; -import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView; import android.widget.Button; @@ -33,7 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class DiscoveryActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { +public class DiscoveryActivity extends GBActivity implements AdapterView.OnItemClickListener { private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class); private static final long SCAN_DURATION = 60000; // 60s diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index 4c18bb90..7dba008b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -4,7 +4,6 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; -import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; import android.webkit.ConsoleMessage; @@ -32,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; -public class ExternalPebbleJSActivity extends AppCompatActivity { +public class ExternalPebbleJSActivity extends GBActivity { private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index be79ea0a..90390a1a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -8,7 +8,6 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; import android.view.View; import android.widget.Button; @@ -35,7 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; -public class FwAppInstallerActivity extends AppCompatActivity implements InstallActivity { +public class FwAppInstallerActivity extends GBActivity implements InstallActivity { private static final Logger LOG = LoggerFactory.getLogger(FwAppInstallerActivity.class); private static final String ITEM_DETAILS = "details"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java new file mode 100644 index 00000000..39c972ea --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java @@ -0,0 +1,22 @@ +package nodomain.freeyourgadget.gadgetbridge.activities; + + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; + + +public class GBActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + if (GBApplication.isDarkThemeEnabled()) { + setTheme(R.style.GadgetbridgeThemeDark); + } else { + setTheme(R.style.GadgetbridgeTheme); + } + + super.onCreate(savedInstanceState); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index c8cb4d7c..9378d2a6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -150,9 +150,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { protected void init() { BACKGROUND_COLOR = getResources().getColor(R.color.background_material_light); - DESCRIPTION_COLOR = getResources().getColor(R.color.primarytext); + DESCRIPTION_COLOR = getResources().getColor(R.color.primarytext_light); CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext); - LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext); + LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext_light); HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate); HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill); AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light); diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml deleted file mode 100644 index 80bee978..00000000 --- a/app/src/main/res/values-v21/styles.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5f53994c..ac484db2 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,7 +7,8 @@ #f0f03000 #0091ea - #ff000000 + #000000 + #ffffff #ff808080 #1f000000 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 1bcf060c..5d6ea28d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,10 +1,29 @@ + + + + From 367aced03d530d5b9741f1e890697df57317d4e0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 15:34:53 +0200 Subject: [PATCH 232/274] also use theme in settings --- .../gadgetbridge/activities/AbstractSettingsActivity.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index d9a3b1c3..2d62ce04 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -20,6 +20,9 @@ import android.view.ViewGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; + /** * A settings activity with support for preferences directly displaying their value. * If you combine such preferences with a custom OnPreferenceChangeListener, you have @@ -86,6 +89,11 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { @Override protected void onCreate(Bundle savedInstanceState) { + if (GBApplication.isDarkThemeEnabled()) { + setTheme(R.style.GadgetbridgeThemeDark); + } else { + setTheme(R.style.GadgetbridgeTheme); + } getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); super.onCreate(savedInstanceState); From f76a1ba16fe490d9d01529607b27b8138c8b1185 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 16:15:58 +0200 Subject: [PATCH 233/274] allow to switch to dark theme im settings --- .../freeyourgadget/gadgetbridge/GBApplication.java | 10 +++------- app/src/main/res/values/arrays.xml | 11 +++++++++++ app/src/main/res/values/strings.xml | 3 +++ app/src/main/res/xml/preferences.xml | 7 +++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index a9ffd273..1aa62200 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -195,11 +195,7 @@ public class GBApplication extends Application { public static void releaseDB() { dbLock.unlock(); } - - public static boolean isRunningOnKitkatOrLater() { - return VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } - + public static boolean isRunningLollipopOrLater() { return VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } @@ -299,7 +295,7 @@ public class GBApplication extends Application { editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION)); break; } - editor.commit(); + editor.apply(); } public static LimitedQueue getIDSenderLookup() { @@ -307,6 +303,6 @@ public class GBApplication extends Application { } public static boolean isDarkThemeEnabled() { - return false; + return sharedPrefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_light)).equals(context.getString(R.string.pref_theme_value_dark)); } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 1f5cfa29..937c29f2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1,5 +1,16 @@ + + @string/pref_theme_light + @string/pref_theme_dark + + + @string/pref_theme_value_light + @string/pref_theme_value_dark + + light + dark + @string/always @string/when_screen_off diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa365a9b..5f43f764 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -39,6 +39,9 @@ Date and Time Sync time Sync time to device when connecting and when time or timezone changes on Android + Theme + Light + Dark Notifications Repetitions diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 60eb7c5f..4d49eba1 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -11,6 +11,13 @@ android:defaultValue="default" android:key="audio_player" android:title="@string/pref_title_audo_player" /> + Date: Thu, 14 Apr 2016 16:44:44 +0200 Subject: [PATCH 234/274] use android:summary="%s" for ListPreferences --- .../gadgetbridge/GBApplication.java | 2 +- .../activities/SettingsActivity.java | 6 ------ .../miband/MiBandPreferencesActivity.java | 6 ------ app/src/main/res/xml/miband_preferences.xml | 18 ++++++++++++------ app/src/main/res/xml/preferences.xml | 18 ++++++++++++------ 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 1aa62200..f292133a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -195,7 +195,7 @@ public class GBApplication extends Application { public static void releaseDB() { dbLock.unlock(); } - + public static boolean isRunningLollipopOrLater() { return VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } 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 34942ef2..d541af9c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -133,11 +133,6 @@ public class SettingsActivity extends AbstractSettingsActivity { @Override protected String[] getPreferenceKeysWithSummary() { return new String[]{ - "audio_player", - "notification_mode_calls", - "notification_mode_sms", - "notification_mode_k9mail", - "pebble_activitytracker", "pebble_emu_addr", "pebble_emu_port", "pebble_reconnect_attempts", @@ -159,7 +154,6 @@ public class SettingsActivity extends AbstractSettingsActivity { "canned_reply_15", "canned_reply_16", PREF_USER_YEAR_OF_BIRTH, - PREF_USER_GENDER, PREF_USER_HEIGHT_CM, PREF_USER_WEIGHT_KG, PREF_USER_SLEEP_DURATION, 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 7ef19b80..919e7d2c 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 @@ -59,20 +59,14 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { protected String[] getPreferenceKeysWithSummary() { return new String[]{ PREF_USER_ALIAS, - PREF_MIBAND_WEARSIDE, PREF_MIBAND_ADDRESS, PREF_MIBAND_FITNESS_GOAL, PREF_MIBAND_DONT_ACK_TRANSFER, PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, - getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS), - getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_INCOMING_CALL), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL), - getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_K9MAIL), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_K9MAIL), - getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_PEBBLEMSG), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_PEBBLEMSG), - getNotificationPrefKey(VIBRATION_PROFILE, ORIGIN_GENERIC), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_GENERIC), }; } diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 042cd8e1..c544d254 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -14,7 +14,8 @@ android:entries="@array/wearside" android:entryValues="@array/wearside_values" android:key="mi_wearside" - android:title="@string/miband_prefs_wearside" /> + android:title="@string/miband_prefs_wearside" + android:summary="%s" /> + android:title="@string/miband_prefs_vibration" + android:summary="%s" /> + android:title="@string/miband_prefs_vibration" + android:summary="%s" /> + android:title="@string/miband_prefs_vibration" + android:summary="%s" /> + android:title="@string/miband_prefs_vibration" + android:summary="%s" /> + android:title="@string/miband_prefs_vibration" + android:summary="%s" /> + android:title="@string/pref_title_audo_player" + android:summary="%s" /> + android:title="@string/pref_title_notifications_call" + android:summary="%s" /> + android:title="@string/pref_title_notifications_sms" + android:summary="%s" /> + android:title="@string/pref_title_notifications_k9mail" + android:summary="%s" /> + android:title="@string/activity_prefs_gender" + android:summary="%s" /> + android:title="@string/pref_title_pebble_activitytracker" + android:summary="%s" /> Date: Thu, 14 Apr 2016 17:04:49 +0200 Subject: [PATCH 235/274] allow dark theme in charts activity. The charts however are still the same --- .../activities/AbstractGBFragmentActivity.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java index b76e50c3..dcb87d89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java @@ -5,6 +5,9 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; + /** * A base activity that supports paging through fragments by swiping. * Subclasses will have to add a ViewPager to their layout and add something @@ -31,6 +34,12 @@ public abstract class AbstractGBFragmentActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { + if (GBApplication.isDarkThemeEnabled()) { + setTheme(R.style.GadgetbridgeThemeDark); + } else { + setTheme(R.style.GadgetbridgeTheme); + } + super.onCreate(savedInstanceState); // Create the adapter that will return a fragment for each of the three From a9b75a63b348568b8d935906e91bac2d8e8af8c3 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 17:16:43 +0200 Subject: [PATCH 236/274] simply derive AbstractGBFragmentActivity from GBActivity instead of FragmentActivity This fixes the Actionbar being invisible --- .../activities/AbstractGBFragmentActivity.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java index dcb87d89..ba2e5a7f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java @@ -1,13 +1,9 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.os.Bundle; -import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; - /** * A base activity that supports paging through fragments by swiping. * Subclasses will have to add a ViewPager to their layout and add something @@ -21,7 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; * * @see AbstractGBFragment */ -public abstract class AbstractGBFragmentActivity extends FragmentActivity { +public abstract class AbstractGBFragmentActivity extends GBActivity { /** * The {@link android.support.v4.view.PagerAdapter} that will provide * fragments for each of the sections. We use a @@ -34,12 +30,6 @@ public abstract class AbstractGBFragmentActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { - if (GBApplication.isDarkThemeEnabled()) { - setTheme(R.style.GadgetbridgeThemeDark); - } else { - setTheme(R.style.GadgetbridgeTheme); - } - super.onCreate(savedInstanceState); // Create the adapter that will return a fragment for each of the three From a9e7cdcaa7c62731aeab8728abb9ed3b791a5adc Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Apr 2016 17:41:04 +0200 Subject: [PATCH 237/274] use some colors from the theme for charts activity --- .../activities/charts/AbstractChartFragment.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 9378d2a6..41c61114 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -4,11 +4,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.content.LocalBroadcastManager; +import android.util.TypedValue; import android.view.View; import com.github.mikephil.charting.charts.BarLineChartBase; @@ -149,10 +151,14 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } protected void init() { - BACKGROUND_COLOR = getResources().getColor(R.color.background_material_light); - DESCRIPTION_COLOR = getResources().getColor(R.color.primarytext_light); + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getContext().getTheme(); + theme.resolveAttribute(android.R.attr.background, typedValue, true); + BACKGROUND_COLOR = typedValue.data; + theme.resolveAttribute(android.R.attr.textColor, typedValue, true); + LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = typedValue.data; + CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext); - LEGEND_TEXT_COLOR = getResources().getColor(R.color.primarytext_light); HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate); HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill); AK_ACTIVITY_COLOR = getResources().getColor(R.color.chart_activity_light); @@ -704,7 +710,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { return (int) ((date.getTime() / 1000)); } - public static class DefaultChartsData extends ChartsData{ + public static class DefaultChartsData extends ChartsData { private final CombinedData combinedData; public DefaultChartsData(CombinedData combinedData) { From a460049a1b56180c192444792ce1788da5bdc1b3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 14 Apr 2016 23:23:06 +0200 Subject: [PATCH 238/274] Sort by label and blacklist status, hopefully fast enough #275 --- .../activities/AppBlacklistActivity.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index 21e8393a..adf33ddc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Comparator; +import java.util.IdentityHashMap; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -59,6 +60,29 @@ public class AppBlacklistActivity extends GBActivity { final List packageList = pm.getInstalledApplications(PackageManager.GET_META_DATA); ListView appListView = (ListView) findViewById(R.id.appListView); + // sort the package list by label and blacklist status + final IdentityHashMap nameMap = new IdentityHashMap<>(packageList.size()); + for (ApplicationInfo ai : packageList) { + CharSequence name = pm.getApplicationLabel(ai); + if (name == null) { + name = ai.packageName; + } + if (GBApplication.blacklist.contains(ai.packageName)) { + // sort blacklisted first by prefixing with a '!' + name = "!" + name; + } + nameMap.put(ai, name.toString()); + } + + Collections.sort(packageList, new Comparator() { + @Override + public int compare(ApplicationInfo ai1, ApplicationInfo ai2) { + final String s1 = nameMap.get(ai1); + final String s2 = nameMap.get(ai2); + return s1.compareTo(s2); + } + }); + final ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item_with_checkbox, packageList) { @Override public View getView(int position, View view, ViewGroup parent) { @@ -79,22 +103,6 @@ public class AppBlacklistActivity extends GBActivity { checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName)); - Collections.sort(packageList, new Comparator() { - @Override - public int compare(ApplicationInfo ai1, ApplicationInfo ai2) { - boolean blacklisted1 = GBApplication.blacklist.contains(ai1.packageName); - boolean blacklisted2 = GBApplication.blacklist.contains(ai2.packageName); - - if ((blacklisted1 && blacklisted2) || (!blacklisted1 && !blacklisted2)) { - // both blacklisted or both not blacklisted = sort by alphabet - return ai1.packageName.compareTo(ai2.packageName); - } else if (blacklisted1) { - return -1; - } else { - return 1; - } - } - }); return view; } }; From e451e8155c2168294942d4c01ceebdcb6f1cb260 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 14 Apr 2016 23:55:40 +0200 Subject: [PATCH 239/274] Remember the map so that we can look up the name later, as well, closes #275 --- .../gadgetbridge/activities/AppBlacklistActivity.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index adf33ddc..bef49438 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -48,6 +48,7 @@ public class AppBlacklistActivity extends GBActivity { }; private SharedPreferences sharedPrefs; + private IdentityHashMap nameMap; @Override protected void onCreate(Bundle savedInstanceState) { @@ -61,7 +62,7 @@ public class AppBlacklistActivity extends GBActivity { ListView appListView = (ListView) findViewById(R.id.appListView); // sort the package list by label and blacklist status - final IdentityHashMap nameMap = new IdentityHashMap<>(packageList.size()); + nameMap = new IdentityHashMap<>(packageList.size()); for (ApplicationInfo ai : packageList) { CharSequence name = pm.getApplicationLabel(ai); if (name == null) { @@ -98,7 +99,7 @@ public class AppBlacklistActivity extends GBActivity { CheckBox checkbox = (CheckBox) view.findViewById(R.id.item_checkbox); deviceAppVersionAuthorLabel.setText(appInfo.packageName); - deviceAppNameLabel.setText(appInfo.loadLabel(pm)); + deviceAppNameLabel.setText(nameMap.get(appInfo)); deviceImageView.setImageDrawable(appInfo.loadIcon(pm)); checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName)); From 7c21f2872a44b8bbd28b9adc98b48b4bf65a2a78 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Fri, 15 Apr 2016 08:50:26 +0200 Subject: [PATCH 240/274] fix travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 38045ed0..d58ee5f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ android: - tools # The BuildTools version used by your project - - build-tools-23.0.2 + - build-tools-23.0.3 # The SDK version used to compile your project - android-23 From 98d7237ec3aef774a8ceab919d0d97f5a1be9bef Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 15 Apr 2016 22:56:37 +0200 Subject: [PATCH 241/274] Display a different notification icon, when disconnected Better icons welcome :-) --- .../service/DeviceCommunicationService.java | 4 ++-- .../freeyourgadget/gadgetbridge/util/GB.java | 8 ++++---- .../ic_notification_disconnected.png | Bin 0 -> 1202 bytes .../ic_notification_disconnected.png | Bin 0 -> 1033 bytes .../ic_notification_disconnected.png | Bin 0 -> 1469 bytes .../ic_notification_disconnected.png | Bin 0 -> 2314 bytes 6 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_notification_disconnected.png create mode 100644 app/src/main/res/drawable-mdpi/ic_notification_disconnected.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_notification_disconnected.png 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 85e3ceb7..c26cb1b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -117,7 +117,7 @@ public class DeviceCommunicationService extends Service { mGBDevice = device; boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); setReceiversEnableState(enableReceivers); - GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), context); + GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context); } else { LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + mGBDevice); } @@ -387,7 +387,7 @@ public class DeviceCommunicationService extends Service { private void start() { if (!mStarted) { - startForeground(GB.NOTIFICATION_ID, GB.createNotification(getString(R.string.gadgetbridge_running), this)); + startForeground(GB.NOTIFICATION_ID, GB.createNotification(getString(R.string.gadgetbridge_running), false, this)); mStarted = true; } } 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 a20e0f2e..07a7d593 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -45,7 +45,7 @@ public class GB { public static final String DISPLAY_MESSAGE_SEVERITY = "severity"; public static GBEnvironment environment; - public static Notification createNotification(String text, Context context) { + public static Notification createNotification(String text, boolean connected, Context context) { if (env().isLocalTest()) { return null; } @@ -59,7 +59,7 @@ public class GB { builder.setContentTitle(context.getString(R.string.app_name)) .setTicker(text) .setContentText(text) - .setSmallIcon(R.drawable.ic_notification) + .setSmallIcon(connected ? R.drawable.ic_notification : R.drawable.ic_notification_disconnected) .setContentIntent(pendingIntent) .setOngoing(true); if (GBApplication.isRunningLollipopOrLater()) { @@ -68,8 +68,8 @@ public class GB { return builder.build(); } - public static void updateNotification(String text, Context context) { - Notification notification = createNotification(text, context); + public static void updateNotification(String text, boolean connected, Context context) { + Notification notification = createNotification(text, connected, context); updateNotification(notification, NOTIFICATION_ID, context); } diff --git a/app/src/main/res/drawable-hdpi/ic_notification_disconnected.png b/app/src/main/res/drawable-hdpi/ic_notification_disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..5d510a7a96b83e9ab703d00d19477c6338e13f18 GIT binary patch literal 1202 zcmV;j1Wo&iP)M?s9>M``&~-il000McNliru-~qKI*!OK{`D0nv@R z5Lp;C21(S!g;5m67_>nVi8c}k8~~fN+Ko*?cYQ86m3|gAys9P|PjZvWedpcxzcc*j zf0f8#YV}qDm|eoJ)pj-E@}o!ouxjkKC4b{2hcQit-4O# ztp2Eu2B1&Wof&}J)vF>3)n|36J?cy9Dz&Hjc`0Xi-2*%o{(Zn#KsPW4I1JnX6f?Je z58MfS3>;4>p-u-D1D67q0jp9<{kZ{Uz?=`f4xE{}JPP~*YzIofQeYv_71y@{2Z8;- z6F?bQ7rqYwqba37+ey__^)(w?zq&0bU_jlkKBo>wT6e3x>MQD;@$${RYRP=|zS^g5 zRKKaZwp(3R)gaU={z8LPT?jmwNkhfTu_|^P_!Srez77UoTy;+oxC2-XOllO2Vx9eN z;343Q8rS-P4}c}W0pNF_GYb3waDA2kAz%-%JkDJLtPS6jMov9I=K|N(*e?UyfNz1* zfOCM^0qEW6pze(SQ21X0OpCp{BdV*bBHa+syvzYv8;q8}CQ>>v_TLGB^Quz$UZgL( zzX#Y<3rx+nj~Y4~crzkZZAXB;Q8!b90)7B)jA$jmh*%eJW#-<`fk%TzMrtX9)S#&E z0-gaT)QRG;0961c$D0>{9^ljPSq^l>@8008!@!#0B5LWHedKLIPE$VclPnwqfmMG@WwEmGN$05vVTa0+k%a1^)+_^6(sE7U|e z=G+TB9dF%=O^$i>2QU@5IMTTv*f(AcG*f-sQ)8Re&PLLtkOS91bjHC`AUbyi9kd51 zle!Oq4bjf++6eGPvk!u%5sI5Tqac492cVyWR8NVxpRI0HSA@i9)dtm-wNzv~U}mT< zsF$k~)b%ZbIi{{@R?N9l7Mk~VU`MP{ux+`8ciIR%UXikG15)P#>w`Zw0>hcXYjjJa zH4;+gd0;5-mu;~IkS<^b@MO%S4Kd}X1GmJ$?TrT>t#qDHNMp=jj7B&HTn&7u_U8eW zYJgc8x~2%+ms0A_D1a@%=>eiw*p#447%WQ(m{dqiOebdCikNo;A=-B20aUC3a3rSu z-jvcngBK~K6S8Abg07ClaEy$f8+4M=;NO literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_notification_disconnected.png b/app/src/main/res/drawable-mdpi/ic_notification_disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..f1587ca34dd657c095b5befaa149ade288552268 GIT binary patch literal 1033 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbQ|PfvG9LC&cytC>W+8@bCYBAi@Li z%i_^UU8z$2z8%0s$ygHP7tG-B>_!@hljQC0!qCAg>jC6&7I;J!Gcd><0%69y3#E2I zLG}_)Usv`AEc_z6`p=hVuLJ5~@N{tu(Kw%+puiDvb3=D=?A}wnsYXUh-W(iUoZTHx zM<%MMs4QKQnzBA_sFiteor?Qn-3``1+{ZH*Vc* zJ~DTXpio};`d!r(Rh7+0{{C6dc0jdheH@XY zn^h*(f7LoMqv_D1N0TlYrMwSqI?{M+)vD}u*Oa^+jyf(7^!=m7vt?12amfJ{tH7g@ zRSO>N&3MG~S6Iz&mQCs_6Om?zCu?pNKRY*je}ZC_jqcuEJT7bZ?PBvy`rFp4kahc; z?2D+f_cwRv|8Hz=H+b~t#z$dw|9MumZ3&AQ8NMD#b$+*Iy%8{Y7(8A5T-G@yGywoG CTp48m literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png b/app/src/main/res/drawable-xhdpi/ic_notification_disconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..b382a8f75b78051d9c8ee4cf775b8f675d27e75b GIT binary patch literal 1469 zcmV;u1w#6XP)M?s9>M``&~-il000McNliru-~=v**d9@|FSI}u`K2%-3bLqdMJS5U zs6a^3NGgdDr4I&?l9?lExQ*jjlMaMwjydBpcm932&&`+9+3q>#Mt>M)?m6c^_xV5X zv)^%KfTXBf4iJEIjz&SA0;MbhoOA#Dc(@w)7`JN;)iQrJX<+j!1g87TjS( z>Tw%ar}fVO76Z2jZv*>*4Ztp-AD9b_4-s`7co#SZEC+V@g|Cs8N79oqK3~HjJBg1Z zbx7*-;y{u2P7Wv7%X#k&LoEx;$hT`@9-f%k#)fzhVu zL3{5+U<&Y!S^lyZIUBvx`2%v2ZlG+@4@$oV3Pz$NCtQRoNe12+SAM!3T+;K3LfOM!bLWbetFKJT}tEDD2? zzOuaDA*t8q0a>b?laOlPlhhw_eV?SeC6$tbIVlDv0AHGG#9_dKb(^`(SYQkAOw4=U z0v<4T^RWkb)8b}#<|6DBu-hW66xBlSwF(=B-I89Ev^nJ74oP#Zi@iSgL}vmICAFj> zkhYcR0v1?aZp~qjvc=N`;CrCm+KeWvHEs4>r**5@D{)Bwu>$y_pasK3Tf_WdmHb9e z`5_}v=q|=zpLDA@R6=5*Ptv7n12|p3aq1Md0Pg@N3$jfqB$fXH>`S}dOFa{N!j{-P z+$e0bDoi0U5Q4+NEe#~_Q=qdgBn$boZzC}PY^~P^|NEtCG`_PFNWI-I)ngM zR_mSWlGl_&d1o183~*&VE1D4W0&}dm75Cjj(zd{I_2rU&&ru5wS=*keaVy6tUx`~V zc^}pWjLc}GinS=8X0?1dLiq#1_y}&toO3l2lWg=;MAmXKE_l>N&f%q1R z(C(@|RFk#T{%1Ak>CXWkOY-@YXfskrtVWC}IfhKZc3@LPz-KO!Xp?iUR7dknu6?7evjM?s9>M``&~-il000McNliru-~8!;&tR)ZJ0W z+l^0@v_Xf;eo5bv)Z=q-YAz#^9+LDPNk^0pPHa{xlD^kukTps7NIKOANNOzyCH+>? zf=)39N#{t~)ncHVY$k)EKvGKSq_;rQbeo|x@-InmDT3}rffM)y$K;d1*(pzZ0(c*= z44Bof%B5w6E7N^8Vb5Gi|4ccVeUdIW7Rgw5Mplr6Wx2B{(%n-?ui0C4ETJm6Fz*OHIlyNsA>t*}@h7)MBZ_RBqg>Rg680B@HJc);>wMO8SJPMUvJ? z8jKxtqoiM?c*eScj7Xar0j~GyEfwO>vDFNDO=WHv>jk^Mv zK+U{fR^ayme+9k(d=#kUWm2^b_Bh}(z`IkbmJ-J`JAMS%X~y~lU{V{;b0;tx_`0!h zHgKb)7o)HwtNnIusMcBLwPt0w&ko@4c5Wr+=63_n0Gog(jRB_tSGNJq8ek>xB~wp; zVc_Qm*p3J!p8$0Mw*ePsG|c^07y2+T4|p8-8?XnMYDwwoz$w5nZGf{GxCyu%nA3p4 zy{2}Lv;$Js`p$D1TyEt0dnKJB>1~p(khI)f_f9Ky}fUj8qI4yBs

j=H`o47+s3-i9AodA!i{tc@z=*}NE-FsZEp8S z;A6nV0u$yDOIl}`$7*&hXZCId@MGU`P+dkHcWH}>u36FT z^FV)APv$Z*XrXz9tS)%aRBf%rId3+VnAs*czcWT$Yau~183t|uzG)uP-!iuuw@H$2 z&yjo8Bt0YPmK;WTlcbMJ+SuZ_K}pw0dZVNZ($=+$NKjADuu_pnR!Q2M*tgv(llQhb z_jyT6B)!YL<>%VX&r;i<7&RXQeqct@*OY9vNce7G74S#kXTZf4H+^u7dk+BL0G;$CXyMkEz;x3)0GxK1rXF zbX8(uAd+p8uC@h$`0sZ~sk6Tpvv4*(Yd zN5p=6*j#;iCD)kiKh`qnvkNSmwUS}r-dMP>0oZQ2-fLo$<;;$K#1;rj?PQCq+Idzg zElcW>MkW10(z%i@vz)G3x5%qH-8mLDN7X$W>?BBaNn0fC8{@d&MD2B*s7XhZ9R+@9 zuGCRZ^P{uvB*Lc9Mm8%7Ne*=-N75p61X7@y-A=PQi%`bgiQRjbm_zExM*-0Hp+HxI+Lv z4m{d1fKq}pEei1|^Tm+RFfTD9ojbj#cml^+fVP#e@A;^ba<#pLEi(5Nm3sw6gX2PJ zl=1tP1BC5)cb1-*M&M*^%&}bUSV<3h&dX|&u9GyeBmF@+dyxAH*WP{Ai5y{R_F7=5 zXyyc-!NH1Guk@I6^_o%M*^vxaRc^R#zOejV*f!+kNX3Dh?O& z#)0bsW>y@N3E$1AGy%w)RE<3wfXQPS?Tp|!Q=zfsbvE-|gztm(0?!7HKAWgl18*`= zRuI;oCyoKS7l{D5fv`L|;*mM=hl$i-i?L^?r3$A4=h!yYST$&dzSV+&7dKXw6h)!~ zgfFGc1197Ir`5w$Z8Ca{A_=eA%vKdr$&PmcBa6e&Pd8rxe zAO~~Y7?pykHLqqy{8Q@swmuq5Z4Yn!gIIOurb%cdG#4Q4m_4=YL}pRP&3 zsD$w!A*@;+w76@N_dv0LvPhivQ*7XGFoqU+zY1^*VUtuP3OtKWIST*(plE>hKRwRk zD_^n8D*GJum5X`%Yl^b!c)oA&QU}O;UA5}@`ZdDkm*EtVbvrg_+e>x^V(*r9d1UHH k6#aMlJ>Hk)Woaz`1CNdz7R3N=3IG5A07*qoM6N<$f}(R$djJ3c literal 0 HcmV?d00001 From 04272942271748f139c1abb8626d67de970f5f1a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 18 Apr 2016 00:20:40 +0200 Subject: [PATCH 242/274] Dynamically enable/disable logging #288 --- .../gadgetbridge/GBApplication.java | 65 +++++++++++++++---- .../activities/SettingsActivity.java | 19 +++--- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 14 files changed, 75 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index f292133a..640ce2db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -22,6 +22,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; import nodomain.freeyourgadget.gadgetbridge.database.ActivityDatabaseHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBConstants; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; @@ -50,6 +52,7 @@ public class GBApplication extends Application { //if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version private static final int CURRENT_PREFS_VERSION = 2; private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); + private static Appender fileLogger; public static final String ACTION_QUIT = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; @@ -86,7 +89,7 @@ public class GBApplication extends Application { // don't do anything here before we set up logging, otherwise // slf4j may be implicitly initialized before we properly configured it. - setupLogging(); + setupLogging(isFileLoggingEnabled()); if (getPrefsFileVersion() != CURRENT_PREFS_VERSION) { migratePrefs(getPrefsFileVersion()); @@ -122,32 +125,68 @@ public class GBApplication extends Application { return sharedPrefs.getBoolean("log_to_file", false); } - private void setupLogging() { - if (isFileLoggingEnabled()) { - try { + public static void setupLogging(boolean enable) { + try { + if (fileLogger == null) { File dir = FileUtils.getExternalFilesDir(); // used by assets/logback.xml since the location cannot be statically determined System.setProperty("GB_LOGFILES_DIR", dir.getAbsolutePath()); - getLogger().info("Gadgetbridge version: " + BuildConfig.VERSION_NAME); - } catch (IOException ex) { - Log.e("GBApplication", "External files dir not available, cannot log to file", ex); - removeFileLogger(); + rememberFileLogger(); } - } else { - removeFileLogger(); + if (enable) { + startFileLogger(); + } else { + stopFileLogger(); + } + getLogger().info("Gadgetbridge version: " + BuildConfig.VERSION_NAME); + } catch (IOException ex) { + Log.e("GBApplication", "External files dir not available, cannot log to file", ex); + stopFileLogger(); } } - private void removeFileLogger() { + private static void startFileLogger() { + if (fileLogger != null && !fileLogger.isStarted()) { + addFileLogger(fileLogger); + fileLogger.start(); + } + } + + private static void stopFileLogger() { + if (fileLogger != null && fileLogger.isStarted()) { + fileLogger.stop(); + removeFileLogger(fileLogger); + } + } + + private static void rememberFileLogger() { + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + fileLogger = root.getAppender("FILE"); + } + + private static void addFileLogger(Appender fileLogger) { try { ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - root.detachAppender("FILE"); + if (!root.isAttached(fileLogger)) { + root.addAppender(fileLogger); + } } catch (Throwable ex) { Log.e("GBApplication", "Error removing logger FILE appender", ex); } } - private Logger getLogger() { + private static void removeFileLogger(Appender fileLogger) { + try { + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + if (root.isAttached(fileLogger)) { + root.detachAppender(fileLogger); + } + } catch (Throwable ex) { + Log.e("GBApplication", "Error removing logger FILE appender", ex); + } + } + + private static Logger getLogger() { return LoggerFactory.getLogger(GBApplication.class); } 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 d541af9c..c8bfe835 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -12,6 +12,7 @@ import android.widget.Toast; import java.io.IOException; import java.util.List; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; @@ -88,16 +89,18 @@ public class SettingsActivity extends AbstractSettingsActivity { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - if (Boolean.TRUE.equals(newVal)) { - try { + boolean doEnable = Boolean.TRUE.equals(newVal); + try { + if (doEnable) { FileUtils.getExternalFilesDir(); // ensures that it is created - } catch (IOException ex) { - GB.toast(getApplicationContext(), - getString(R.string.error_creating_directory_for_logfiles, ex.getLocalizedMessage()), - Toast.LENGTH_LONG, - GB.ERROR, - ex); } + GBApplication.setupLogging(doEnable); + } catch (IOException ex) { + GB.toast(getApplicationContext(), + getString(R.string.error_creating_directory_for_logfiles, ex.getLocalizedMessage()), + Toast.LENGTH_LONG, + GB.ERROR, + ex); } return true; } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f9665cef..56557e6d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -108,7 +108,7 @@ Name/Alias Anzahl der Vibrationen Schlafmonitor - Log-Dateien schreiben (Neustart erforderlich) + Log-Dateien schreiben initialisiere Hole Aktivitätsdaten Von %1$s bis %2$s diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f4e48054..6dc1e3cc 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -107,7 +107,7 @@ Nombre/Apodo Número de vibraciones Monitor de sueño - Guardar logs (requiere reiniciar) + Guardar logs iniciando Recuperando datos de actividad Desde %1$s a %2$s diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0d578e24..5df0e5f7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -108,7 +108,7 @@ Nom/Pseudo Nombre de vibrations Moniteur de sommeil - Écrire les fichiers journaux (redémarrage requis) + Écrire les fichiers journaux Initialisation Récupération des données d\'activité De %1$s à %2$s diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index fa3cd0d7..14f87afb 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -108,7 +108,7 @@ Nome / Soprannome Numero vibrazioni Monitoraggio del sonno - Salva il log su file (richiede riavvio) + Salva il log su file inizializzazione in corso Recupero dati attività Da %1$s a %2$s diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6cf3ba01..3dd91611 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -108,7 +108,7 @@ 名前/別名 バイブレーション回数 睡眠観測 - ログファイルを出力 (再起動が必要) + ログファイルを出力 初期化中 活動データを取得中 %1$sから%2$sまで diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 407990cd..e8cf584d 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -105,7 +105,7 @@ 이름/별명 진동 횟수 수면 측정계 - 기록 파일 작성 (재시작 필요) + 기록 파일 작성 초기화 중 활동 데이터 가져오는 중 %1$s에서 %2$s(으)로 diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 1047a982..f1748748 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -105,7 +105,7 @@ Nazwisko/Pseudonim Liczba wibracji Monitor snu - Zapisuj logi (wymaga restartu) + Zapisuj logi Uruchamianie Pobieranie danych aktywności Od %1$s do %2$s diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6b444aec..1531fe66 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -106,7 +106,7 @@ Имя/псевдоним Количество вибраций Анализ сна - Записывать файлы журнала (нужен перезапуск) + Записывать файлы журнала Инициализация Получение данных активности От %1$s до %2$s diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3e58a5bf..58421a74 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -102,7 +102,7 @@ Titreşim adedi Uyku Monitörü - Kütük dosyalarını yaz (yeniden başlatmak gerekir) + Kütük dosyalarını yaz başlatılıyor Aktivite verisi alınıyor diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d0aac7a8..66d1f3f6 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -105,7 +105,7 @@ Ім\'я/нік Кількість вібрацій Аналіз сну - Записувати файли звіту (потрібен перезапуск) + Записувати файли звіту Ініціалізація… Отримання даних активності Від %1$s до %2$s diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 9d059902..af3de63c 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -83,7 +83,7 @@ Ảnh thiết bị Tên/Bí danh Trình giám sát giấc ngủ - Ghi tập tin nhật ký (cần khởi động lại) + Ghi tập tin nhật ký đang khởi chạy Từ %1$s đến %2$s Đeo bên trái hay phải? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5f43f764..1df50a7a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -126,7 +126,7 @@ Vibration Count Sleep Monitor - Write Log Files (needs restart) + Write Log Files initializing Fetching Activity Data From %1$s to %2$s From c573f989d0592913e8cfbf47550dafc3b677adae Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 21 Apr 2016 23:13:06 +0200 Subject: [PATCH 243/274] Prepare for 0.9.5 --- CHANGELOG.md | 10 ++++++++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 10 ++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e58d589..f375da1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ ###Changelog +####Version (0.9.5) +* Several UI Improvements +* Easier First-time setup by using a FAB +* Optional Dark Theme +* Notification App Blacklist is now sorted +* Gadgetbridge Icon in the notification bar displays connection state +* Logging is now configurable without restart +* Mi Band 1S: Initial live heartrate tracking +* Fix certain crash in charts activity on slower devices (#277) + ####Version (0.9.4) * Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) * Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly diff --git a/app/build.gradle b/app/build.gradle index f8f96f42..c2578c08 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.4" - versionCode 48 + versionName "0.9.5" + versionCode 49 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index b9dbba00..d7054da2 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,15 @@ + + Several UI Improvements + Easier First-time setup by using a FAB + Optional Dark Theme + Notification App Blacklist is now sorted + Gadgetbridge Icon in the notification bar displays connection state + Logging is now configurable without restart + Mi Band 1S: Initial live heartrate tracking + Fix certain crash in charts activity on slower devices (#277) + Fix crash in charts activities when changing the date, quickly (#277) Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) From d5639a052055ee23a35465469373ea73e6437414 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 21 Apr 2016 23:32:49 +0200 Subject: [PATCH 244/274] Updated translations from transifex (thanks!) --- app/src/main/res/values-fr/strings.xml | 16 +++++++++++++--- app/src/main/res/values-ja/strings.xml | 5 +++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5df0e5f7..4324f1f6 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -71,7 +71,7 @@ Le Bluetooth n\'est pas supporté. Le Bluetooth est désactivé. Cliquez sur l\'appareil pour le connecter au gestionnaire d\'application - Cliquez sur l\'appareil pour le connecter + Tapotter sur le périphérique pour le connecter. Connexion impossible. L’adresse Bluetooth est-elle invalide? Gadgetbridge est en fonctionnement Installation du binaire %1$d/%2$d @@ -102,13 +102,12 @@ 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 - Rendez votre appareil découvrable. Les appareils déjà connectés ne seront pas découverts. + Rendre votre montre découvrable. Pour l\'instant les appareils connectés ne seront probablement pas découvert. Si votre montre n’apparaît pas après 2 minutes, essayer à nouveau après avoir redémarré. Note: Image de l\'appareil Nom/Pseudo Nombre de vibrations Moniteur de sommeil - Écrire les fichiers journaux Initialisation Récupération des données d\'activité De %1$s à %2$s @@ -197,6 +196,7 @@ 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 précision du sommeil en attente de reconnexion Réinstaller A propos de vous @@ -211,4 +211,14 @@ Configurer ZzZz Ajouter un widget + Préférer le mode heure pendant le sommeil + Une alarme a été enregistré pour %1$02d:%2$02d + Modèle: %1$s + Micrologiciel: %1$s + Erreur à la création de votre fichier log : %1$s + HR: + Le firmware se met à jour + Échec lors de l\'écriture du micrologiciel + Rythme cardiaque + Rythme cardiaque diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 3dd91611..5018b7d2 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -32,6 +32,9 @@ 日付と時刻 時間を合わせる 接続したとき、Androidで時間またはタイムゾーンを変更したときに、デバイスに時間を同期 + テーマ + ライト + ダーク 通知 繰り返し 電話通知 @@ -220,4 +223,6 @@ HR: ファームウェアを更新しています ファームウェアを送信しませんでした + 心拍数 + 心拍数 From 3fefb57fdd36d3f320ed3fcd55dbcc8d9d191edd Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 23 Apr 2016 23:24:56 +0200 Subject: [PATCH 245/274] Fix colors in alarm activity for dark theme --- .../gadgetbridge/GBApplication.java | 17 +++++++++++++++++ .../charts/AbstractChartFragment.java | 12 +++--------- .../adapter/GBAlarmListAdapter.java | 3 ++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 640ce2db..d4adb011 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -6,11 +6,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.Color; import android.os.Build; import android.os.Build.VERSION; import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; +import android.util.TypedValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -344,4 +347,18 @@ public class GBApplication extends Application { public static boolean isDarkThemeEnabled() { return sharedPrefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_light)).equals(context.getString(R.string.pref_theme_value_dark)); } + + public static int getTextColor(Context context) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = context.getTheme(); + theme.resolveAttribute(android.R.attr.textColor, typedValue, true); + return typedValue.data; + } + public static int getBackgroundColor(Context context) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = context.getTheme(); + theme.resolveAttribute(android.R.attr.background, typedValue, true); + return typedValue.data; + } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 41c61114..b4c776ee 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -4,13 +4,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.content.LocalBroadcastManager; -import android.util.TypedValue; import android.view.View; import com.github.mikephil.charting.charts.BarLineChartBase; @@ -39,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment; import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; @@ -151,13 +150,8 @@ public abstract class AbstractChartFragment extends AbstractGBFragment { } protected void init() { - TypedValue typedValue = new TypedValue(); - Resources.Theme theme = getContext().getTheme(); - theme.resolveAttribute(android.R.attr.background, typedValue, true); - BACKGROUND_COLOR = typedValue.data; - theme.resolveAttribute(android.R.attr.textColor, typedValue, true); - LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = typedValue.data; - + BACKGROUND_COLOR = GBApplication.getBackgroundColor(getContext()); + LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = GBApplication.getTextColor(getContext()); CHART_TEXT_COLOR = getResources().getColor(R.color.secondarytext); HEARTRATE_COLOR = getResources().getColor(R.color.chart_heartrate); HEARTRATE_FILL_COLOR = getResources().getColor(R.color.chart_heartrate_fill); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java index fec4c929..5a5b7b3e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBAlarmListAdapter.java @@ -15,6 +15,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Set; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; @@ -152,7 +153,7 @@ public class GBAlarmListAdapter extends ArrayAdapter { if (isOn) { view.setTextColor(Color.BLUE); } else { - view.setTextColor(Color.BLACK); + view.setTextColor(GBApplication.getTextColor(mContext)); } } } From 18fe09bb7c88d0f867765945ee7803d0453f9f3f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 23 Apr 2016 23:31:19 +0200 Subject: [PATCH 246/274] make add icon on FAB white --- app/src/main/res/drawable/ic_add_white.png | Bin 0 -> 193 bytes .../main/res/layout/activity_controlcenter.xml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_add_white.png diff --git a/app/src/main/res/drawable/ic_add_white.png b/app/src/main/res/drawable/ic_add_white.png new file mode 100644 index 0000000000000000000000000000000000000000..3705a55786bd582196865e37db6dda03fe4e909e GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawn3BBRT^JZv^(q?yd7K3vk;M!Q z+`=Ht$S`Y;1W=H@#M9T6{Q--(sFb1PfyeuRLP4G`jv*QM-d+#nVh|8;aD4r*JZ`_; zRj$-~jMrScuBmuV>J$4aIP>z3Pka8|;$wt@zkFR^uRqwPdUzt^&eVUY4lpoD#dEt< YYYV!Z literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_controlcenter.xml b/app/src/main/res/layout/activity_controlcenter.xml index 7bdcc89f..1be7d667 100644 --- a/app/src/main/res/layout/activity_controlcenter.xml +++ b/app/src/main/res/layout/activity_controlcenter.xml @@ -41,7 +41,7 @@ android:layout_alignParentBottom="true" android:layout_alignParentEnd="true" android:layout_gravity="bottom|end" - android:src="@drawable/ic_add_black" + android:src="@drawable/ic_add_white" app:elevation="6dp" app:pressedTranslationZ="12dp" android:layout_marginBottom="10dp" From abe1c9070f8302f34c870338dbe9ba20aa927123 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 24 Apr 2016 11:32:09 +0200 Subject: [PATCH 247/274] update German and Korean from transifex, thanks! --- app/src/main/res/values-de/strings.xml | 4 ++-- app/src/main/res/values-ko/strings.xml | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 56557e6d..7dfeb3f7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -70,7 +70,7 @@ Dies ist eine Test Benachrichtigung von Gadgetbridge Bluetooth wird nicht unterstützt. Bluetooth ist abgeschaltet. - berühre das verbundene Gerät, um den App Mananger zu starten + berühre das verbundene Gerät, um den App Manager zu starten berühre ein Gerät zum Verbinden Verbindung kann nicht aufgebaut werden. BT Adresse ungültig? Gadgetbridge läuft @@ -102,7 +102,7 @@ Keine gültigen Benutzerinformationen angegeben, verwende Dummy-Daten für\'s Erste. Wenn Dein Mi Band vibriert und blinkt, tippe ein paar Mal schnell hintereinander darauf. Installieren - Mach Dein Gerät auffindbar. Derzeit verbundene Geräte sind in der Regel nicht auffindbar. + Mach Dein Gerät auffindbar. Derzeit verbundene Geräte sind in der Regel nicht auffindbar. Wenn Dein Gerät nach zwei Minuten nicht angezeigt wird, versuche es nach einem Neustart Deines Telefons noch einmal. Tipp: Bild des Geräts Name/Alias diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e8cf584d..ddce42bc 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -19,6 +19,7 @@ 펌웨어/앱 설치 지금 기존의 Mi Band 펌웨어 대신 %s 펌웨어를 설치하려고 합니다. + 지금 Mi Band의 기존 펌웨어 대신 %1$s와 %2$s 펌웨어를 설치하려고 합니다. 이 펌웨어는 테스트를 거쳤고 가젯브릿지와 호환됩니다. 이 펌웨어는 테스트를 거치지 않았고 가젯브릿지와 호환되지 않을 수 있습니다.\n\n Mi Band에 설치하지 않는 것이 좋습니다. 여전히 계속 진행하기를 원하고 이후에도 정상적으로 작동하기를 원한다면, %s 화이트리스트 펌웨어 버전을 가젯브릿지 개발자들에게 알려주세요. @@ -31,6 +32,9 @@ 날짜와 시간 시간 동기화 시간이 바뀌거나, 시간대가 바뀌거나, 접속을 할 때 기기와 시간을 동기화 + 테마 + 밝음 + 어두움 알림 반복 전화 @@ -48,6 +52,7 @@ 개발자 옵션 Mi Band 주소 Pebble 설정 + 선호하는 액티비티 트래커 제3자 안드로이드 앱 접근을 허용 PebbleKit을 사용하는 안드로이드 앱을 실험적으로 지원 강제 알림 프로토콜 @@ -99,7 +104,7 @@ 올바르지 않은 사용자 정보입니다. 일단 임시 사용자 정보를 사용합니다. Mi Band가 진동하고 깜빡일 때, 연달아 몇 번 두드리세요. 설치 - 기기를 발견 가능하도록 설정하세요. 현재 연결된 기기들은 발견될 수 없습니다. + 기기를 발견 가능하도록 설정하세요. 현재 연결된 기기들은 발견될 수 없습니다. 2분이 지나도 기기가 나타나지 않는다면 재부팅 후 다시 시도해보세요. 알림: 기기 이미지 이름/별명 @@ -194,6 +199,7 @@ 호환되지 않는 펌웨어 이 펌웨어는 기기와 호환되지 않습니다 다가오는 이벤트를 위해 예약할 알람 + 수면 측정을 향상시키기 위해 심박 센서 사용 재접속을 기다리는 중 재설치 당신에 대해 @@ -201,4 +207,21 @@ 성별 키 (cm) 몸무게 (kg) + 활성화 + 비활성화 + 인증 중 + 인증 필요 + 설정 + Zzz + 위젯 추가 + 선호하는 수면 시간 + %1$02d:%2$02d 알람이 설정되었습니다 + 하드웨어: %1$s + 펌웨어: %1$s + 기록 파일을 저장할 디렉토리 생성 오류: %1$s + 심박수: + 펌웨어 업데이트 진행 중 + 펌웨어가 전송되지 않음 + 심박수 + 심박수 From 36a34bd17c3b8542c944af37c7bf4c3258d41436 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 24 Apr 2016 11:37:18 +0200 Subject: [PATCH 248/274] fix remaining strings saying "App Mananger". Closes #290 (I fixed on transifex where possible, unfortunately some strings vanish there from time to time, I guess it is the case when the english string changes) --- app/src/main/res/values-ru/strings.xml | 4 ++-- app/src/main/res/values-uk/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 1531fe66..5039e880 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -12,7 +12,7 @@ Отключиться Отладка - App Mananger + App Manager Удалить Заблокированные уведомления @@ -68,7 +68,7 @@ Это тестовое уведомление от Gadgetbridge Bluetooth не поддерживается. Bluetooth отключён. - нажмите на подключённое устройство для App Mananger + нажмите на подключённое устройство для App Manager нажмите на устройство для соединения Не удалось соединиться. Неверен адрес BT? Gadgetbridge запущен diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 66d1f3f6..f0d6f36a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -67,7 +67,7 @@ Це тестове сповіщення від Gadgetbridge Bluetooth не підтримується. Bluetooth вимкнуто. - натисніть на під\'єднаний пристрій для App Mananger + натисніть на під\'єднаний пристрій для App Manager натисніть на пристрій для з\'єднання Не вдалося з\'єднатися. Можливо помилкова адреса BT? Gadgetbridge запущено From 65bd1581bc13b4cd805a6fe8d2309ee80129bb73 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 24 Apr 2016 17:34:36 +0200 Subject: [PATCH 249/274] Fix receivers, display measured heart rate as a toast again, fixes #292 --- .../gadgetbridge/activities/DebugActivity.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index a6eee775..d7d81f69 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -13,6 +13,7 @@ import android.os.Bundle; import android.support.v4.app.NavUtils; import android.support.v4.app.NotificationCompat; import android.support.v4.app.RemoteInput; +import android.support.v4.content.LocalBroadcastManager; import android.view.MenuItem; import android.view.View; import android.widget.Button; @@ -29,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; @@ -63,15 +65,22 @@ public class DebugActivity extends GBActivity { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { - case GBApplication.ACTION_QUIT: + case GBApplication.ACTION_QUIT: { finish(); break; - case ACTION_REPLY: + } + case ACTION_REPLY: { Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); CharSequence reply = remoteInput.getCharSequence(EXTRA_REPLY); LOG.info("got wearable reply: " + reply); GB.toast(context, "got wearable reply: " + reply, Toast.LENGTH_SHORT, GB.INFO); break; + } + case DeviceService.ACTION_HEARTRATE_MEASUREMENT: { + int hrValue = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, -1); + GB.toast(DebugActivity.this, "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO); + break; + } } } }; @@ -84,7 +93,9 @@ public class DebugActivity extends GBActivity { IntentFilter filter = new IntentFilter(); filter.addAction(GBApplication.ACTION_QUIT); filter.addAction(ACTION_REPLY); - registerReceiver(mReceiver, filter); + filter.addAction(DeviceService.ACTION_HEARTRATE_MEASUREMENT); + LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter); + registerReceiver(mReceiver, filter); // for ACTION_REPLY editContent = (EditText) findViewById(R.id.editContent); sendSMSButton = (Button) findViewById(R.id.sendSMSButton); @@ -348,6 +359,7 @@ public class DebugActivity extends GBActivity { @Override protected void onDestroy() { super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver); unregisterReceiver(mReceiver); } From b89eb14be79c53e8a3e77c6f4547264626cd57e8 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Apr 2016 00:13:09 +0200 Subject: [PATCH 250/274] allow two digits for number of call notifications (e.g. 60) --- app/src/main/res/xml/miband_preferences.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index c544d254..bccb1348 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -79,7 +79,7 @@ android:defaultValue="60" android:inputType="number" android:key="mi_vibration_count_incoming_call" - android:maxLength="1" + android:maxLength="2" android:title="@string/pref_title_notifications_repetitions" /> From 0c715a2669a95adf68800e28ced065b07714004d Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Apr 2016 23:18:55 +0200 Subject: [PATCH 251/274] Wrap access to SharedPreferences with "Prefs" (to centralize quirk handling) --- .../gadgetbridge/GBApplication.java | 10 +- .../activities/AppBlacklistActivity.java | 4 - .../activities/AppManagerActivity.java | 11 +- .../activities/ConfigureAlarms.java | 13 +- .../activities/ControlCenter.java | 7 +- .../PebbleContentProvider.java | 6 +- .../devices/miband/MiBandPairingActivity.java | 5 +- .../devices/pebble/PebbleCoordinator.java | 5 +- .../BluetoothStateChangeReceiver.java | 5 +- .../externalevents/K9Receiver.java | 7 +- .../externalevents/NotificationListener.java | 11 +- .../externalevents/PebbleReceiver.java | 7 +- .../externalevents/PhoneCallReceiver.java | 5 +- .../externalevents/SMSReceiver.java | 8 +- .../externalevents/TimeChangeReceiver.java | 5 +- .../service/DeviceCommunicationService.java | 17 ++- .../devices/pebble/PebbleIoThread.java | 14 +-- .../receivers/GBMusicControlReceiver.java | 6 +- .../gadgetbridge/util/DeviceHelper.java | 9 +- .../gadgetbridge/util/Prefs.java | 113 ++++++++++++++++++ 20 files changed, 198 insertions(+), 70 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index d4adb011..a50e5831 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -36,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; //import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectReceiver; @@ -56,6 +57,7 @@ public class GBApplication extends Application { private static final int CURRENT_PREFS_VERSION = 2; private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); private static Appender fileLogger; + private static Prefs prefs; public static final String ACTION_QUIT = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; @@ -89,6 +91,7 @@ public class GBApplication extends Application { super.onCreate(); sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs = new Prefs(sharedPrefs); // don't do anything here before we set up logging, otherwise // slf4j may be implicitly initialized before we properly configured it. @@ -125,7 +128,7 @@ public class GBApplication extends Application { } public static boolean isFileLoggingEnabled() { - return sharedPrefs.getBoolean("log_to_file", false); + return prefs.getBoolean("log_to_file", false); } public static void setupLogging(boolean enable) { @@ -345,7 +348,7 @@ public class GBApplication extends Application { } public static boolean isDarkThemeEnabled() { - return sharedPrefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_light)).equals(context.getString(R.string.pref_theme_value_dark)); + return prefs.getString("pref_key_theme", context.getString(R.string.pref_theme_value_light)).equals(context.getString(R.string.pref_theme_value_dark)); } public static int getTextColor(Context context) { @@ -361,4 +364,7 @@ public class GBApplication extends Application { return typedValue.data; } + public static Prefs getPrefs() { + return prefs; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index bef49438..c7db9fc7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -4,11 +4,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.view.LayoutInflater; @@ -47,7 +45,6 @@ public class AppBlacklistActivity extends GBActivity { } }; - private SharedPreferences sharedPrefs; private IdentityHashMap nameMap; @Override @@ -56,7 +53,6 @@ public class AppBlacklistActivity extends GBActivity { setContentView(R.layout.activity_appblacklist); final PackageManager pm = getPackageManager(); - sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); final List packageList = pm.getInstalledApplications(PackageManager.GET_META_DATA); ListView appListView = (ListView) findViewById(R.id.appListView); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java index 5be4f6f0..2e6bbba0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppManagerActivity.java @@ -4,10 +4,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.v4.app.NavUtils; import android.support.v4.content.LocalBroadcastManager; import android.view.ContextMenu; @@ -34,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class AppManagerActivity extends GBActivity { @@ -59,7 +58,7 @@ public class AppManagerActivity extends GBActivity { appList.add(new GBDeviceApp(uuid, appName, appCreator, "", appType)); } - if (sharedPrefs.getBoolean("pebble_force_untested", false)) { + if (prefs.getBoolean("pebble_force_untested", false)) { appList.addAll(getSystemApps()); } @@ -68,7 +67,7 @@ public class AppManagerActivity extends GBActivity { } }; - private SharedPreferences sharedPrefs; + private Prefs prefs; private final List appList = new ArrayList<>(); private GBDeviceAppAdapter mGBDeviceAppAdapter; @@ -130,7 +129,7 @@ public class AppManagerActivity extends GBActivity { throw new IllegalArgumentException("Must provide a device when invoking this activity"); } - sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + prefs = GBApplication.getPrefs(); setContentView(R.layout.activity_appmanager); @@ -150,7 +149,7 @@ public class AppManagerActivity extends GBActivity { appList.addAll(getCachedApps()); - if (sharedPrefs.getBoolean("pebble_force_untested", false)) { + if (prefs.getBoolean("pebble_force_untested", false)) { appList.addAll(getSystemApps()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 49f8e7a7..c9a8c33d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -16,6 +16,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.adapter.GBAlarmListAdapter; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.impl.GBAlarm; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS; @@ -34,12 +35,12 @@ public class ConfigureAlarms extends GBActivity { setContentView(R.layout.activity_configure_alarms); - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - preferencesAlarmListSet = sharedPrefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); + Prefs prefs = GBApplication.getPrefs(); + preferencesAlarmListSet = prefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); if (preferencesAlarmListSet.isEmpty()) { //initialize the preferences preferencesAlarmListSet = new HashSet<>(Arrays.asList(GBAlarm.DEFAULT_ALARMS)); - sharedPrefs.edit().putStringSet(PREF_MIBAND_ALARMS, preferencesAlarmListSet).apply(); + prefs.getPreferences().edit().putStringSet(PREF_MIBAND_ALARMS, preferencesAlarmListSet).apply(); } mGBAlarmListAdapter = new GBAlarmListAdapter(this, preferencesAlarmListSet); @@ -66,9 +67,9 @@ public class ConfigureAlarms extends GBActivity { } private void updateAlarmsFromPrefs() { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - preferencesAlarmListSet = sharedPrefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); - int reservedSlots = Integer.parseInt(sharedPrefs.getString(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, "0")); + Prefs prefs = GBApplication.getPrefs(); + preferencesAlarmListSet = prefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); + int reservedSlots = Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, "0")); mGBAlarmListAdapter.setAlarmList(preferencesAlarmListSet, reservedSlots); mGBAlarmListAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index b9ad4d98..9b6ab4b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class ControlCenter extends GBActivity { @@ -184,9 +185,9 @@ public class ControlCenter extends GBActivity { /* * Ask for permission to intercept notifications on first run. */ - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - if (sharedPrefs.getBoolean("firstrun", true)) { - sharedPrefs.edit().putBoolean("firstrun", false).apply(); + Prefs prefs = GBApplication.getPrefs(); + if (prefs.getBoolean("firstrun", true)) { + prefs.getPreferences().edit().putBoolean("firstrun", false).apply(); Intent enableIntent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); startActivity(enableIntent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java index c20a552d..27976ff3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java @@ -14,8 +14,10 @@ import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.content.LocalBroadcastManager; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PebbleContentProvider extends ContentProvider { @@ -59,8 +61,8 @@ public class PebbleContentProvider extends ContentProvider { MatrixCursor mc = new MatrixCursor(columnNames); int connected = 0; int appMessage = 0; - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.getContext()); - if (sharedPrefs.getBoolean("pebble_enable_pebblekit", false)) { + Prefs prefs = GBApplication.getPrefs(); + if (prefs.getBoolean("pebble_enable_pebblekit", false)) { appMessage = 1; } if (mGBDevice != null && mGBDevice.getType() == DeviceType.PEBBLE && mGBDevice.isInitialized()) { 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 9a2b9fac..1a950c86 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 @@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class MiBandPairingActivity extends Activity { private static final Logger LOG = LoggerFactory.getLogger(MiBandPairingActivity.class); @@ -170,8 +171,8 @@ public class MiBandPairingActivity extends Activity { unregisterReceiver(mBondingReceiver); if (pairedSuccessfully) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - sharedPrefs.edit().putString(MiBandConst.PREF_MIBAND_ADDRESS, macAddress).apply(); + Prefs prefs = GBApplication.getPrefs(); + prefs.getPreferences().edit().putString(MiBandConst.PREF_MIBAND_ADDRESS, macAddress).apply(); } Intent intent = new Intent(this, ControlCenter.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index f7017d7b..e2b3a656 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -14,6 +14,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PebbleCoordinator extends AbstractDeviceCoordinator { public PebbleCoordinator() { @@ -45,8 +46,8 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider() { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - int activityTracker = Integer.parseInt(sharedPrefs.getString("pebble_activitytracker", Integer.toString(SampleProvider.PROVIDER_PEBBLE_HEALTH))); + Prefs prefs = GBApplication.getPrefs(); + int activityTracker = Integer.parseInt(prefs.getString("pebble_activitytracker", Integer.toString(SampleProvider.PROVIDER_PEBBLE_HEALTH))); switch (activityTracker) { case SampleProvider.PROVIDER_PEBBLE_HEALTH: return new HealthSampleProvider(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 74a3d2dc..2b21690c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -10,6 +10,7 @@ import android.support.v4.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenter; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class BluetoothStateChangeReceiver extends BroadcastReceiver { @Override @@ -22,8 +23,8 @@ public class BluetoothStateChangeReceiver extends BroadcastReceiver { Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); LocalBroadcastManager.getInstance(context).sendBroadcast(refreshIntent); - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - if (!sharedPrefs.getBoolean("general_autoconnectonbluetooth", false)) { + Prefs prefs = GBApplication.getPrefs(); + if (!prefs.getBoolean("general_autoconnectonbluetooth", false)) { return; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index 1b615d24..8487ead6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class K9Receiver extends BroadcastReceiver { @@ -24,11 +25,11 @@ public class K9Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - if ("never".equals(sharedPrefs.getString("notification_mode_k9mail", "when_screen_off"))) { + Prefs prefs = GBApplication.getPrefs(); + if ("never".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { return; } - if ("when_screen_off".equals(sharedPrefs.getString("notification_mode_k9mail", "when_screen_off"))) { + if ("when_screen_off".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { PowerManager powermanager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (powermanager.isScreenOn()) { return; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 124c0b99..19d14bd7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -30,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class NotificationListener extends NotificationListenerService { @@ -161,8 +162,8 @@ public class NotificationListener extends NotificationListenerService { return; } - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - if (!sharedPrefs.getBoolean("notifications_generic_whenscreenon", false)) { + Prefs prefs = GBApplication.getPrefs(); + if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) { PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE); if (powermanager.isScreenOn()) { return; @@ -189,13 +190,13 @@ public class NotificationListener extends NotificationListenerService { } if (source.equals("eu.siacs.conversations")) { - if (!"never".equals(sharedPrefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { + if (!"never".equals(prefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { return; } } if (source.equals("com.fsck.k9")) { - if (!"never".equals(sharedPrefs.getString("notification_mode_k9mail", "when_screen_off"))) { + if (!"never".equals(prefs.getString("notification_mode_k9mail", "when_screen_off"))) { return; } } @@ -205,7 +206,7 @@ public class NotificationListener extends NotificationListenerService { source.equals("com.sonyericsson.conversations") || source.equals("com.android.messaging") || source.equals("org.smssecure.smssecure")) { - if (!"never".equals(sharedPrefs.getString("notification_mode_sms", "when_screen_off"))) { + if (!"never".equals(prefs.getString("notification_mode_sms", "when_screen_off"))) { return; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java index 01dab88f..42e94e4b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java @@ -15,6 +15,7 @@ import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PebbleReceiver extends BroadcastReceiver { @@ -23,11 +24,11 @@ public class PebbleReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - if ("never".equals(sharedPrefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { + Prefs prefs = GBApplication.getPrefs(); + if ("never".equals(prefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { return; } - if ("when_screen_off".equals(sharedPrefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { + if ("when_screen_off".equals(prefs.getString("notification_mode_pebblemsg", "when_screen_off"))) { PowerManager powermanager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (powermanager.isScreenOn()) { return; 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 010b5fb8..054723fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -9,6 +9,7 @@ import android.telephony.TelephonyManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PhoneCallReceiver extends BroadcastReceiver { @@ -64,8 +65,8 @@ public class PhoneCallReceiver extends BroadcastReceiver { break; } if (callCommand != CallSpec.CALL_UNDEFINED) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - if ("never".equals(sharedPrefs.getString("notification_mode_calls", "always"))) { + Prefs prefs = GBApplication.getPrefs(); + if ("never".equals(prefs.getString("notification_mode_calls", "always"))) { return; } CallSpec callSpec = new CallSpec(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java index a2dd09a3..d6db2027 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java @@ -12,17 +12,17 @@ import android.telephony.SmsMessage; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - if ("never".equals(sharedPrefs.getString("notification_mode_sms", "when_screen_off"))) { + Prefs prefs = GBApplication.getPrefs(); + if ("never".equals(prefs.getString("notification_mode_sms", "when_screen_off"))) { return; } - if ("when_screen_off".equals(sharedPrefs.getString("notification_mode_sms", "when_screen_off"))) { + if ("when_screen_off".equals(prefs.getString("notification_mode_sms", "when_screen_off"))) { PowerManager powermanager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (powermanager.isScreenOn()) { return; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java index 82f0836b..e2ea614c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java @@ -14,6 +14,7 @@ import java.util.GregorianCalendar; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class TimeChangeReceiver extends BroadcastReceiver { @@ -22,10 +23,10 @@ public class TimeChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); + Prefs prefs = GBApplication.getPrefs(); final String action = intent.getAction(); - if (sharedPrefs.getBoolean("datetime_synconconnect", true) && (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED))) { + if (prefs.getBoolean("datetime_synconconnect", true) && (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED))) { Date newTime = GregorianCalendar.getInstance().getTime(); LOG.info("Time or Timezone changed, syncing with device: " + DateTimeUtils.formatDate(newTime) + " (" + newTime.toGMTString() + "), " + intent.getAction()); GBApplication.deviceService().onSetTime(); 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 c26cb1b8..b5b4b7fb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -6,11 +6,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.IBinder; -import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; @@ -39,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CALLSTATE; @@ -170,7 +169,7 @@ public class DeviceCommunicationService extends Service { // when we get past this, we should have valid mDeviceSupport and mGBDevice instances - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + Prefs prefs = GBApplication.getPrefs(); switch (action) { case ACTION_START: start(); @@ -181,8 +180,8 @@ public class DeviceCommunicationService extends Service { String btDeviceAddress = null; if (gbDevice == null) { btDeviceAddress = intent.getStringExtra(EXTRA_DEVICE_ADDRESS); - if (btDeviceAddress == null && sharedPrefs != null) { // may be null in test cases - btDeviceAddress = sharedPrefs.getString("last_device_address", null); + if (btDeviceAddress == null && prefs != null) { // may be null in test cases + btDeviceAddress = prefs.getString("last_device_address", null); } if (btDeviceAddress != null) { gbDevice = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this); @@ -191,8 +190,8 @@ public class DeviceCommunicationService extends Service { btDeviceAddress = gbDevice.getAddress(); } - if (sharedPrefs != null) { - sharedPrefs.edit().putString("last_device_address", btDeviceAddress).apply(); + if (prefs != null) { + prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply(); } if (gbDevice != null && !isConnecting() && !isConnected()) { @@ -241,12 +240,12 @@ public class DeviceCommunicationService extends Service { if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) > 0) || (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null)) { // NOTE: maybe not where it belongs - if (sharedPrefs.getBoolean("pebble_force_untested", false)) { + if (prefs.getBoolean("pebble_force_untested", false)) { // I would rather like to save that as an array in ShadredPreferences // this would work but I dont know how to do the same in the Settings Activity's xml ArrayList replies = new ArrayList<>(); for (int i = 1; i <= 16; i++) { - String reply = sharedPrefs.getString("canned_reply_" + i, null); + String reply = prefs.getString("canned_reply_" + i, null); if (reply != null && !reply.equals("")) { replies.add(reply); } 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 b2a42630..e3d22eaa 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 @@ -8,10 +8,8 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.net.Uri; import android.os.ParcelUuid; -import android.preference.PreferenceManager; import org.json.JSONArray; import org.json.JSONException; @@ -29,6 +27,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo; @@ -44,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class PebbleIoThread extends GBDeviceIoThread { private static final Logger LOG = LoggerFactory.getLogger(PebbleIoThread.class); @@ -62,7 +62,7 @@ public class PebbleIoThread extends GBDeviceIoThread { public static final String PEBBLEKIT_ACTION_APP_START = "com.getpebble.action.app.START"; public static final String PEBBLEKIT_ACTION_APP_STOP = "com.getpebble.action.app.STOP"; - final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + final Prefs prefs = GBApplication.getPrefs(); private final PebbleProtocol mPebbleProtocol; private final PebbleSupport mPebbleSupport; @@ -160,7 +160,7 @@ public class PebbleIoThread extends GBDeviceIoThread { mPebbleProtocol = (PebbleProtocol) gbDeviceProtocol; mBtAdapter = btAdapter; mPebbleSupport = pebbleSupport; - mEnablePebblekit = sharedPrefs.getBoolean("pebble_enable_pebblekit", false); + mEnablePebblekit = prefs.getBoolean("pebble_enable_pebblekit", false); } @Override @@ -199,7 +199,7 @@ public class PebbleIoThread extends GBDeviceIoThread { return false; } - mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false)); + mPebbleProtocol.setForceProtocol(prefs.getBoolean("pebble_force_protocol", false)); mIsConnected = true; if (originalState == GBDevice.State.WAITING_FOR_RECONNECT) { @@ -364,7 +364,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (e.getMessage().contains("socket closed")) { //FIXME: this does not feel right LOG.info(e.getMessage()); mIsConnected = false; - int reconnectAttempts = Integer.valueOf(sharedPrefs.getString("pebble_reconnect_attempts", "10")); + int reconnectAttempts = Integer.valueOf(prefs.getString("pebble_reconnect_attempts", "10")); if (reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); @@ -480,7 +480,7 @@ public class PebbleIoThread extends GBDeviceIoThread { private boolean evaluateGBDeviceEventPebble(GBDeviceEvent deviceEvent) { if (deviceEvent instanceof GBDeviceEventVersionInfo) { - if (sharedPrefs.getBoolean("datetime_synconconnect", true)) { + if (prefs.getBoolean("datetime_synconconnect", true)) { LOG.info("syncing time"); write(mPebbleProtocol.encodeSetTime()); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java index fce01765..053f034e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java @@ -12,7 +12,9 @@ import android.view.KeyEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class GBMusicControlReceiver extends BroadcastReceiver { private static final Logger LOG = LoggerFactory.getLogger(GBMusicControlReceiver.class); @@ -53,8 +55,8 @@ public class GBMusicControlReceiver extends BroadcastReceiver { } if (keyCode != -1) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - String audioPlayer = sharedPrefs.getString("audio_player", "default"); + Prefs prefs = GBApplication.getPrefs(); + String audioPlayer = prefs.getString("audio_player", "default"); long eventtime = SystemClock.uptimeMillis(); 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 b3aad4b7..ce41e842 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -12,6 +12,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; @@ -75,8 +76,8 @@ public class DeviceHelper { } } - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); - String miAddr = sharedPrefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, ""); + Prefs prefs = GBApplication.getPrefs(); + String miAddr = prefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, ""); if (miAddr.length() > 0) { GBDevice miDevice = new GBDevice(miAddr, "MI", DeviceType.MIBAND); if (!availableDevices.contains(miDevice)) { @@ -84,8 +85,8 @@ public class DeviceHelper { } } - String pebbleEmuAddr = sharedPrefs.getString("pebble_emu_addr", ""); - String pebbleEmuPort = sharedPrefs.getString("pebble_emu_port", ""); + String pebbleEmuAddr = prefs.getString("pebble_emu_addr", ""); + String pebbleEmuPort = prefs.getString("pebble_emu_port", ""); if (pebbleEmuAddr.length() >= 7 && pebbleEmuPort.length() > 0) { GBDevice pebbleEmuDevice = new GBDevice(pebbleEmuAddr + ":" + pebbleEmuPort, "Pebble qemu", DeviceType.PEBBLE); availableDevices.add(pebbleEmuDevice); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java new file mode 100644 index 00000000..fcb49112 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java @@ -0,0 +1,113 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.content.SharedPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +/** + * Wraps SharedPreferences to avoid ClassCastExceptions and others. + */ +public class Prefs { + private static final Logger LOG = LoggerFactory.getLogger(Prefs.class); + + private final SharedPreferences preferences; + + public Prefs(SharedPreferences preferences) { + this.preferences = preferences; + } + + public SharedPreferences getPreferences() { + return preferences; + } + + public String getString(String key, String defaultValue) { + String value = preferences.getString(key, defaultValue); + if (value == null || "".equals(value)) { + return defaultValue; + } + return value; + } + + public Set getStringSet(String key, Set defaultValue) { + Set value = preferences.getStringSet(key, defaultValue); + if (value == null || value.isEmpty()) { + return defaultValue; + } + return value; + } + + public int getInt(String key, int defaultValue) { + try { + return preferences.getInt(key, defaultValue); + } catch (Exception ex) { + try { + String value = preferences.getString(key, String.valueOf(defaultValue)); + if ("".equals(value)) { + return defaultValue; + } + return Integer.parseInt(value); + } catch (Exception ex2) { + logReadError(key, ex); + return defaultValue; + } + } + } + + public long getLong(String key, long defaultValue) { + try { + return preferences.getLong(key, defaultValue); + } catch (Exception ex) { + try { + String value = preferences.getString(key, String.valueOf(defaultValue)); + if ("".equals(value)) { + return defaultValue; + } + return Long.parseLong(value); + } catch (Exception ex2) { + logReadError(key, ex); + return defaultValue; + } + } + } + + public float getFloat(String key, float defaultValue) { + try { + return preferences.getFloat(key, defaultValue); + } catch (Exception ex) { + try { + String value = preferences.getString(key, String.valueOf(defaultValue)); + if ("".equals(value)) { + return defaultValue; + } + return Float.parseFloat(value); + } catch (Exception ex2) { + logReadError(key, ex); + return defaultValue; + } + } + } + + public boolean getBoolean(String key, boolean defaultValue) { + try { + return preferences.getBoolean(key, defaultValue); + } catch (Exception ex) { + try { + String value = preferences.getString(key, String.valueOf(defaultValue)); + if ("".equals(value)) { + return defaultValue; + } + return Boolean.parseBoolean(value); + } catch (Exception ex2) { + logReadError(key, ex); + return defaultValue; + } + } + } + + private void logReadError(String key, Exception ex) { + LOG.error("Error reading preference value: " + key + "; returning default value", ex); // log the first exception + } +} From 0704915a882303f0fd23eb8a30c218cc4f1b9323 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Apr 2016 23:39:03 +0200 Subject: [PATCH 252/274] Move parsing of preference strings to int values to Prefs --- .../gadgetbridge/GBApplication.java | 1 - .../activities/ConfigureAlarms.java | 2 +- .../PebbleContentProvider.java | 2 -- .../devices/miband/MiBandConst.java | 17 +++++--------- .../devices/miband/MiBandCoordinator.java | 15 +++++++------ .../devices/pebble/PebbleCoordinator.java | 4 +--- .../gadgetbridge/impl/GBAlarm.java | 9 ++++---- .../gadgetbridge/model/ActivityUser.java | 13 ++++++----- .../service/AbstractDeviceSupport.java | 7 +++--- .../service/devices/miband/MiBandSupport.java | 22 ++++++++++--------- .../operations/FetchActivityOperation.java | 3 ++- .../devices/pebble/PebbleIoThread.java | 2 +- .../receivers/GBMusicControlReceiver.java | 2 -- 13 files changed, 44 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index a50e5831..214349c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.res.Resources; -import android.graphics.Color; import android.os.Build; import android.os.Build.VERSION; import android.preference.PreferenceManager; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index c9a8c33d..e14a914c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -69,7 +69,7 @@ public class ConfigureAlarms extends GBActivity { private void updateAlarmsFromPrefs() { Prefs prefs = GBApplication.getPrefs(); preferencesAlarmListSet = prefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); - int reservedSlots = Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, "0")); + int reservedSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0); mGBAlarmListAdapter.setAlarmList(preferencesAlarmListSet, reservedSlots); mGBAlarmListAdapter.notifyDataSetChanged(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java index 27976ff3..d0235521 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java @@ -6,11 +6,9 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.v4.content.LocalBroadcastManager; 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 511de9dd..6853b371 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 @@ -1,10 +1,10 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; -import android.content.SharedPreferences; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + public final class MiBandConst { private static final Logger LOG = LoggerFactory.getLogger(MiBandConst.class); @@ -27,19 +27,12 @@ public final class MiBandConst { public static final String MI_1A = "1A"; public static final String MI_1S = "1S"; - public static int getNotificationPrefIntValue(String pref, String origin, SharedPreferences prefs, int defaultValue) { + public static int getNotificationPrefIntValue(String pref, String origin, Prefs prefs, int defaultValue) { String key = getNotificationPrefKey(pref, origin); - String value = null; - try { - value = prefs.getString(key, String.valueOf(defaultValue)); - return Integer.valueOf(value); - } catch (NumberFormatException ex) { - LOG.error("Error converting preference value to int: " + key + ": " + value); - return defaultValue; - } + return prefs.getInt(key, defaultValue); } - public static String getNotificationPrefStringValue(String pref, String origin, SharedPreferences prefs, String defaultValue) { + public static String getNotificationPrefStringValue(String pref, String origin, Prefs prefs, String defaultValue) { String key = getNotificationPrefKey(pref, origin); return prefs.getString(key, defaultValue); } 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 86ea9330..9a504f8e 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 @@ -18,6 +18,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class MiBandCoordinator extends AbstractDeviceCoordinator { private static final Logger LOG = LoggerFactory.getLogger(MiBandCoordinator.class); @@ -112,7 +113,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { */ public static UserInfo getConfiguredUserInfo(String miBandAddress) throws IllegalArgumentException { ActivityUser activityUser = new ActivityUser(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + Prefs prefs = GBApplication.getPrefs(); UserInfo info = UserInfo.create( miBandAddress, @@ -128,7 +129,7 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { public static int getWearLocation(String miBandAddress) throws IllegalArgumentException { int location = 0; //left hand - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + Prefs prefs = GBApplication.getPrefs(); if ("right".equals(prefs.getString(MiBandConst.PREF_MIBAND_WEARSIDE, "left"))) { location = 1; // right hand } @@ -136,17 +137,17 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { } public static boolean getHeartrateSleepSupport(String miBandAddress) throws IllegalArgumentException { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + Prefs prefs = GBApplication.getPrefs(); return prefs.getBoolean(MiBandConst.PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION, false); } public static int getFitnessGoal(String miBandAddress) throws IllegalArgumentException { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - return Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_FITNESS_GOAL, "10000")); + Prefs prefs = GBApplication.getPrefs(); + return prefs.getInt(MiBandConst.PREF_MIBAND_FITNESS_GOAL, 10000); } public static int getReservedAlarmSlots(String miBandAddress) throws IllegalArgumentException { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - return Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, "0")); + Prefs prefs = GBApplication.getPrefs(); + return prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index e2b3a656..d76e7126 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -2,9 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; import android.net.Uri; -import android.preference.PreferenceManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.activities.AppManagerActivity; @@ -47,7 +45,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { @Override public SampleProvider getSampleProvider() { Prefs prefs = GBApplication.getPrefs(); - int activityTracker = Integer.parseInt(prefs.getString("pebble_activitytracker", Integer.toString(SampleProvider.PROVIDER_PEBBLE_HEALTH))); + int activityTracker = prefs.getInt("pebble_activitytracker", SampleProvider.PROVIDER_PEBBLE_HEALTH); switch (activityTracker) { case SampleProvider.PROVIDER_PEBBLE_HEALTH: return new HealthSampleProvider(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java index d0178e4c..546bcee2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java @@ -1,8 +1,6 @@ package nodomain.freeyourgadget.gadgetbridge.impl; -import android.content.SharedPreferences; import android.os.Parcel; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import java.util.Calendar; @@ -12,6 +10,7 @@ import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MIBAND_ALARMS; @@ -187,8 +186,8 @@ public class GBAlarm implements Alarm { } public void store() { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - Set preferencesAlarmListSet = sharedPrefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); + Prefs prefs = GBApplication.getPrefs(); + Set preferencesAlarmListSet = prefs.getStringSet(PREF_MIBAND_ALARMS, new HashSet()); //the old Set cannot be updated in place see http://developer.android.com/reference/android/content/SharedPreferences.html#getStringSet%28java.lang.String,%20java.util.Set%3Cjava.lang.String%3E%29 Set newPrefs = new HashSet<>(preferencesAlarmListSet); @@ -202,7 +201,7 @@ public class GBAlarm implements Alarm { } } newPrefs.add(this.toPreferences()); - sharedPrefs.edit().putStringSet(PREF_MIBAND_ALARMS, newPrefs).apply(); + prefs.getPreferences().edit().putStringSet(PREF_MIBAND_ALARMS, newPrefs).apply(); } public static final Creator CREATOR = new Creator() { 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 c0a436a6..9383a9ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -6,6 +6,7 @@ import android.preference.PreferenceManager; import java.util.Calendar; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; /** * Class holding the common user information needed by most activity trackers @@ -86,11 +87,11 @@ public class ActivityUser { } private void fetchPreferences() { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - activityUserGender = Integer.parseInt(prefs.getString(PREF_USER_GENDER, Integer.toString(defaultUserGender))); - activityUserHeightCm = Integer.parseInt(prefs.getString(PREF_USER_HEIGHT_CM, Integer.toString(defaultUserHeightCm))); - activityUserWeightKg = Integer.parseInt(prefs.getString(PREF_USER_WEIGHT_KG, Integer.toString(defaultUserWeightKg))); - activityUserYearOfBirth = Integer.parseInt(prefs.getString(PREF_USER_YEAR_OF_BIRTH, Integer.toString(defaultUserYearOfBirth))); - activityUserSleepDuration = Integer.parseInt(prefs.getString(PREF_USER_SLEEP_DURATION, Integer.toString(defaultUserSleepDuration))); + Prefs prefs = GBApplication.getPrefs(); + activityUserGender = prefs.getInt(PREF_USER_GENDER, defaultUserGender); + activityUserHeightCm = prefs.getInt(PREF_USER_HEIGHT_CM, defaultUserHeightCm); + activityUserWeightKg = prefs.getInt(PREF_USER_WEIGHT_KG, defaultUserWeightKg); + activityUserYearOfBirth = prefs.getInt(PREF_USER_YEAR_OF_BIRTH, defaultUserYearOfBirth); + activityUserSleepDuration = prefs.getInt(PREF_USER_SLEEP_DURATION, defaultUserSleepDuration); } } 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 d103011a..683829c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -6,11 +6,9 @@ import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.telephony.SmsManager; @@ -44,6 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBCallControlReceiver; import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBMusicControlReceiver; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; // TODO: support option for a single reminder notification when notifications could not be delivered? // conditions: app was running and received notifications, but device was not connected. @@ -247,8 +246,8 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { Intent notificationListenerIntent = new Intent(action); notificationListenerIntent.putExtra("handle", deviceEvent.handle); if (deviceEvent.reply != null) { - SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); - String suffix = sharedPrefs.getString("canned_reply_suffix", null); + Prefs prefs = GBApplication.getPrefs(); + String suffix = prefs.getString("canned_reply_suffix", null); if (suffix != null && !Objects.equals(suffix, "")) { deviceEvent.reply += suffix; } 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 97357ccf..ea3acc03 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 @@ -21,6 +21,7 @@ import java.util.GregorianCalendar; import java.util.List; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; @@ -52,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.Fe import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COLOUR; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_FLASH_COUNT; @@ -422,7 +424,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { private void performPreferredNotification(String task, String notificationOrigin, BtLEAction extraAction) { try { TransactionBuilder builder = performInitialized(task); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + Prefs prefs = GBApplication.getPrefs(); int vibrateDuration = getPreferredVibrateDuration(notificationOrigin, prefs); int vibratePause = getPreferredVibratePause(notificationOrigin, prefs); short vibrateTimes = getPreferredVibrateCount(notificationOrigin, prefs); @@ -441,35 +443,35 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } } - private int getPreferredFlashDuration(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredFlashDuration(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(FLASH_DURATION, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_DURATION); } - private int getPreferredOriginalColour(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredOriginalColour(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(FLASH_ORIGINAL_COLOUR, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_ORIGINAL_COLOUR); } - private int getPreferredFlashColour(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredFlashColour(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(FLASH_COLOUR, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_COLOUR); } - private int getPreferredFlashCount(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredFlashCount(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(FLASH_COUNT, notificationOrigin, prefs, DEFAULT_VALUE_FLASH_COUNT); } - private int getPreferredVibratePause(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredVibratePause(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(VIBRATION_PAUSE, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_PAUSE); } - private short getPreferredVibrateCount(String notificationOrigin, SharedPreferences prefs) { + private short getPreferredVibrateCount(String notificationOrigin, Prefs prefs) { return (short) Math.min(Short.MAX_VALUE, getNotificationPrefIntValue(VIBRATION_COUNT, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_COUNT)); } - private int getPreferredVibrateDuration(String notificationOrigin, SharedPreferences prefs) { + private int getPreferredVibrateDuration(String notificationOrigin, Prefs prefs) { return getNotificationPrefIntValue(VIBRATION_DURATION, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_DURATION); } - private VibrationProfile getPreferredVibrateProfile(String notificationOrigin, SharedPreferences prefs, short repeat) { + private VibrationProfile getPreferredVibrateProfile(String notificationOrigin, Prefs prefs, short repeat) { String profileId = getNotificationPrefStringValue(VIBRATION_PROFILE, notificationOrigin, prefs, DEFAULT_VALUE_VIBRATION_PROFILE); return VibrationProfile.getProfile(profileId, repeat); } @@ -1032,7 +1034,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - int availableSlots = Integer.parseInt(prefs.getString(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, "0")); + int availableSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0); if (availableSlots > 0) { CalendarEvents upcomingEvents = new CalendarEvents(); 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 ea4dd069..51a5a120 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 @@ -32,6 +32,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAc import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; //import java.util.concurrent.Executors; //import java.util.concurrent.ScheduledExecutorService; @@ -365,7 +366,7 @@ public class FetchActivityOperation extends AbstractMiBandOperation { */ private void sendAckDataTransfer(Calendar time, int bytesTransferred) { byte[] ackTime = MiBandDateConverter.calendarToRawBytes(time); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(GBApplication.getContext()); + Prefs prefs = GBApplication.getPrefs(); byte[] ackChecksum = new byte[]{ (byte) (bytesTransferred & 0xff), 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 e3d22eaa..482e0d5a 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 @@ -364,7 +364,7 @@ public class PebbleIoThread extends GBDeviceIoThread { if (e.getMessage().contains("socket closed")) { //FIXME: this does not feel right LOG.info(e.getMessage()); mIsConnected = false; - int reconnectAttempts = Integer.valueOf(prefs.getString("pebble_reconnect_attempts", "10")); + int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); if (reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java index 053f034e..459d3c15 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java @@ -3,10 +3,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.receivers; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.media.AudioManager; import android.os.SystemClock; -import android.preference.PreferenceManager; import android.view.KeyEvent; import org.slf4j.Logger; From e35ce978bd2708916c4e7bc4ed16b8f131affad8 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Apr 2016 23:43:19 +0200 Subject: [PATCH 253/274] Remove now unused imports + fix one more SharedPreferences usage --- .../gadgetbridge/activities/ConfigureAlarms.java | 2 -- .../freeyourgadget/gadgetbridge/activities/ControlCenter.java | 2 -- .../gadgetbridge/devices/miband/MiBandCoordinator.java | 2 -- .../gadgetbridge/devices/miband/MiBandPairingActivity.java | 2 -- .../externalevents/BluetoothStateChangeReceiver.java | 2 -- .../gadgetbridge/externalevents/K9Receiver.java | 2 -- .../gadgetbridge/externalevents/NotificationListener.java | 2 -- .../gadgetbridge/externalevents/PebbleReceiver.java | 2 -- .../gadgetbridge/externalevents/PhoneCallReceiver.java | 2 -- .../gadgetbridge/externalevents/SMSReceiver.java | 2 -- .../gadgetbridge/externalevents/TimeChangeReceiver.java | 2 -- .../freeyourgadget/gadgetbridge/model/ActivityUser.java | 3 --- .../gadgetbridge/service/devices/miband/MiBandSupport.java | 4 +--- .../devices/miband/operations/FetchActivityOperation.java | 2 -- .../freeyourgadget/gadgetbridge/util/DeviceHelper.java | 2 -- 15 files changed, 1 insertion(+), 32 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index e14a914c..0a978d5d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -1,9 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.MenuItem; import android.widget.ListView; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 9b6ab4b4..7ddb5a88 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -10,11 +10,9 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; 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 9a504f8e..a669412e 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 @@ -2,9 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; import android.net.Uri; -import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 1a950c86..12d88683 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 @@ -7,11 +7,9 @@ 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.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.widget.TextView; import android.widget.Toast; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 2b21690c..73d26d0d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -4,8 +4,6 @@ import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java index 8487ead6..899ee0c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/K9Receiver.java @@ -3,11 +3,9 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.PowerManager; -import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 19d14bd7..9f93618b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -8,12 +8,10 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.PowerManager; -import android.preference.PreferenceManager; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.support.v4.app.NotificationCompat; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java index 42e94e4b..801c151b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java @@ -3,9 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.PowerManager; -import android.preference.PreferenceManager; import org.json.JSONArray; import org.json.JSONException; 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 054723fd..496872db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -3,8 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import nodomain.freeyourgadget.gadgetbridge.GBApplication; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java index d6db2027..0404cbc2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java @@ -3,10 +3,8 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.PowerManager; -import android.preference.PreferenceManager; import android.telephony.SmsMessage; import nodomain.freeyourgadget.gadgetbridge.GBApplication; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java index e2ea614c..0f58c21f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java @@ -3,8 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 9383a9ce..358c4a8e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -1,8 +1,5 @@ package nodomain.freeyourgadget.gadgetbridge.model; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - import java.util.Calendar; import nodomain.freeyourgadget.gadgetbridge.GBApplication; 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 ea3acc03..2d97818d 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 @@ -3,9 +3,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.widget.Toast; @@ -1033,7 +1031,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { TransactionBuilder builder = performInitialized("Send upcoming events"); BluetoothGattCharacteristic characteristic = getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + Prefs prefs = GBApplication.getPrefs(); int availableSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0); if (availableSlots > 0) { 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 51a5a120..183999f3 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 @@ -2,9 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; -import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; -import android.preference.PreferenceManager; import android.widget.Toast; import org.slf4j.Logger; 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 ce41e842..05a62976 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -3,8 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.util; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.widget.Toast; import java.util.ArrayList; From 47984dba0a4df7aaf7b42fb65e5cf6260d4bed34 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Mon, 25 Apr 2016 23:45:27 +0200 Subject: [PATCH 254/274] javadoc --- .../freeyourgadget/gadgetbridge/util/Prefs.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java index fcb49112..7c2fc2b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java @@ -19,10 +19,6 @@ public class Prefs { this.preferences = preferences; } - public SharedPreferences getPreferences() { - return preferences; - } - public String getString(String key, String defaultValue) { String value = preferences.getString(key, defaultValue); if (value == null || "".equals(value)) { @@ -110,4 +106,12 @@ public class Prefs { private void logReadError(String key, Exception ex) { LOG.error("Error reading preference value: " + key + "; returning default value", ex); // log the first exception } + + /** + * Access to the underlying SharedPreferences, typically only used for editing values. + * @return the underlying SharedPreferences object. + */ + public SharedPreferences getPreferences() { + return preferences; + } } From e1551226f6f4dd035775e655da56f48e29a36d94 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Mon, 25 Apr 2016 23:51:58 +0200 Subject: [PATCH 255/274] Reject empty strings in Preferences for numeric inputs --- .../activities/AbstractSettingsActivity.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index 2d62ce04..965a0cfd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities; import android.content.res.Configuration; import android.os.Bundle; +import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; @@ -12,6 +13,7 @@ import android.support.v4.app.NavUtils; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatDelegate; import android.support.v7.widget.Toolbar; +import android.text.InputType; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; @@ -41,6 +43,14 @@ public abstract class AbstractSettingsActivity extends PreferenceActivity { private static class SimpleSetSummaryOnChangeListener implements Preference.OnPreferenceChangeListener { @Override public boolean onPreferenceChange(Preference preference, Object value) { + if (preference instanceof EditTextPreference) { + if (((EditTextPreference) preference).getEditText().getKeyListener().getInputType() == InputType.TYPE_CLASS_NUMBER) { + if ("".equals(String.valueOf(value))) { + // reject empty numeric input + return false; + } + } + } updateSummary(preference, value); return true; } From eca5d40efe5070cb70cbe8f86b93a40226996664 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Tue, 26 Apr 2016 00:02:35 +0200 Subject: [PATCH 256/274] More javadoc --- .../gadgetbridge/util/Prefs.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java index 7c2fc2b1..2b4fcdb6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java @@ -35,6 +35,14 @@ public class Prefs { return value; } + /** + * Returns the preference saved under the given key as an integer value. + * Note that it is irrelevant whether the preference value was actually + * saved as an integer value or a string value. + * @param key the preference key + * @param defaultValue the default value to return if the preference value is unset + * @return the saved preference value or the given defaultValue + */ public int getInt(String key, int defaultValue) { try { return preferences.getInt(key, defaultValue); @@ -52,6 +60,14 @@ public class Prefs { } } + /** + * Returns the preference saved under the given key as a long value. + * Note that it is irrelevant whether the preference value was actually + * saved as a long value or a string value. + * @param key the preference key + * @param defaultValue the default value to return if the preference value is unset + * @return the saved preference value or the given defaultValue + */ public long getLong(String key, long defaultValue) { try { return preferences.getLong(key, defaultValue); @@ -69,6 +85,14 @@ public class Prefs { } } + /** + * Returns the preference saved under the given key as a float value. + * Note that it is irrelevant whether the preference value was actually + * saved as a float value or a string value. + * @param key the preference key + * @param defaultValue the default value to return if the preference value is unset + * @return the saved preference value or the given defaultValue + */ public float getFloat(String key, float defaultValue) { try { return preferences.getFloat(key, defaultValue); @@ -86,6 +110,14 @@ public class Prefs { } } + /** + * Returns the preference saved under the given key as a boolean value. + * Note that it is irrelevant whether the preference value was actually + * saved as a boolean value or a string value. + * @param key the preference key + * @param defaultValue the default value to return if the preference value is unset + * @return the saved preference value or the given defaultValue + */ public boolean getBoolean(String key, boolean defaultValue) { try { return preferences.getBoolean(key, defaultValue); From 5e02724c4cdbc10efa9a860f285df89c8da2d381 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Thu, 28 Apr 2016 23:17:13 +0200 Subject: [PATCH 257/274] Make automatic reconnect after connection loss configurable #293 Mi Band: automatically reconnect when the device is back in range Also: #89 --- .../gadgetbridge/GBApplication.java | 7 ++++++ .../service/AbstractDeviceSupport.java | 11 ++++++++++ .../service/DeviceCommunicationService.java | 22 ++++++++++++++++++- .../gadgetbridge/service/DeviceSupport.java | 14 ++++++++++++ .../service/ServiceDeviceSupport.java | 10 +++++++++ .../btle/AbstractBTLEDeviceSupport.java | 9 ++++++++ .../gadgetbridge/service/btle/BtLEQueue.java | 19 +++++++--------- .../devices/pebble/PebbleIoThread.java | 2 +- .../gadgetbridge/util/GBPrefs.java | 16 ++++++++++++++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/preferences.xml | 4 ++++ build.gradle | 2 +- 12 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 214349c0..9020d883 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -34,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -57,6 +58,7 @@ public class GBApplication extends Application { private static LimitedQueue mIDSenderLookup = new LimitedQueue(16); private static Appender fileLogger; private static Prefs prefs; + private static GBPrefs gbPrefs; public static final String ACTION_QUIT = "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit"; @@ -91,6 +93,7 @@ public class GBApplication extends Application { sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context); prefs = new Prefs(sharedPrefs); + gbPrefs = new GBPrefs(prefs); // don't do anything here before we set up logging, otherwise // slf4j may be implicitly initialized before we properly configured it. @@ -366,4 +369,8 @@ public class GBApplication extends Application { public static Prefs getPrefs() { return prefs; } + + public static GBPrefs getGBPrefs() { + return gbPrefs; + } } 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 683829c5..21d7965b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -59,6 +59,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { protected GBDevice gbDevice; private BluetoothAdapter btAdapter; private Context context; + private boolean autoReconnect; public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) { this.gbDevice = gbDevice; @@ -81,6 +82,16 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { return gbDevice.isInitialized(); } + @Override + public void setAutoReconnect(boolean enable) { + autoReconnect = enable; + } + + @Override + public boolean getAutoReconnect() { + return autoReconnect; + } + @Override public GBDevice getDevice() { return gbDevice; 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 b5b4b7fb..ee681cad 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -6,6 +6,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.IBinder; @@ -37,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_APP_CONFIGURE; @@ -88,7 +90,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_PERFORM_PAIR; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; -public class DeviceCommunicationService extends Service { +public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener { private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class); private boolean mStarted = false; @@ -130,6 +132,9 @@ public class DeviceCommunicationService extends Service { super.onCreate(); LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED)); mFactory = new DeviceSupportFactory(this); + + Prefs prefs = GBApplication.getPrefs(); + prefs.getPreferences().registerOnSharedPreferenceChangeListener(this); } @Override @@ -190,8 +195,10 @@ public class DeviceCommunicationService extends Service { btDeviceAddress = gbDevice.getAddress(); } + boolean autoReconnect = GBPrefs.AUTO_RECONNECT_DEFAULT; if (prefs != null) { prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply(); + autoReconnect = prefs.getPreferences().getBoolean(GBPrefs.AUTO_RECONNECT, GBPrefs.AUTO_RECONNECT_DEFAULT); } if (gbDevice != null && !isConnecting() && !isConnected()) { @@ -203,6 +210,7 @@ public class DeviceCommunicationService extends Service { if (pair) { deviceSupport.pair(); } else { + deviceSupport.setAutoReconnect(autoReconnect); deviceSupport.connect(); } } else { @@ -478,6 +486,8 @@ public class DeviceCommunicationService extends Service { @Override public void onDestroy() { + GBApplication.getPrefs().getPreferences().unregisterOnSharedPreferenceChangeListener(this); + LOG.debug("DeviceCommunicationService is being destroyed"); super.onDestroy(); @@ -514,4 +524,14 @@ public class DeviceCommunicationService extends Service { return name; } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (GBPrefs.AUTO_RECONNECT.equals(key)) { + boolean autoReconnect = GBApplication.getGBPrefs().getAutoReconnect(); + if (mDeviceSupport != null) { + mDeviceSupport.setAutoReconnect(autoReconnect); + } + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java index bce4e64b..61646c54 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java @@ -62,6 +62,20 @@ public interface DeviceSupport extends EventHandler { */ boolean useAutoConnect(); + /** + * Configures this instance to automatically attempt to reconnect after a connection loss. + * How, how long, or how often is up to the implementation. + * Note that tome implementations may not support automatic reconnection at all. + * @param enable + */ + void setAutoReconnect(boolean enable); + + /** + * Returns whether this instance to configured to automatically attempt to reconnect after a + * connection loss. + */ + boolean getAutoReconnect(); + /** * Attempts to pair and connect this device with the gadget device. Success * will be reported via a device change Intent. 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 2a0de20e..8047aad6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -56,6 +56,16 @@ public class ServiceDeviceSupport implements DeviceSupport { return delegate.connect(); } + @Override + public void setAutoReconnect(boolean enable) { + delegate.setAutoReconnect(enable); + } + + @Override + public boolean getAutoReconnect() { + return delegate.getAutoReconnect(); + } + @Override public void dispose() { delegate.dispose(); 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 6361431a..04179458 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 @@ -42,10 +42,19 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im public boolean connect() { if (mQueue == null) { mQueue = new BtLEQueue(getBluetoothAdapter(), getDevice(), this, getContext()); + mQueue.setAutoReconnect(getAutoReconnect()); } return mQueue.connect(); } + @Override + public void setAutoReconnect(boolean enable) { + super.setAutoReconnect(enable); + if (mQueue != null) { + mQueue.setAutoReconnect(enable); + } + } + /** * Subclasses should populate the given builder to initialize the device (if necessary). * 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 71d1beaf..f6009435 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 @@ -34,12 +34,6 @@ public final class BtLEQueue { private final GBDevice mGbDevice; private final BluetoothAdapter mBluetoothAdapter; private BluetoothGatt mBluetoothGatt; - /** - * When an automatic reconnect was attempted after a connection breakdown (error) - */ - private long lastReconnectTime = System.currentTimeMillis(); - - private static final long MIN_MILLIS_BEFORE_RECONNECT = 1000 * 60 * 5; // 5 minutes private final BlockingQueue mTransactions = new LinkedBlockingQueue<>(); private volatile boolean mDisposed; @@ -51,6 +45,7 @@ public final class BtLEQueue { private CountDownLatch mConnectionLatch; private BluetoothGattCharacteristic mWaitCharacteristic; private final InternalGattCallback internalGattCallback; + private boolean mAutoReconnect; private Thread dispatchThread = new Thread("GadgetBridge GATT Dispatcher") { @@ -130,6 +125,10 @@ public final class BtLEQueue { dispatchThread.start(); } + public void setAutoReconnect(boolean enable) { + mAutoReconnect = enable; + } + protected boolean isConnected() { return mGbDevice.isConnected(); } @@ -222,11 +221,9 @@ public final class BtLEQueue { * @return true if a reconnection attempt was made, or false otherwise */ private boolean maybeReconnect() { - long currentTime = System.currentTimeMillis(); - if (currentTime - lastReconnectTime >= MIN_MILLIS_BEFORE_RECONNECT) { - LOG.info("Automatic reconnection attempt..."); - lastReconnectTime = currentTime; - return connect(); + if (mAutoReconnect && mBluetoothGatt != null) { + LOG.info("Enabling automatic ble reconnect..."); + return mBluetoothGatt.connect(); } return false; } 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 482e0d5a..d2265027 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 @@ -365,7 +365,7 @@ public class PebbleIoThread extends GBDeviceIoThread { LOG.info(e.getMessage()); mIsConnected = false; int reconnectAttempts = prefs.getInt("pebble_reconnect_attempts", 10); - if (reconnectAttempts > 0) { + if (GBApplication.getGBPrefs().getAutoReconnect() && reconnectAttempts > 0) { gbDevice.setState(GBDevice.State.CONNECTING); gbDevice.sendDeviceUpdateIntent(getContext()); int delaySeconds = 1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java new file mode 100644 index 00000000..e895919d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java @@ -0,0 +1,16 @@ +package nodomain.freeyourgadget.gadgetbridge.util; + +public class GBPrefs { + + public static final String AUTO_RECONNECT = "general_autocreconnect"; + public static boolean AUTO_RECONNECT_DEFAULT = true; + private final Prefs mPrefs; + + public GBPrefs(Prefs prefs) { + mPrefs = prefs; + } + + public boolean getAutoReconnect() { + return mPrefs.getBoolean(AUTO_RECONNECT, AUTO_RECONNECT_DEFAULT); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1df50a7a..1ec538ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -244,5 +244,6 @@ Firmware not sent Heart Rate Heart Rate + Reconnect automatically diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index dad3bc0b..4def8d01 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -7,6 +7,10 @@ android:defaultValue="false" android:key="general_autoconnectonbluetooth" android:title="@string/pref_title_general_autoconnectonbluetooth" /> + Date: Fri, 29 Apr 2016 21:49:17 +0200 Subject: [PATCH 258/274] Fix testcases (all this should be scrapped and redone with e.g. robolectric) --- .../service/DeviceCommunicationService.java | 29 ++++++++++++++----- .../service/AbstractServiceTestCase.java | 14 ++++++--- .../DeviceCommunicationServiceTestCase.java | 11 +++++++ .../gadgetbridge/test/GBMockApplication.java | 15 ++++++++++ .../gadgetbridge/test/MockHelper.java | 9 ++++++ 5 files changed, 67 insertions(+), 11 deletions(-) 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 ee681cad..7738f40c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -133,8 +133,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED)); mFactory = new DeviceSupportFactory(this); - Prefs prefs = GBApplication.getPrefs(); - prefs.getPreferences().registerOnSharedPreferenceChangeListener(this); + if (hasPrefs()) { + getPrefs().getPreferences().registerOnSharedPreferenceChangeListener(this); + } } @Override @@ -174,7 +175,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere // when we get past this, we should have valid mDeviceSupport and mGBDevice instances - Prefs prefs = GBApplication.getPrefs(); + Prefs prefs = getPrefs(); switch (action) { case ACTION_START: start(); @@ -196,9 +197,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere } boolean autoReconnect = GBPrefs.AUTO_RECONNECT_DEFAULT; - if (prefs != null) { + if (prefs != null && prefs.getPreferences() != null) { prefs.getPreferences().edit().putString("last_device_address", btDeviceAddress).apply(); - autoReconnect = prefs.getPreferences().getBoolean(GBPrefs.AUTO_RECONNECT, GBPrefs.AUTO_RECONNECT_DEFAULT); + autoReconnect = getGBPrefs().getAutoReconnect(); } if (gbDevice != null && !isConnecting() && !isConnected()) { @@ -486,7 +487,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere @Override public void onDestroy() { - GBApplication.getPrefs().getPreferences().unregisterOnSharedPreferenceChangeListener(this); + if (hasPrefs()) { + getPrefs().getPreferences().unregisterOnSharedPreferenceChangeListener(this); + } LOG.debug("DeviceCommunicationService is being destroyed"); super.onDestroy(); @@ -528,10 +531,22 @@ public class DeviceCommunicationService extends Service implements SharedPrefere @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (GBPrefs.AUTO_RECONNECT.equals(key)) { - boolean autoReconnect = GBApplication.getGBPrefs().getAutoReconnect(); + boolean autoReconnect = getGBPrefs().getAutoReconnect(); if (mDeviceSupport != null) { mDeviceSupport.setAutoReconnect(autoReconnect); } } } + + protected boolean hasPrefs() { + return getPrefs().getPreferences() != null; + } + + public Prefs getPrefs() { + return GBApplication.getPrefs(); + } + + public GBPrefs getGBPrefs() { + return GBApplication.getGBPrefs(); + } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java index fe9e03e3..b3b426bf 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractServiceTestCase.java @@ -5,7 +5,9 @@ import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.preference.PreferenceManager; import junit.framework.Assert; @@ -22,7 +24,7 @@ public abstract class AbstractServiceTestCase { private final Class mServiceClass; private T mServiceInstance; private Context mContext; - private Application mApplication; + private GBMockApplication mApplication; private boolean wasStarted; private PackageManager mPackageManager; private NotificationManager mNotificationManager; @@ -41,6 +43,10 @@ public abstract class AbstractServiceTestCase { return mServiceInstance; } + protected MockHelper getmMockHelper() { + return mMockHelper; + } + @Before public void setUp() throws Exception { mMockHelper = new MockHelper(); @@ -69,7 +75,7 @@ public abstract class AbstractServiceTestCase { mServiceInstance = null; } - protected Application createApplication(PackageManager packageManager) { + protected GBMockApplication createApplication(PackageManager packageManager) { return new GBMockApplication(packageManager); } @@ -85,9 +91,9 @@ public abstract class AbstractServiceTestCase { return new GBMockContext(application); } - private T createService(Class serviceClass, Application application, NotificationManager notificationManager) throws Exception { + protected T createService(Class serviceClass, GBMockApplication application, NotificationManager notificationManager) throws Exception { T service = mMockHelper.createService(serviceClass, application); - mMockHelper.addSystemServiceTo(service, Context.NOTIFICATION_SERVICE, getNotificationService()); + mMockHelper.addSystemServiceTo(service, Context.NOTIFICATION_SERVICE, notificationManager); return service; } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java index e4d1fe5b..8fa5ea5f 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationServiceTestCase.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service; +import android.app.Application; +import android.app.NotificationManager; import android.content.Context; import org.junit.Before; @@ -11,6 +13,7 @@ import org.mockito.Mockito; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.test.GBMockApplication; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -41,6 +44,14 @@ public class DeviceCommunicationServiceTestCase extends AbstractServiceTestCase< super(DeviceCommunicationService.class); } + @Override + protected DeviceCommunicationService createService(Class serviceClass, GBMockApplication application, NotificationManager notificationManager) throws Exception { + DeviceCommunicationService service = getmMockHelper().createDeviceCommunicationService(serviceClass, application); + getmMockHelper().addSystemServiceTo(service, Context.NOTIFICATION_SERVICE, notificationManager); + return service; + } + + @Before public void setUp() throws Exception { super.setUp(); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java index 805716fa..2fb2ffbf 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/GBMockApplication.java @@ -1,18 +1,27 @@ package nodomain.freeyourgadget.gadgetbridge.test; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.preference.PreferenceManager; import android.test.mock.MockApplication; import nodomain.freeyourgadget.gadgetbridge.GBEnvironment; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class GBMockApplication extends MockApplication { + private static final String PREF_NAME = "testprefs"; private final PackageManager mPackageManager; + private Prefs prefs; + private GBPrefs gbPrefs; public GBMockApplication(PackageManager packageManager) { GB.environment = GBEnvironment.createDeviceEnvironment().createLocalTestEnvironment(); mPackageManager = packageManager; + prefs = new Prefs(PreferenceManager.getDefaultSharedPreferences(this)); + gbPrefs = new GBPrefs(prefs); } @Override @@ -25,4 +34,10 @@ public class GBMockApplication extends MockApplication { return mPackageManager; } + public Prefs getPrefs() { + return prefs; + } + public GBPrefs getGBPrefs() { + return gbPrefs; + } } diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java index 9461cab1..00947b98 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/test/MockHelper.java @@ -11,6 +11,8 @@ import org.mockito.Mockito; import java.lang.reflect.Constructor; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; + public class MockHelper { public NotificationManager createNotificationManager(Context mContext) throws Exception { Constructor[] constructors = NotificationManager.class.getDeclaredConstructors(); @@ -29,6 +31,13 @@ public class MockHelper { return mockedService; } + public T createDeviceCommunicationService(Class serviceClass, GBMockApplication application) throws Exception { + T mockedService = createService(serviceClass, application); + Mockito.when(mockedService.getPrefs()).thenReturn(application.getPrefs()); + Mockito.when(mockedService.getGBPrefs()).thenReturn(application.getGBPrefs()); + return mockedService; + } + public void addSystemServiceTo(Context context, String serviceName, Object service) { Mockito.when(context.getSystemService(serviceName)).thenReturn(service); } From 6863fababebe088dd47cb84eac0b7d3074d15978 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Fri, 29 Apr 2016 22:07:16 +0200 Subject: [PATCH 259/274] Update changelog and prepare for 0.9.6 --- CHANGELOG.md | 6 ++++++ app/build.gradle | 4 ++-- app/src/main/res/xml/changelog_master.xml | 23 +++++++++++++++-------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f375da1e..285abea9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ ###Changelog +####Version (0.9.6) +* Again some UI/theme improvements +* New preference to reconnect after connection loss (defaults to true) +* Fix crash when dealing with certain old preference values +* Mi Band: automatically reconnect when back in range after connection loss +* Mi Band 1S: display heart rate value again when invoked via the Debug view ####Version (0.9.5) * Several UI Improvements diff --git a/app/build.gradle b/app/build.gradle index c2578c08..d567c3f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.5" - versionCode 49 + versionName "0.9.6" + versionCode 50 } buildTypes { release { diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index d7054da2..ab86fe0d 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,14 +1,21 @@ + + Again some UI/theme improvements + New preference to reconnect after connection loss (defaults to true) + Fix crash when dealing with certain old preference values + Mi Band: automatically reconnect when back in range after connection loss + Mi Band 1S: display heart rate value again when invoked via the Debug view + - Several UI Improvements - Easier First-time setup by using a FAB - Optional Dark Theme - Notification App Blacklist is now sorted - Gadgetbridge Icon in the notification bar displays connection state - Logging is now configurable without restart - Mi Band 1S: Initial live heartrate tracking - Fix certain crash in charts activity on slower devices (#277) + Several UI Improvements + Easier First-time setup by using a FAB + Optional Dark Theme + Notification App Blacklist is now sorted + Gadgetbridge Icon in the notification bar displays connection state + Logging is now configurable without restart + Mi Band 1S: Initial live heartrate tracking + Fix certain crash in charts activity on slower devices (#277) Fix crash in charts activities when changing the date, quickly (#277) From 827e10f49e6b80dce6faa9f81c4a69b15fa6691e Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 1 May 2016 22:03:40 +0200 Subject: [PATCH 260/274] Updated Mi Band features --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b790dedf..9367cd44 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ * Generic Android notifications * Synchronize the time to the Mi Band * Display firmware version and battery state +* Firmware Update +* Heartrate Measurement (alpha) * Synchronize activity data * Display sleep data (alpha) * Display sports data (step count) (alpha) From 619a17425f59b99543f3ab92e4333eeff87e4ce6 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 4 May 2016 12:31:29 +0200 Subject: [PATCH 261/274] Mi Band: Display hint about starting Activity Activity instead of App Manager TODO: Fix the string, I have no idea how to properly name the Activity Activity --- .../gadgetbridge/activities/ControlCenter.java | 3 ++- .../gadgetbridge/devices/DeviceCoordinator.java | 2 ++ .../gadgetbridge/devices/UnknownDeviceCoordinator.java | 5 +++++ .../gadgetbridge/devices/miband/MiBandCoordinator.java | 6 ++++++ .../gadgetbridge/devices/pebble/PebbleCoordinator.java | 6 ++++++ app/src/main/res/values/strings.xml | 1 + 6 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java index 7ddb5a88..702aa134 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenter.java @@ -390,7 +390,8 @@ public class ControlCenter extends GBActivity { } if (connected) { - hintTextView.setText(R.string.tap_connected_device_for_app_mananger); + DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(selectedDevice); + hintTextView.setText(coordinator.getTapString()); } else if (!deviceList.isEmpty()) { hintTextView.setText(R.string.tap_a_device_to_connect); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index e1bfe822..7ecc3e8d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -107,4 +107,6 @@ public interface DeviceCoordinator { * @return */ boolean supportsAlarmConfiguration(); + + int getTapString(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index 7d9b88d8..1af8da27 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -88,4 +88,9 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator { public boolean supportsAlarmConfiguration() { return false; } + + @Override + public int getTapString() { + return 0; + } } 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 a669412e..c4e8e83c 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 @@ -8,6 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; @@ -78,6 +79,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator { return true; } + @Override + public int getTapString() { + return R.string.tap_connected_device_for_activity; + } + public static boolean hasValidUserInfo() { String dummyMacAddress = MiBandService.MAC_ADDRESS_FILTER_1_1A + ":00:00:00"; try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index d76e7126..c74db054 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -5,6 +5,7 @@ import android.content.Context; import android.net.Uri; import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.AppManagerActivity; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; @@ -80,4 +81,9 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator { public boolean supportsAlarmConfiguration() { return false; } + + @Override + public int getTapString() { + return R.string.tap_connected_device_for_app_mananger; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ec538ec..a3392096 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -87,6 +87,7 @@ Bluetooth is not supported. Bluetooth is disabled. Tap connected device for App Manager + Tap connected device for Activiy Tap a device to connect Cannot connect. BT address invalid? Gadgetbridge running From 045d5119ff356bec3138307af3d1d1902deac24e Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 4 May 2016 13:07:11 +0200 Subject: [PATCH 262/274] Do not update summary for checkbox preference Was causing summary to get overwritten by "true" or "false" when changing preferences --- .../gadgetbridge/devices/miband/MiBandPreferencesActivity.java | 1 - 1 file changed, 1 deletion(-) 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 919e7d2c..e49ae1ab 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 @@ -61,7 +61,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { PREF_USER_ALIAS, PREF_MIBAND_ADDRESS, PREF_MIBAND_FITNESS_GOAL, - PREF_MIBAND_DONT_ACK_TRANSFER, PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_SMS), getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL), From 65a95366f4e9b49ac85137456ebc660bcb2121ae Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Wed, 4 May 2016 13:24:32 +0200 Subject: [PATCH 263/274] Mi Band: Allow setting low-latency FW update mode in Mi Band development settings --- .../devices/miband/operations/UpdateFirmwareOperation.java | 7 ++++++- app/src/main/res/values/strings.xml | 5 ++++- app/src/main/res/xml/miband_preferences.xml | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) 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 4bb7d6ba..5b733b3f 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 @@ -13,6 +13,7 @@ import java.io.IOException; import java.util.Arrays; 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.MiBandFWHelper; @@ -25,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractMiFir import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.util.CheckSums; import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class UpdateFirmwareOperation extends AbstractMiBandOperation { private static final Logger LOG = LoggerFactory.getLogger(UpdateFirmwareOperation.class); @@ -32,6 +34,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { private final Uri uri; private boolean firmwareInfoSent = false; private UpdateCoordinator updateCoordinator; + final Prefs prefs = GBApplication.getPrefs(); public UpdateFirmwareOperation(Uri uri, MiBandSupport support) { super(support); @@ -285,7 +288,9 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); -// getSupport().setLowLatency(builder); + if (prefs.getBoolean("mi_low_latency_fw_update", false)) { + getSupport().setLowLatency(builder); + } for (int i = 0; i < packets; i++) { byte[] fwChunk = Arrays.copyOfRange(fwbytes, i * packetLength, i * packetLength + packetLength); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a3392096..133216e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,9 +199,12 @@ Steps Live Activity Steps today, target: %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. - Do not ack activity data transfer + Use low-latency mode for FW updates + This might help on devices where firmware updates fail + Steps History Current steps/min Total Steps diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index bccb1348..041fc628 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -161,5 +161,10 @@ 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 From 5b2189528360f1ae95f0af53d0560b4c540f8d2f Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 7 May 2016 21:46:20 +0200 Subject: [PATCH 264/274] try to get BT alias name by reflection. Useful if you have a lot of Mi Bands --- .../gadgetbridge/util/DeviceHelper.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) 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 05a62976..ee274dc7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -5,6 +5,11 @@ import android.bluetooth.BluetoothDevice; import android.content.Context; import android.widget.Toast; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -22,6 +27,9 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class DeviceHelper { + + private static final Logger LOG = LoggerFactory.getLogger(DeviceHelper.class); + private static final DeviceHelper instance = new DeviceHelper(); public static DeviceHelper getInstance() { @@ -95,12 +103,23 @@ public class DeviceHelper { public GBDevice toSupportedDevice(BluetoothDevice device) { GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN); + + String deviceName = device.getName(); + try { + Method method = device.getClass().getMethod("getAliasName"); + if (method != null) { + deviceName = (String) method.invoke(device); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) { + LOG.info("Could not get device alias for " + deviceName); + } + if (coordinator != null && coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), device.getName(), coordinator.getDeviceType()); + return new GBDevice(device.getAddress(), deviceName, coordinator.getDeviceType()); } for (DeviceCoordinator coordinator : getAllCoordinators()) { if (coordinator.supports(candidate)) { - return new GBDevice(device.getAddress(), device.getName(), coordinator.getDeviceType()); + return new GBDevice(device.getAddress(), deviceName, coordinator.getDeviceType()); } } return null; From 9b7f2c1e915f47e3b569ccb6a88a9d673e835688 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 8 May 2016 17:19:01 +0200 Subject: [PATCH 265/274] try to fix weiredness with pebble reconnects --- .../service/devices/pebble/PebbleIoThread.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 d2265027..43b86e2f 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 @@ -396,6 +396,14 @@ public class PebbleIoThread extends GBDeviceIoThread { } catch (IOException ex) { ex.printStackTrace(); LOG.info("error while reconnecting"); + } finally { + try { + if (mBtServerSocket != null) { + mBtServerSocket.close(); + mBtServerSocket = null; + } + } catch (IOException ignore) { + } } } if (!mIsConnected) { From 1a353239c45c91e04db7cbe39f6f86429618910a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 8 May 2016 21:38:55 +0200 Subject: [PATCH 266/274] Fix log content --- .../nodomain/freeyourgadget/gadgetbridge/GBApplication.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 9020d883..e17c4584 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -179,7 +179,7 @@ public class GBApplication extends Application { root.addAppender(fileLogger); } } catch (Throwable ex) { - Log.e("GBApplication", "Error removing logger FILE appender", ex); + Log.e("GBApplication", "Error adding logger FILE appender", ex); } } From 40837996f86010dea9f472e733ae1a3b1420335a Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 8 May 2016 21:39:23 +0200 Subject: [PATCH 267/274] Fix logback initialization, closes #300 --- .../nodomain/freeyourgadget/gadgetbridge/util/Prefs.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java index 2b4fcdb6..03aca1aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java @@ -1,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.util; import android.content.SharedPreferences; +import android.util.Log; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,7 +12,9 @@ import java.util.Set; * Wraps SharedPreferences to avoid ClassCastExceptions and others. */ public class Prefs { - private static final Logger LOG = LoggerFactory.getLogger(Prefs.class); + private static final String TAG = "Prefs"; + // DO NOT use slf4j logger here, this would break its configuration via GBApplication +// private static final Logger LOG = LoggerFactory.getLogger(Prefs.class); private final SharedPreferences preferences; @@ -136,7 +139,7 @@ public class Prefs { } private void logReadError(String key, Exception ex) { - LOG.error("Error reading preference value: " + key + "; returning default value", ex); // log the first exception + Log.e(TAG, "Error reading preference value: " + key + "; returning default value", ex); // log the first exception } /** From b805612ae5ba579e1a93d08ed65c8c557d585da3 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 8 May 2016 21:39:55 +0200 Subject: [PATCH 268/274] Allow in-process dex with 2GB heap, as recommended --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 1d3591c8..8c144972 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,7 @@ # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit From e2def7b467e0cf02ec1c6ad7f9ce836debae0ff0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 14 May 2016 12:15:48 +0200 Subject: [PATCH 269/274] update changelog, bump version --- CHANGELOG.md | 11 ++++++++--- app/build.gradle | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 285abea9..1ceacbab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,17 @@ ###Changelog -####Version (0.9.6) +####Version 0.9.7 +* Pebble: hopefully fix some reconnect issues +* Show aliases for BT Devices if they had been renamed in BT Settings +* Do not show a hint about App Manager when a Mi Band is connected + +####Version 0.9.6 * Again some UI/theme improvements * New preference to reconnect after connection loss (defaults to true) * Fix crash when dealing with certain old preference values * Mi Band: automatically reconnect when back in range after connection loss * Mi Band 1S: display heart rate value again when invoked via the Debug view -####Version (0.9.5) +####Version 0.9.5 * Several UI Improvements * Easier First-time setup by using a FAB * Optional Dark Theme @@ -16,7 +21,7 @@ * Mi Band 1S: Initial live heartrate tracking * Fix certain crash in charts activity on slower devices (#277) -####Version (0.9.4) +####Version 0.9.4 * Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!) * Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly * Pebble: delay between reconnection attempts (from 1 up to 64 seconds) diff --git a/app/build.gradle b/app/build.gradle index d567c3f3..fffe7f3c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { targetSdkVersion 23 // note: always bump BOTH versionCode and versionName! - versionName "0.9.6" - versionCode 50 + versionName "0.9.7" + versionCode 51 } buildTypes { release { From 5efe9a5eb86b7e8b51fba995461de3fea17fe2e4 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sat, 14 May 2016 12:23:01 +0200 Subject: [PATCH 270/274] update japanese and geraman translations form transifex (THANKS!) I did not merge others because tx pull deletes stuff again --- app/src/main/res/values-de/strings.xml | 18 +++++++++++++++++- app/src/main/res/values-ja/strings.xml | 6 +++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7dfeb3f7..121df48e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -32,6 +32,9 @@ Datum und Zeit Uhrzeit synchronisieren Synchronisiere die Urzeit mit dem Gerät (bei Verbindingsaufbau und wenn die Zeit oder Zeitzone auf dem Android Gerät eingestellt wird) + Thema + Hell + Dunkel Benachrichtigungen Wiederholungen Anrufe @@ -71,6 +74,7 @@ Bluetooth wird nicht unterstützt. Bluetooth ist abgeschaltet. berühre das verbundene Gerät, um den App Manager zu starten + Tippe auf ein verbundenes Gerät, um die Aktivitäten anzuzeigen berühre ein Gerät zum Verbinden Verbindung kann nicht aufgebaut werden. BT Adresse ungültig? Gadgetbridge läuft @@ -179,9 +183,11 @@ Schritte Live Aktivität Schritte heute, Ziel: %1$s + Transfer von Aktivitätsdaten nicht bestätigen Wenn der Transfer der Aktivitätsdaten nicht bestätigt wird, werden die Daten nicht auf dem Mi Band gelöscht. Das ist Sinnvoll, wenn neben Gadgetbridge noch andere Apps auf das Mi Band zugreifen. Aktivitätsdaten verbleiben auf dem Mi Band, auch nach der Synchronisierung. Hilfreich wenn das Mi Band mit weiteren Apps verwendet wird. - Transfer von Aktivitätsdaten nicht bestätigen + Benutze Modus mit niedriger Latenz für FW-Updates + Dies kann bei Geräten helfen, bei denen Firmwareupdates fehlschlagen Verlauf Schritte Akt. Schritte pro Minute Schritte insgesamt @@ -196,6 +202,7 @@ Aktivitätsdaten auf dem Gerät lassen Inkompatible Firmware Diese Firmware ist nicht mit dem Gerät kompatibel + Verwende den Herzfrequenzsensor um die Schlaferkennung zu verbessern warte auf eingehende Verbindung Erneut installieren Über Dich @@ -205,10 +212,19 @@ Gewicht in kg aktivieren deaktivieren + Authentifiziere + Authentifizierung erforderlich Zzz Widget hinzufügen Gewünschte Schlafdauer in Stunden Ein Wecker wurde auf %1$02d:%2$02d gestellt HW: %1$s FW: %1$s + Fehler beim Erstellen des Verzeichnisses für Logdateien: %1$s + HF: + Firmwareupdate wird durchgeführt + Firmware wurde nicht gesendet + Herzfrequenz + Herzfrequenz + Verbindungen automatisch wiederherstellen diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 5018b7d2..864ce541 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -74,6 +74,7 @@ Bluetoothはサポートされていません。 Bluetoothは無効です。 アプリマネージャーの接続されたデバイスをタップ + アクティビティの接続されたデバイスをタップ 接続するデバイスをタップ 接続できません。 BTアドレスが無効? ガジェットブリッジは実行中 @@ -182,9 +183,11 @@ 歩数 生活活動 今日の歩数、目標: %1$s + 活動データ転送に応答しない 活動データは、バンドが応答しない場合は、クリアされません。 GBを他のアプリと一緒に使用する場合に便利です。 同期後もMi Bandに活動データを保持します。 GBを他のアプリと一緒に使用する場合に便利です。 - 活動データ転送に応答しない + FW の更新に低遅延モードを使用する + これはファームウェアの更新が失敗するデバイスで役立つことがあります 歩数履歴 現在の歩数/分 合計歩数 @@ -225,4 +228,5 @@ ファームウェアを送信しませんでした 心拍数 心拍数 + 自動的に再接続 From 59638432163923231c6a76ae8478643c4f03b8c1 Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 15 May 2016 00:15:31 +0200 Subject: [PATCH 271/274] Experimental support for #274 (untested yet) --- .../gadgetbridge/devices/miband/MiBandConst.java | 1 + .../gadgetbridge/devices/miband/UserInfo.java | 2 +- .../gadgetbridge/service/devices/miband/DeviceInfo.java | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) 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 6853b371..8379d75b 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 @@ -26,6 +26,7 @@ public final class MiBandConst { public static final String MI_1 = "1"; public static final String MI_1A = "1A"; public static final String MI_1S = "1S"; + public static final String MI_AMAZFIT = "Amazfit"; public static int getNotificationPrefIntValue(String pref, String origin, Prefs prefs, int defaultValue) { String key = getNotificationPrefKey(pref, origin); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java index 5bb901b3..4bc728c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java @@ -85,7 +85,7 @@ public class UserInfo { sequence[8] = (byte) (type & 0xff); int aliasFrom = 9; - if (mDeviceInfo.isMili1A() || mDeviceInfo.isMili1S()) { + if (!mDeviceInfo.isMili1()) { sequence[9] = (byte) (mDeviceInfo.feature & 255); sequence[10] = (byte) (mDeviceInfo.appearance & 255); aliasFrom = 11; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 1072ff50..3ba149df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -112,6 +112,10 @@ public class DeviceInfo extends AbstractInfo { return (feature == 4 && appearance == 0) || hwVersion == 4; } + public boolean isAmazFit() { + return hwVersion == 6; + } + public String getHwVersion() { if (isMili1()) { return MiBandConst.MI_1; @@ -122,6 +126,9 @@ public class DeviceInfo extends AbstractInfo { if (isMili1S()) { return MiBandConst.MI_1S; } + if (isAmazFit()) { + return MiBandConst.MI_AMAZFIT; + } return "?"; } } From 3a1f91b5a8860053a6229952ce8a80ba3bf5350b Mon Sep 17 00:00:00 2001 From: cpfeiffer Date: Sun, 15 May 2016 00:21:55 +0200 Subject: [PATCH 272/274] Default to low latency mode #287 Tested with Mi1A and Mi1S -- works fine and faster than without low-latency mode. --- .../devices/miband/operations/UpdateFirmwareOperation.java | 2 +- app/src/main/res/xml/miband_preferences.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 5b733b3f..32185d83 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 @@ -288,7 +288,7 @@ public class UpdateFirmwareOperation extends AbstractMiBandOperation { int firmwareProgress = 0; TransactionBuilder builder = performInitialized("send firmware packet"); - if (prefs.getBoolean("mi_low_latency_fw_update", false)) { + if (prefs.getBoolean("mi_low_latency_fw_update", true)) { getSupport().setLowLatency(builder); } for (int i = 0; i < packets; i++) { diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 041fc628..37e5d777 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -165,6 +165,6 @@ android:key="mi_low_latency_fw_update" android:title="@string/pref_title_low_latency_fw_update" android:summary="@string/pref_summary_low_latency_fw_update" - android:defaultValue="false" /> + android:defaultValue="true" /> \ No newline at end of file From d66f842e9b0e367ec20372a86705e735052acdd0 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 15 May 2016 22:24:37 +0200 Subject: [PATCH 273/274] Mi Band: Make sure live activity gets stopped when using the back button --- .../gadgetbridge/activities/charts/LiveActivityFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index 7b201372..19a62a4b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -351,6 +351,7 @@ public class LiveActivityFragment extends AbstractChartFragment { @Override public void onDestroyView() { + onMadeInvisibleInActivity(); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver); super.onDestroyView(); } From 8c88223f267a8ca7f7c7bc0ebe51f19047a7c095 Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Sun, 15 May 2016 23:29:19 +0200 Subject: [PATCH 274/274] update changelog --- CHANGELOG.md | 3 +++ app/src/main/res/xml/changelog_master.xml | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ceacbab..bf801f26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ###Changelog ####Version 0.9.7 * Pebble: hopefully fix some reconnect issues +* Mi Band: fix live activity monitoring running forever if back button pressed +* Mi Band: allow low latency firmware updates, fixes update with some phones +* Mi Band: inital experimental and probably broken support for Amazfit * Show aliases for BT Devices if they had been renamed in BT Settings * Do not show a hint about App Manager when a Mi Band is connected diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index ab86fe0d..425e5c4a 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,13 @@ + + Pebble: hopefully fix some reconnect issues + Mi Band: fix live activity monitoring running forever if back button pressed + Mi Band: allow low latency firmware updates, fixes update with some phones + Mi Band: inital experimental and probably broken support for Amazfit + Show aliases for BT Devices if they had been renamed in BT Settings + Do not show a hint about App Manager when a Mi Band is connected + Again some UI/theme improvements New preference to reconnect after connection loss (defaults to true)