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 58667332b..612ff073b 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 9e56849d0..88e12c030 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 cbbcfaf6e..e33177132 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 1949b1ac7..27b7132c2 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 5150f0825..7ab5d03f1 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 62a105269..6f469e344 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 5b57bb312..8426034e8 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 ec3a8c8a2..9fe446504 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 88e12c030..de149c544 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 aff24692a..79f253d84 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 653dae863..dda3ea212 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 e33177132..258105d3a 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 e909702fb..9d21e2636 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 3eed8f854..6efeeb001 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 98e0a2f82..3e58a5bf7 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 3af9e0f21..fe50d4f5f 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 9a6a7738a..56b39a6e7 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 78915517d..0304d36c6 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 4b73c989b..65997e955 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 05c1ac7a9..9807b9cd1 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 7ab5d03f1..2c37c9141 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 200616743..6f3aa77f3 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 7b3af1b8f..868cda131 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 6f469e344..3cb8343fc 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 48439da7d..9aa098bc8 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 000000000..46e14ea65 --- /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 612ff073b..8276fca86 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 c18572139..cc31c187c 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 8276fca86..58476f9bf 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 000000000..30ac869c3 --- /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 49165df49..69b164129 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 58476f9bf..444313d91 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 69b164129..ab7e2306b 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 7213e752a..ceee46b45 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 ea5621ac1..f1b8c31af 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 ceee46b45..5a507875f 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 172f5285c..d87f7241a 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 60f1653dd..a139eb1a7 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 0304d36c6..81a7d2db2 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 5a507875f..6a73973d8 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 444313d91..6bd4bf3b1 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 6a73973d8..4e5655724 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 4e5655724..c5970c9e2 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 5ce64b901..ad99cfea9 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 000000000..5da0e230c --- /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 80450f722..917e19e5c 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 000000000..f4f251a59 --- /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 000000000..58e371298 --- /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 6bd4bf3b1..17bcf5498 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 ab7e2306b..3cda31442 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 17bcf5498..b888e75e3 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 3cda31442..f2b314886 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 b888e75e3..ceee63cc5 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 42bc1566a..eb8ef7aa2 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 22d9469af..a73f784f7 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 ceee63cc5..1ab21c4c6 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 f4f251a59..000000000 --- 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 30ac869c3..b17e7259e 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 58e371298..1def3cdb3 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 1ab21c4c6..41cfe311e 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 1def3cdb3..97bf73754 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 b17e7259e..00f67721e 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 97bf73754..12d036a8d 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 41cfe311e..bb8988568 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 d0263b572..32bbc8280 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 54e33829d..52c4ba10c 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 5da0e230c..c557bbadf 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 12d036a8d..000000000 --- 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 000000000..553510249 --- /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 000000000..6ce8bd09c --- /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 bb8988568..5b6ee85d1 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 305a4abe0..07bdfe1ac 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 c5970c9e2..bf9cd1359 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 32bbc8280..1d8b54b49 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 52c4ba10c..6e7398e99 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 553510249..a2ec192a4 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 bf9cd1359..f07328eb1 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 c33a1909e..9a2b9fac3 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 382230042..9e832494e 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 b5f3125df..8104cb466 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 000000000..6f25b1634 --- /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 a78b37f86..43d6c75db 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 81a7d2db2..cb75de4d8 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 f07328eb1..8725069d1 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 8725069d1..465db271b 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 2c37c9141..acf843044 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 917e19e5c..a76534d13 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 cc31c187c..1f5cfa293 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 cb75de4d8..149516920 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 aea487610..f547b01e8 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 8104cb466..4af53e708 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 f547b01e8..53a526eec 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 465db271b..e05f4495d 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 c17829176..26513a067 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 e05f4495d..dfadb833c 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 9807b9cd1..ee33a7dee 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 46e14ea65..f2c87c499 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 53a526eec..cb2a8a6fb 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 db241ed99..2615f2827 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 dfadb833c..5b54465e2 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 187372dd0..11ac53096 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 f1b8c31af..d2c037517 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 ee33a7dee..af43c2969 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 b50f71936..10a7f296f 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 5b54465e2..c4fdea08d 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 c4fdea08d..62225e983 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 7571badc7..c6f87cff6 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 21bc39285..9019013b4 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 9019013b4..6cefcef6e 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 1cb7416d6..f75ab4dc4 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 99a2d28e4..6d3324234 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 f7ac4e159..ef5078760 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 62225e983..9fb5827de 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 f2b314886..e3ffaec42 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 61afd2792..03993b021 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 b60927b46..b729123e5 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 f2c2f37f1..17411ff6b 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 1d8b54b49..2df3759d6 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 696f30d72..5d1a72b4d 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 6e7398e99..7dfeb5c89 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 f23033670..c805e352b 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 000000000..e23d13e50 --- /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 f75ab4dc4..6ade2e638 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 6d3324234..b515c5efb 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 6cefcef6e..84f9c7cc8 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 667763d67..10655018f 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 07bdfe1ac..4507fbc43 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 5b4e0b409..0761e1b6f 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 a4fbe772c..c5d051831 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 6d7bf8533..1136f0840 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 349c0f481..7cf008bfa 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 cee7a5e2e..54cc6c239 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 af4a6d1b8..bd4f33e9d 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 d85c12909..15e6f1c6c 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 b75b1c596..8faf48a9c 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 43d6c75db..2e839967a 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 590bbb1df..d045789c9 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 799b7ab5b..9bc0997ae 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 000000000..a3fe49982 --- /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 000000000..af68a9653 --- /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 d87f7241a..0ff6333aa 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 000000000..cb79cc7a5 --- /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 4071c83df..243b5deb0 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 e463dd443..ee65b5674 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 000000000..551fb9276 --- /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 a139eb1a7..dd5d22c3b 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 8ab859887..fc9e2a29b 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 0ff6333aa..471bc6849 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 cb79cc7a5..9bc3577e5 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 243b5deb0..9794ca8fa 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 e3ffaec42..0463cffc4 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 000000000..b925c3fbd --- /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 6d40ae254..cf58ce202 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 9bc3577e5..b04dce902 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 b9b8c99cb..c04cea7ea 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 b04dce902..85b1dcddb 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 85b1dcddb..6f05a0e65 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 0d3de3701..8036d8743 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 027a54c3d..9839ade2a 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 bd4f33e9d..263e174b5 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 bae6f8b90..f3b8c58b9 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 4ddeff3af..d13c90d22 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 63c3c0227..f412e803c 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 0f0b33999..66560c0d0 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 471bc6849..38aee186e 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 408592c25..e4b04a6be 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 38aee186e..080dba67d 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 8036d8743..adcf077d5 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 9839ade2a..222786937 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 cb2b1f489..c0a436a68 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 0463cffc4..b5bfe222d 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 6f05a0e65..61ee95e77 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 263e174b5..498cb29c9 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 77158b155..8722f00d0 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 cf58ce202..6d40ae254 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 ee65b5674..e287b3288 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 c29460da9..8b90c5a68 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 11b8cf78a..fb1583f7e 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 af68a9653..760a2ec83 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 61ee95e77..36e8101e5 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 b925c3fbd..0e13e925d 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 b964773af..757fdd6e4 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 a5fed0102..b9b679bfc 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 483f556d3..b6d157c9c 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 97023cdd7..135579066 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 9aa67c862..20c4ac5b7 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 51aac97f2..dc0624dc4 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 135579066..f186f88ee 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 b9b679bfc..7c53d3265 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 000000000..84638f3cc --- /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 9db8880cb..da05d4f02 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 508e1b82b..fdeccda64 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 dc0624dc4..02163451a 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 f08c0b4c0..8d3f96fe6 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 da05d4f02..286ce91f8 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 02163451a..07f4ca5eb 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 63c3c0227..1b84f3ff6 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 1b84f3ff6..20b349ec1 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 fdeccda64..3746bc497 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 c4775c50c..22c79eca9 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 8d3f96fe6..07fa3da6b 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 f186f88ee..bc1facb33 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 6cde07e15..b9b622d36 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 2b056d9f8..6b9b2ca19 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 22c79eca9..61f6ad018 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 9794ca8fa..30becd117 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 e287b3288..42f05b745 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 de9bc9dcd..b354edb74 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 080dba67d..5955ce244 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 5b6ee85d1..d426740bc 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 ac25d82ce..a4d10e985 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 760a2ec83..3e025ccb8 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 bc1facb33..3b40db5bc 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 27b7132c2..f9460d603 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 f3b8c58b9..0eddd5041 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 36e8101e5..122a2969f 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 f9460d603..5683cf83d 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 3b40db5bc..f8812b308 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 5683cf83d..1b615d24b 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 1e8140623..296ebda0d 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 0eddd5041..0d4954227 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 122a2969f..9e783eb5b 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 adcbee54d..2d796e108 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 644b94d38..17b891c4a 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 d7a64de0b..5a7044974 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 000000000..6d98e522d --- /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 6b9b2ca19..e5e144414 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 ccae442fd..a351287cb 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 fc9e2a29b..22fafcfd0 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 d4b555c38..6b48ad64a 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 6b48ad64a..b655a6012 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 dd1ab2da9..fed094ac5 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 9e832494e..e296ad989 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 f2350768b..1e28c8694 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 dcb768831..0c5dc4971 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 5a7044974..744501397 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 53272f326..be8b6182b 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 e5e144414..42faa3372 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 246dca8d2..d7b4b1bbc 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 000000000..8c3180255 --- /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 22fafcfd0..3d60e89a1 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 2d796e108..ef5b7fbd5 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 6d98e522d..ea27e6eb3 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 a351287cb..68da0b40c 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 9fe446504..f4e480546 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 de149c544..a5de1a186 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 dda3ea212..a2021e85e 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 fe50d4f5f..d0aac7a86 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 d7b4b1bbc..34c14667c 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 5955ce244..d72971206 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 9743a0832..d2280d177 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 a4d10e985..dd77ad431 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 9e783eb5b..f6138fb68 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 a5de1a186..0d578e24f 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 3746bc497..191981dbe 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 ea27e6eb3..7a1448e16 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 000000000..b842ec1ed --- /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 ef5b7fbd5..e490e855e 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 e17f5c2ee..5bb901b3d 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 000000000..672475838 --- /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 000000000..e22bf9dbe --- /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 000000000..4f9885172 --- /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 744501397..2379fe8ea 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 000000000..ce975245c --- /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 000000000..aa5067813 --- /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 000000000..1447559e5 --- /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 000000000..3270f0d8f --- /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 000000000..86d4d61e2 --- /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 7a1448e16..000000000 --- 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 68da0b40c..9f98c8967 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 b842ec1ed..aeee2b3b7 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 aeee2b3b7..0c7471a35 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 1447559e5..32193d334 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 0c7471a35..732464f6d 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 d72971206..e2a1d56dc 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 f6138fb68..caa6a290c 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 61f6ad018..21f7774f5 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 b655a6012..cd2decd60 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 fed094ac5..92448d9c0 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 84638f3cc..c7077451f 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 e490e855e..3c4ba09b2 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 b9b622d36..b87406e17 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 0c5dc4971..078ced99b 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 0d4954227..ad49e23d2 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 672475838..cf6265930 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 4f9885172..e303e45f7 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 2379fe8ea..ddad3f24c 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 32193d334..c9301b904 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 3270f0d8f..b8ea7aaba 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 86d4d61e2..9cd013f79 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 be8b6182b..8dab5a4cd 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 8b90c5a68..781e56c16 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 fb1583f7e..2ab8ae2d1 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 0e13e925d..11ec96782 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 c7ba649fe..fe9e03e3a 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 8722f00d0..29043020b 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 732464f6d..8ceee2150 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 b9f16b123..805716fa7 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 667e44977..080361e30 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 286ce91f8..e6bc3ddff 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 191981dbe..961e88147 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 757aa96c9..f9665cef9 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 79f253d84..8bff74715 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 258105d3a..407990cdc 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 9d21e2636..1047a9824 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 6efeeb001..6b444aecf 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 56b39a6e7..9d059902d 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 e4b04a6be..13b044865 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 fc5ea93e5..e1bfe8225 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 052c17308..7d9b88d8d 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 868cda131..3ecf4c655 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 a76534d13..f7017d7b1 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 e6bc3ddff..dbc14535b 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 07f4ca5eb..b101eae90 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 8bff74715..fa3cd0d7b 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 ad49e23d2..e16d9325d 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 cf6265930..3e4236417 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 c9301b904..058bfb439 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 b8ea7aaba..9bb5e572f 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 9cd013f79..38484f7ba 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 e303e45f7..f1d4d074b 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 dbc14535b..5bb6019b9 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 b101eae90..71f48881a 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 c04cea7ea..5d0ac7d12 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 3e025ccb8..df85886da 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 caa6a290c..2f86bfaf2 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 91efc16b7..9bcb005c7 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 3e4236417..f14fd50bf 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 e22bf9dbe..cbe8148c6 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 f1d4d074b..b7e79a311 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 058bfb439..12c2388f6 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 9bb5e572f..ff0ad86c8 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 38484f7ba..febec98e6 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 000000000..da0aa7ee4 --- /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 8ceee2150..2d81aa373 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 2d81aa373..bc43e0698 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 cae0d15bb..b790dedf2 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 13b044865..187c5c955 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 3c4ba09b2..b1ba58138 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 17b891c4a..8fcc91a4b 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 b7e79a311..c184db9a6 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 b1ba58138..21d5e64eb 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 596a5141b..616e678a4 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 3d60e89a1..fb65429dc 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 21d5e64eb..4e88e43a2 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 a11989280..f88f4a950 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 8dab5a4cd..ff12bf681 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 cd2decd60..a1ed4359a 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 ff12bf681..b789938a6 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 42faa3372..ea4dd0690 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 b789938a6..81f44548d 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 5bb6019b9..6eb5f7931 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 81f44548d..907bb1405 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 fb65429dc..4a9d3ecef 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 6eb5f7931..21a12e342 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 961e88147..dbb2b1d1e 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 71f48881a..e7ce6e5da 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 a2021e85e..1ca2b0ed2 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 c184db9a6..43a360892 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 000000000..1ec25c292 --- /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 ddad3f24c..217988781 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 12c2388f6..d2aa5cb1a 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 907bb1405..a4aa4a423 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 000000000..14c4df335 --- /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 9f98c8967..d3cf3b2bf 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 4a9d3ecef..5978634eb 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 bc43e0698..dfac214f6 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 e296ad989..763e63750 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 21a12e342..c1af7af5e 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 dfac214f6..5f6053b98 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 c1af7af5e..8c5bc3d5e 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 dbb2b1d1e..accb84ae6 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 e7ce6e5da..608a543c8 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 5b0d5549f..81a5a2f62 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 5978634eb..25a6f77ec 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 757fdd6e4..2418f97a1 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 616e678a4..34942ef29 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 adcf077d5..f03413afa 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 4e88e43a2..20ff39d92 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 371e33d44..1b4520303 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 222786937..62d7f15cf 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 498cb29c9..8217f6d4c 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 e16d9325d..a9261f7ba 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 d13c90d22..993b03190 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 f14fd50bf..e274c9ce4 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 43a360892..a4b3daf6d 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 d2aa5cb1a..926afca19 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 a4aa4a423..733b89acb 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 14c4df335..9df8b7782 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 d3cf3b2bf..f2de50d90 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 d426740bc..3a7e2a37c 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 eaea3c84d..5563de599 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 439c75290..b08b105d5 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 da0aa7ee4..7c767cc11 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 29043020b..6528b6131 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 2418f97a1..3d438bcdc 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 3a7e2a37c..fc00f3274 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 8f3a826bf..7a3e2d20b 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 f2de50d90..3acff6e6d 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 25a6f77ec..3bcbddd84 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 e2a1d56dc..6b566f3e2 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 763e63750..1e46d9782 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 8c5bc3d5e..7986a69bc 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 3acff6e6d..710f4a34e 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 7986a69bc..c691eebf0 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 accb84ae6..a1f7b997d 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 608a543c8..0c25965e3 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 000000000..03b479948 --- /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 b7a7e9389..258199ad8 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 710f4a34e..c953e663c 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 c953e663c..35072d565 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 03b479948..000000000 --- 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 000000000..03b479948 --- /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 1314dc0e0..e83815e62 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 35072d565..87723639e 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 217988781..1072ff50c 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 733b89acb..e1656924f 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 87723639e..0d8e26ec4 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 d12fee966..4d3c48a13 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 6f3aa77f3..f3cc3182d 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 3ecf4c655..86ea93303 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 3cb8343fc..76cca5cbc 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 733b89acb..a72e23dba 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 3bcbddd84..5c06b5a5a 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 65997e955..042cd8e1e 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 a72e23dba..a3367180b 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 0d8e26ec4..59b042db7 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 e1656924f..1210c1081 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 d4f6ed63d..079f01328 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 0c7b88cdf..7f8d507f1 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 92448d9c0..dcfee994f 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 000000000..33250737a --- /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 ad88c1797..d33b9dd90 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 59b042db7..03c405175 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 c6f87cff6..a20e0f2eb 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 8846623b0..eb13abc1b 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 b5bfe222d..504a3629e 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 66560c0d0..2734a67ca 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 5563de599..9801b8cbc 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 2734a67ca..f9fc5de89 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 051c8ce61..6361431a9 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 21bc0be87..414d983fc 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 000000000..a7cb39ce5 --- /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 b4ec95489..5cecee06d 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 6f25b1634..8415c6958 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 f03413afa..af6f9a5eb 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 76cca5cbc..7ef19b80d 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 62d7f15cf..17b8614f0 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 8217f6d4c..7c38088f4 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 a9261f7ba..68ecefee9 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 993b03190..a3994b474 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 136cb5f1e..f6dd7ce9f 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 9801b8cbc..a871cac65 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 b08b105d5..5cd9ef820 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 6528b6131..b3604199f 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 03c405175..4bb7d6ba3 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 b74cdbfdb..b31ba1d21 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 3d438bcdc..dc7eb2f5f 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 33250737a..29e5ed12f 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 af6f9a5eb..5ddad49e3 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 f3cc3182d..511de9ddd 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 1b4520303..d910e75eb 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 079f01328..124c0b99d 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 26513a067..010b5fb82 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 17b8614f0..add9f676e 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 000000000..1d088a493 --- /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 000000000..af52f02f1 --- /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 5cb945119..000000000 --- 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 d33b9dd90..d103011ab 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 68ecefee9..6ade401de 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 a3994b474..e54e61bd3 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 a7cb39ce5..6d373fafb 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 f6dd7ce9f..37b225919 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 fc00f3274..2f6f92367 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 f9fc5de89..74e4736d9 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 a871cac65..5d3bd6108 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 5cd9ef820..2aa1fb660 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 b3604199f..19fd18105 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 21f7774f5..13bbc2c0e 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 608a6f2a9..cd45452de 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 6b9a75619..b0836eebb 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 f5a0bc4f9..8d5f07fd3 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 5c06b5a5a..407d428f3 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 2f6f92367..d79f3d9f0 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 81bb43192..f89f03639 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 504a3629e..1c8b6606e 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 c691eebf0..13335a92a 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 8528287fd..aaa67ec39 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 d79f3d9f0..5d261e6a0 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 0c25965e3..8527cad61 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 a1f7b997d..d0646fe55 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 1c8b6606e..b2a42630a 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 13335a92a..6d4d3b66f 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 1ca2b0ed2..6cf3ba015 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 8527cad61..dddd9bab6 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 5d3bd6108..aa78a3936 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 f8812b308..ce9e1ae46 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 42aa64de9..49dbbd400 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 aaa67ec39..7d06876fd 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 b31ba1d21..4839a9924 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 6d40ae254..cce938648 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 a73f784f7..38045ed07 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 13bbc2c0e..f163ba657 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 4839a9924..71d1beaf9 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 6d4d3b66f..0e58d589f 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 dddd9bab6..b9dbba008 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 81a5a2f62..fc8e727ec 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 d0646fe55..b87fb8a0b 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 4d3c48a13..d52abe1d3 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 a137ad0cb..9bfe12959 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 284f67915..f85d3ca09 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 630e9c047..c2767f56f 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 6b566f3e2..59732098f 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 187c5c955..82e82fa50 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 dc7eb2f5f..1fa097247 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 fc8e727ec..82f2c7e5a 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 2f86bfaf2..4ddc013cb 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 7f8d507f1..d74250aa5 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 39a5d2c82..832b2e5c1 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 dfd096b58..80bee978d 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 000000000..9ac085d71 --- /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 c32752d3e..75a3c13e7 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 7c58693ec..7260a205e 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 f69e2ae5b..cd014362e 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 4f18495dd..48ff18f84 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 5ddad49e3..a6dd21bf1 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 add9f676e..61af3fcb8 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 7c38088f4..2cf979cda 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 000000000..f841da00e --- /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 6ade401de..85e3ceb7d 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 e54e61bd3..2a0de20ef 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 37b225919..9eec7cf59 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 aa78a3936..5dfa8b2a5 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 2aa1fb660..bdf1b1014 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 82744a0c9..5f53994c2 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 beac2a5df..fa365a9b0 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 19fd18105..9d486bc00 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 9eec7cf59..97357ccf2 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 75a3c13e7..60c6dfa0f 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 cd014362e..ef1dcd072 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 b87fb8a0b..b58658fa5 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 9ac085d71..a4d39925d 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 60c6dfa0f..c8cb4d7ce 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 ef1dcd072..7b2013729 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 b58658fa5..f8f96f427 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 20e79bed9..a9ffd273e 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 9bfe12959..7b3342ae9 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 f85d3ca09..e7900e444 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 06021c084..21e8393a1 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 f5182ee36..5be4f6f09 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 d04338cc9..49f8e7a75 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 cb188ba46..b9ad4d98b 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 524522e20..a6eee7750 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 82f2c7e5a..f2ab531b6 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 4c18bb90b..7dba008be 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 be79ea0a8..90390a1af 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 000000000..39c972ea1 --- /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 c8cb4d7ce..9378d2a6c 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 80bee978d..000000000 --- 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 5f53994c2..ac484db26 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 1bcf060c5..5d6ea28d8 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 d9a3b1c36..2d62ce046 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 a9ffd273e..1aa622006 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 1f5cfa293..937c29f23 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 fa365a9b0..5f43f7648 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 60eb7c5fc..4d49eba12 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 1aa622006..f292133af 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 34942ef29..d541af9cc 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 7ef19b80d..919e7d2c6 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 042cd8e1e..c544d2544 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 b76e50c3a..dcb87d89c 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 dcb87d89c..ba2e5a7f4 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 9378d2a6c..41c611143 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 21e8393a1..adf33ddc6 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 adf33ddc6..bef494383 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 38045ed07..d58ee5f86 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 85e3ceb7d..c26cb1b86 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 a20e0f2eb..07a7d5939 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 f292133af..640ce2dbc 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 d541af9cc..c8bfe8356 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 f9665cef9..56557e6d1 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 f4e480546..6dc1e3cc2 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 0d578e24f..5df0e5f7e 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 fa3cd0d7b..14f87afb0 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 6cf3ba015..3dd916118 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 407990cdc..e8cf584d6 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 1047a9824..f17487481 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 6b444aecf..1531fe66f 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 3e58a5bf7..58421a74c 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 d0aac7a86..66d1f3f6d 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 9d059902d..af3de63c3 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 5f43f7648..1df50a7ae 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 0e58d589f..f375da1ed 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 f8f96f427..c2578c08e 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 b9dbba008..d7054da26 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 5df0e5f7e..4324f1f65 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 3dd916118..5018b7d20 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 640ce2dbc..d4adb0117 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 41c611143..b4c776eef 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 fec4c9290..5a5b7b3e5 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 7bdcc89fa..1be7d667c 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 56557e6d1..7dfeb3f71 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 e8cf584d6..ddce42bca 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 1531fe66f..5039e880b 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 66d1f3f6d..f0d6f36a4 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 a6eee7750..d7d81f692 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 c544d2544..bccb1348a 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 d4adb0117..a50e5831f 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 bef494383..c7db9fc71 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 5be4f6f09..2e6bbba04 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 49f8e7a75..c9a8c33d5 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 b9ad4d98b..9b6ab4b41 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 c20a552dd..27976ff3f 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 9a2b9fac3..1a950c866 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 f7017d7b1..e2b3a6567 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 74a3d2dcf..2b21690ca 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 1b615d24b..8487ead6a 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 124c0b99d..19d14bd7b 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 01dab88f6..42e94e4b3 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 010b5fb82..054723fd9 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 a2dd09a32..d6db20274 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 82f0836b2..e2ea614c1 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 c26cb1b86..b5b4b7fb6 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 b2a42630a..e3d22eaac 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 fce017656..053f034ef 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 b3aad4b79..ce41e842c 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 000000000..fcb49112a --- /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 a50e5831f..214349c0d 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 c9a8c33d5..e14a914ca 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 27976ff3f..d02355211 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 511de9ddd..6853b3714 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 86ea93303..9a504f8e5 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 e2b3a6567..d76e71269 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 d0178e4cf..546bcee22 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 c0a436a68..9383a9ce2 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 d103011ab..683829c54 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 97357ccf2..ea3acc03a 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 ea4dd0690..51a5a1206 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 e3d22eaac..482e0d5a8 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 053f034ef..459d3c15d 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 e14a914ca..0a978d5d3 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 9b6ab4b41..7ddb5a884 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 9a504f8e5..a669412eb 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 1a950c866..12d886836 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 2b21690ca..73d26d0d4 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 8487ead6a..899ee0c6a 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 19d14bd7b..9f93618b2 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 42e94e4b3..801c151bb 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 054723fd9..496872db3 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 d6db20274..0404cbc22 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 e2ea614c1..0f58c21ff 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 9383a9ce2..358c4a8ea 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 ea3acc03a..2d97818d7 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 51a5a1206..183999f3d 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 ce41e842c..05a629768 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 fcb49112a..7c2fc2b11 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 2d62ce046..965a0cfd6 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 7c2fc2b11..2b4fcdb62 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 214349c0d..9020d8835 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 683829c54..21d7965b9 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 b5b4b7fb6..ee681cad4 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 bce4e64b7..61646c54a 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 2a0de20ef..8047aad60 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 6361431a9..04179458c 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 71d1beaf9..f60094356 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 482e0d5a8..d22650273 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 000000000..e895919d3 --- /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 1df50a7ae..1ec538ecd 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 dad3bc0bd..4def8d019 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 ee681cad4..7738f40c7 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 fe9e03e3a..b3b426bfd 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 e4d1fe5b5..8fa5ea5f7 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 805716fa7..2fb2ffbff 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 9461cab1d..00947b989 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 f375da1ed..285abea9a 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 c2578c08e..d567c3f32 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 d7054da26..ab86fe0d9 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 b790dedf2..9367cd440 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 7ddb5a884..702aa1346 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 e1bfe8225..7ecc3e8d1 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 7d9b88d8d..1af8da27e 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 a669412eb..c4e8e83c0 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 d76e71269..c74db0541 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 1ec538ecd..a3392096a 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 919e7d2c6..e49ae1abb 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 4bb7d6ba3..5b733b3f1 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 a3392096a..133216e52 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 bccb1348a..041fc6282 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 05a629768..ee274dc7b 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 d22650273..43b86e2f1 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 9020d8835..e17c4584a 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 2b4fcdb62..03aca1aa2 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 1d3591c8a..8c1449720 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 285abea9a..1ceacbab0 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 d567c3f32..fffe7f3cb 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 7dfeb3f71..121df48e8 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 5018b7d20..864ce5412 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 6853b3714..8379d75bd 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 5bb901b3d..4bc728c4c 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 1072ff50c..3ba149df6 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 5b733b3f1..32185d832 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 041fc6282..37e5d7777 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 7b2013729..19a62a4b4 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 1ceacbab0..bf801f266 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 ab86fe0d9..425e5c4a0 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)