mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-26 03:46:49 +01:00
merge master branch
This commit is contained in:
commit
88ba3e8e14
46
CHANGELOG.md
46
CHANGELOG.md
@ -1,9 +1,51 @@
|
||||
### Changelog
|
||||
|
||||
#### Version 0.28.0 (WIP)
|
||||
* Inital support for ZeTime: time,weather and activity data sync, notification support and music playback control is working
|
||||
#### Version 0.29.1
|
||||
* Mi Band 3: Support setting language to to German, Italian, French, Polish, Japanese, Korean (read wiki)
|
||||
* Mi Band 3: Support flashing latest RES files
|
||||
* Mi Band 3: Fix notification text not being displayed
|
||||
* Mi Band 3/Cor/Bip: Display app name when no app specific icon is available
|
||||
* Teclast: add/improve H1 and H3 watch recognition
|
||||
* Support transliteration for Lithuanian and Bengali
|
||||
* Fix BLE reconnect issues in certain conditions
|
||||
* Various fixes for display issues on small screens
|
||||
* Fix some potential NPEs
|
||||
* WIP: Display start and end of sleep in statistics
|
||||
|
||||
#### Version 0.29.0
|
||||
* New Device: Initial support for ID115
|
||||
* New Device: Initial support for Lenovo Watch9
|
||||
* Show splash screen during startup
|
||||
* Vertically align device icon in main activity
|
||||
* Try to support the google clock application (untested)
|
||||
* Amazfit Cor: Allow to configure displayed menu items
|
||||
* Amazfit Cor: Support basic music control
|
||||
* Amazfit Cor: Fix flashing font files
|
||||
* Amazfit Bip: improved GPX export
|
||||
* Amazfit Bip: Fix exported GPX file names for *FAT storage
|
||||
* Amazfit Bip: Fix current weather not being displayed with later firmwares
|
||||
* Amazfit Bip/Cor: Try to fix device being sometimes stuck in connecting state
|
||||
* Mi Band 2: Put some device specific settings into its own settings category
|
||||
* Mi Band 3: Support disabling of on-device menu items
|
||||
* Mi Band 3: Support locking the Mi Band sceen (swipe up to unlock)
|
||||
* Mi Band 2/3: New icon
|
||||
* NO1 F1: Set time during initialization
|
||||
|
||||
#### Version 0.28.1
|
||||
* Fix wrong weather icon mapping in rare cases
|
||||
* Fix device discovery on Android 4.4
|
||||
* Amazfit Bip: Use UTC in gpx tracks for better compatibility with external software
|
||||
* Amazfit Bip: Add the (localized) activity type to the gpx filename
|
||||
* Amazfit Bip: Fix weather on latest firmwares
|
||||
|
||||
#### Version 0.28.0
|
||||
* Initial support for ZeTime: time, weather and activity data sync, notification support and music playback control is working
|
||||
* Amazfit Bip/Cor: Rework firmware detection to cope with new version scheme
|
||||
* Amazfit Bip: Support setting language to Russian
|
||||
* Amazfit Cor: Support language switching on newer firmwares
|
||||
* Mi Band 3: support setting language (english and spanish tested)
|
||||
* Mi Band 3: Fix pairing
|
||||
* Mi Band 3: Send AQI to enable display of current temperature
|
||||
|
||||
#### Version 0.27.1
|
||||
* Pebble: Change appstore search to point to RomanPort's pebble appstore
|
||||
|
@ -26,17 +26,21 @@
|
||||
* Carsten Pfeiffer <cpfeiffer@users.noreply.github.com>
|
||||
* Daniele Gobbetti <daniele+github@gobbetti.name>
|
||||
* João Paulo Barraca <jpbarraca@gmail.com>
|
||||
* Jonas <jonasdcdm@posteo.net>
|
||||
* Yaron Shahrabani <sh.yaron@gmail.com>
|
||||
* Jonas <jonasdcdm@posteo.net>
|
||||
* postsorino <postsorino@krutt.org>
|
||||
* protomors <protomors@gmail.com>
|
||||
* Sebastian Kranz <tklightforce@googlemail.com>
|
||||
* Vadim Kaushan <admin@disasm.info>
|
||||
* Allan Nordhøy <epost@anotheragency.no>
|
||||
* protomors <protomors@gmail.com>
|
||||
* José Rebelo <joserebelo@outlook.com>
|
||||
* TaaviE <taavi.eomae+weblate@gmail.com>
|
||||
* mueller-ma <mueller-ma@users.noreply.github.com>
|
||||
* ivanovlev <ivanovlev@mail.ru>
|
||||
* naofum <naofum@gmail.com>
|
||||
* youzhiran <2668760098@qq.com>
|
||||
* Tijl Schepens <tijl.schepens@hotmail.com>
|
||||
* TaaviE <taavi.eomae+weblate@gmail.com>
|
||||
* mesnevi <shams@airpost.net>
|
||||
* Julien Pivotto <roidelapluie@inuits.eu>
|
||||
* Taavi Eomäe <taavi.eomae+github@gmail.com>
|
||||
* Steffen Liebergeld <perl@gmx.org>
|
||||
@ -45,52 +49,63 @@
|
||||
* Felix Konstantin Maurer <maufl@maufl.de>
|
||||
* Sergey Trofimov <sarg@sarg.org.ru>
|
||||
* Robert Barat <rbarat07@gmail.com>
|
||||
* José Rebelo <joserebelo@outlook.com>
|
||||
* Pavel Elagin <pelagin@techcd.ru>
|
||||
* JohnnySun <bmy001@gmail.com>
|
||||
* Uwe Hermann <uwe@hermann-uwe.de>
|
||||
* Kranz <Kranz>
|
||||
* Edoardo Rosa <edoardo.rosa90@gmail.com>
|
||||
* Alberto <albertsal83@gmail.com>
|
||||
* Vladislav Serkov <vladserkoff@protonmail.com>
|
||||
* Vebryn <vebryn@gmail.com>
|
||||
* Gilles Émilien MOREL <contact@gilles-morel.fr>
|
||||
* Gergely Peidl <gergely@peidl.net>
|
||||
* Emre <wenigerpluesch@mailbox.org>
|
||||
* Bożydar <trening302@o2.pl>
|
||||
* 0nse <0nse@users.noreply.github.com>
|
||||
* Максим Якимчук <xpinovo@gmail.com>
|
||||
* Rimas Raguliūnas <rarimas@gmail.com>
|
||||
* nautilusx <mail.ka@mailbox.org>
|
||||
* masakoodaa <masakoodaa@protonmail.com>
|
||||
* Marius Cornescu <marius_cornescu@yahoo.com>
|
||||
* Lukas Veneziano <fs@venezilu.de>
|
||||
* Kompact <joaorafael123@hotmail.com>
|
||||
* K0L0B0G <github@gorobav.ru>
|
||||
* Jasper <jespiex456@hotmail.com>
|
||||
* Christian Fischer <sw-dev@computerlyrik.de>
|
||||
* c4ndel4 <hadrian.candela@gmail.com>
|
||||
* 6arms1leg <m.brnsfld@googlemail.com>
|
||||
* Zhong Jianxin <azuwis@gmail.com>
|
||||
* walkjivefly <mark@walkjivefly.com>
|
||||
* Thomas <tutonis@gmail.com>
|
||||
* Ted Stein <me@tedstein.net>
|
||||
* petronovak <petro.novak@gmail.com>
|
||||
* Pascal <pascal.tannich@gmail.com>
|
||||
* NotAFIle <nota@notafile.com>
|
||||
* Normano64 <per.bergqwist@gmail.com>
|
||||
* NicoBuntu <nicolas__du95@hotmail.fr>
|
||||
* nautilusx <mail.ka@mailbox.org>
|
||||
* Minori Hiraoka (미노리) <minori@mnetwork.co.kr>
|
||||
* Michal Novotny <mignov@gmail.com>
|
||||
* mesnevi <shams@airpost.net>
|
||||
* Martin <ritualz@users.noreply.github.com>
|
||||
* LL <lu.lecocq@free.fr>
|
||||
* Jesús <zaagur@gmail.com>
|
||||
* exit-failure <hakrala@web.de>
|
||||
* Denis <korden@sky-play.ru>
|
||||
* Avamander <Avamander@users.noreply.github.com>
|
||||
* AnthonyDiGirolamo <anthony.digirolamo@gmail.com>
|
||||
* Andreas Kromke <Andreas.Kromke@web.de>
|
||||
* Ⲇⲁⲛⲓ Φi <daniphii@outlook.com>
|
||||
* Your Name <you@example.com>
|
||||
* Yar <yaroslav.isakov@gmail.com>
|
||||
* xzovy <caleb@caleb-cooper.net>
|
||||
* xphnx <xphnx@users.noreply.github.com>
|
||||
* Vitaliy Shuruta <vshuruta@gmail.com>
|
||||
* Vincèn PUJOL <vincen@vincen.org>
|
||||
* Tomer Rosenfeld <tomerosenfeld007@gmail.com>
|
||||
* Tomas Radej <tradej@redhat.com>
|
||||
* tiparega <11555126+tiparega@users.noreply.github.com>
|
||||
* Tarik Sekmen <tarik@ilixi.org>
|
||||
* Szymon Tomasz Stefanek <s.stefanek@gmail.com>
|
||||
* szilardx <15869670+szilardx@users.noreply.github.com>
|
||||
* Sergio Lopez <slp@sinrega.org>
|
||||
* Sami Alaoui <4ndroidgeek@gmail.com>
|
||||
* Roman Plevka <rplevka@redhat.com>
|
||||
@ -98,47 +113,61 @@
|
||||
* redking <redking974@gmail.com>
|
||||
* Quallenauge <Hamsi2k@freenet.de>
|
||||
* Pavel Motyrev <legioner.r@gmail.com>
|
||||
* Pascal <pascal.tannich@gmail.com>
|
||||
* Olexandr Nesterenko <olexn@ukr.net>
|
||||
* Nicolò Balzarotti <anothersms@gmail.com>
|
||||
* Natanael Arndt <arndtn@gmail.com>
|
||||
* Molnár Barnabás <nsd4rkn3ss@gmail.com>
|
||||
* Moarc <aldwulf@gmail.com>
|
||||
* Mike van Rossum <mike@vanrossum.net>
|
||||
* Michal Novak <michal.novak@post.cz>
|
||||
* michaelneu <git@michaeln.eu>
|
||||
* McSym28 <McSym28@users.noreply.github.com>
|
||||
* MaxL <z60loa8qw3umzu3@my10minutemail.com>
|
||||
* Martin <ritualz@users.noreply.github.com>
|
||||
* maxirnilian <maxirnilian@users.noreply.github.com>
|
||||
* Martin Piatka <chachacha2323@gmail.com>
|
||||
* Margreet <margreetkeelan@gmail.com>
|
||||
* Marc Schlaich <marc.schlaich@googlemail.com>
|
||||
* Marcel pl (m4rcel) <marcel.garbarczyk@gmail.com>
|
||||
* Manuel Soler <vg8020@gmail.com>
|
||||
* Luiz Felipe das Neves Lopes <androidfelipe23@gmail.com>
|
||||
* Leonardo Amaral <contato@leonardoamaral.com.br>
|
||||
* lazarosfs <lazarosfs@csd.auth.gr>
|
||||
* ladbsoft <30509719+ladbsoft@users.noreply.github.com>
|
||||
* Kristjan Räts <kristjanrats@gmail.com>
|
||||
* Konrad Iturbe <KonradIT@users.noreply.github.com>
|
||||
* kevlarcade <kevlarcade@gmail.com>
|
||||
* Kevin Richter <me@kevinrichter.nl>
|
||||
* Kaz Wolfe <root@kazwolfe.io>
|
||||
* Kasha <kasha_malaga@hotmail.com>
|
||||
* kalaee <alex.kalaee@gmail.com>
|
||||
* Joseph Kim <official.jkim@gmail.com>
|
||||
* jonnsoft <>
|
||||
* Jan Lolek <janlolek@seznam.cz>
|
||||
* Jakub Jelínek <jakub.jelinek@gmail.com>
|
||||
* Ivan <ivan_tizhanin@mail.ru>
|
||||
* Hasan Ammar <ammarh@gmail.com>
|
||||
* Grzegorz Dznsk <grantmlody96@gmail.com>
|
||||
* Gilles MOREL <contact@gilles-morel.fr>
|
||||
* Gideão Gomes Ferreira <trjctr@gmail.com>
|
||||
* Gabe Schrecker <gabe@pbrb.co.uk>
|
||||
* freezed-or-frozen <freezed.or.frozen@gmail.com>
|
||||
* Frank Slezak <KazWolfe@users.noreply.github.com>
|
||||
* Dreamwalker <aristojeff@gmail.com>
|
||||
* Dougal19 <4662351+Dougal19@users.noreply.github.com>
|
||||
* Davis Mosenkovs <davikovs@gmail.com>
|
||||
* Daniel Hauck <maill@dhauck.eu>
|
||||
* dakhnod <dakhnod@gmail.com>
|
||||
* criogenic <criogenic@gmail.com>
|
||||
* clach04 <Chris.Clark@actian.com>
|
||||
* Chris Perelstein <chris.perelstein@gmail.com>
|
||||
* chabotsi <chabotsi+github@chabotsi.fr>
|
||||
* Carlos Ferreira <calbertoferreira@gmail.com>
|
||||
* bucala <marcel.bucala@gmail.com>
|
||||
* boun <boun@gmx.de>
|
||||
* batataspt@gmail.com <batataspt@gmail.com>
|
||||
* atkyritsis <at.kyritsis@gmail.com>
|
||||
* Aniruddha Adhikary <aniruddha@adhikary.net>
|
||||
* andrewlytvyn <indusfreelancer@gmail.com>
|
||||
* AndrewH <36428679+andrewheadricke@users.noreply.github.com>
|
||||
* andre <andre.buesgen@yahoo.de>
|
||||
* Allen B <28495335+Allen-B1@users.noreply.github.com>
|
||||
|
45
FEATURES.md
45
FEATURES.md
@ -1,27 +1,28 @@
|
||||
## Feature Matrix
|
||||
|
||||
| | Pebble OG | Pebble Time/2 | Mi Band | Mi Band 2 | Amazfit Bip |
|
||||
|-----------------------------------| ----------|---------------|---------|-----------|-------------|
|
||||
|Calls Notification | YES | YES | YES | YES | YES |
|
||||
|Reject Calls | YES | YES | NO | NO | YES |
|
||||
|Accept Calls | NO(2) | NO(2) | NO | NO | NO |
|
||||
|Generic Notification | YES | YES | YES | YES | YES |
|
||||
|Dismiss Notifications on Phone | YES | YES | NO | NO | NO |
|
||||
|Predefined Replies | YES | YES | NO | NO | NO |
|
||||
|Voice Replies | N/A | NO(3) | N/A | N/A | N/A |
|
||||
|Calendar Sync | YES | YES | NO | NO | NO |
|
||||
|Configure alarms from Gadgetbridge | NO | NO | YES | YES | YES |
|
||||
|Smart alarms | NO(1) | YES | YES | NO | NO |
|
||||
|Weather | NO(1) | YES | NO | NO | YES |
|
||||
|Activity Tracking | NO(1) | YES | YES | YES | YES |
|
||||
|Sleep Tracking | NO(1) | YES | YES | YES | YES |
|
||||
|HR Tracking | N/A | YES | YES | YES | YES |
|
||||
|Realtime Activity Tracking | NO | NO | YES | YES | YES |
|
||||
|Music Control | YES | YES | NO | NO | NO |
|
||||
|Watchapp/face Installation | YES | YES | NO | NO | YES |
|
||||
|Firmware Installation | YES | YES | YES | YES | YES |
|
||||
|Taking Screenshots | YES | YES | NO | NO | NO |
|
||||
|Support Android Companion Apps | YES | YES | NO | NO | NO |
|
||||
| | Pebble OG | Pebble Time/2 | Mi Band | Mi Band 2 | Mi Band 3 | Amazfit Bip | Amazfit Cor |
|
||||
|-----------------------------------| ----------|---------------|---------|-----------|-----------|-------------|-------------|
|
||||
|Calls Notification | YES | YES | YES | YES | YES | YES | YES |
|
||||
|Reject Calls | YES | YES | NO | NO | YES | YES | YES |
|
||||
|Accept Calls | NO(2) | NO(2) | NO | NO | NO | NO | NO |
|
||||
|Generic Notification | YES | YES | YES | YES | YES | YES | YES |
|
||||
|Dismiss Notifications on Phone | YES | YES | NO | NO | NO | NO | NO |
|
||||
|Predefined Replies | YES | YES | NO | NO | NO | NO | NO |
|
||||
|Voice Replies | N/A | NO(3) | N/A | N/A | N/A | N/A | N/A |
|
||||
|Calendar Sync | YES | YES | NO | NO | NO | NO(3) | NO |
|
||||
|Configure alarms from Gadgetbridge | NO | NO | YES | YES | YES | YES | YES |
|
||||
|Smart alarms | NO(1) | YES | YES | NO | NO | NO | NO |
|
||||
|Weather | NO(1) | YES | NO | NO | YES | YES | YES |
|
||||
|Activity Tracking | NO(1) | YES | YES | YES | YES | YES | YES |
|
||||
|GPS tracks import | NO | NO | NO | NO | NO | YES | NO |
|
||||
|Sleep Tracking | NO(1) | YES | YES | YES | YES | YES | YES |
|
||||
|HR Tracking | N/A | YES | YES | YES | YES | YES | YES |
|
||||
|Realtime Activity Tracking | NO | NO | YES | YES | YES | YES | YES |
|
||||
|Music Control | YES | YES | NO | NO | NO | NO | YES |
|
||||
|Watchapp/face Installation | YES | YES | NO | NO | NO | YES | YES |
|
||||
|Firmware Installation | YES | YES | YES | YES | YES | YES | YES |
|
||||
|Taking Screenshots | YES | YES | NO | NO | NO | NO | NO |
|
||||
|Support Android Companion Apps | YES | YES | NO | NO | NO | NO | NO |
|
||||
|
||||
(1) Possible via 3rd Party Watchapp
|
||||
(2) Theoretically possible (works on iOS, would need lot of work)
|
||||
|
@ -70,6 +70,7 @@ public class GBDaoGenerator {
|
||||
addNo1F1ActivitySample(schema, user, device);
|
||||
addXWatchActivitySample(schema, user, device);
|
||||
addZeTimeActivitySample(schema, user, device);
|
||||
addID115ActivitySample(schema, user, device);
|
||||
|
||||
addCalendarSyncState(schema, device);
|
||||
|
||||
@ -301,6 +302,18 @@ public class GBDaoGenerator {
|
||||
return activitySample;
|
||||
}
|
||||
|
||||
private static Entity addID115ActivitySample(Schema schema, Entity user, Entity device) {
|
||||
Entity activitySample = addEntity(schema, "ID115ActivitySample");
|
||||
activitySample.implementsSerializable();
|
||||
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty("caloriesBurnt");
|
||||
activitySample.addIntProperty("distanceMeters");
|
||||
activitySample.addIntProperty("activeTimeMinutes");
|
||||
return activitySample;
|
||||
}
|
||||
|
||||
private static void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) {
|
||||
activitySample.setSuperclass(superClass);
|
||||
activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider");
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.4 KiB |
27
README.md
27
README.md
@ -2,7 +2,7 @@ Gadgetbridge
|
||||
============
|
||||
|
||||
Gadgetbridge is an Android (4.4+) application which will allow you to use your
|
||||
Pebble, Mi Band, Amazfit Bit and HPlus device (and more) without the vendor's closed source application
|
||||
Pebble, Mi Band, Amazfit Bip and HPlus device (and more) without the vendor's closed source application
|
||||
and without the need to create an account and transmit any of your data to the
|
||||
vendor's servers.
|
||||
|
||||
@ -13,27 +13,32 @@ vendor's servers.
|
||||
|
||||
[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Gadgetbridge/donate)
|
||||
|
||||
|
||||
[![Build](https://travis-ci.org/Freeyourgadget/Gadgetbridge.svg?branch=master)](https://travis-ci.org/Freeyourgadget/Gadgetbridge)
|
||||
|
||||
## Download
|
||||
|
||||
[![Gadgetbridge on F-Droid](/Get_it_on_F-Droid.svg.png?raw=true "Download from F-Droid")](https://f-droid.org/repository/browse/?fdid=nodomain.freeyourgadget.gadgetbridge)
|
||||
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/app/nodomain.freeyourgadget.gadgetbridge)
|
||||
|
||||
[List of changes](https://github.com/Freeyourgadget/Gadgetbridge/blob/master/CHANGELOG.md)
|
||||
|
||||
## Supported Devices
|
||||
* Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
* Pebble 2 [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
* Mi Band, Mi Band 1A, Mi Band 1S [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band)
|
||||
* Mi Band 2 [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-2)
|
||||
* Amazfit Bip [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Bip)
|
||||
* Amazfit Cor (no maintainer) [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Amazfit-Cor)
|
||||
* HPlus Devices (e.g. ZeBand) [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/HPlus)
|
||||
* Teclast H10, H30 (WIP)
|
||||
* ID115 (WIP)
|
||||
* Lenovo Watch 9 (WIP)
|
||||
* Liveview (WIP)
|
||||
* Mi Band, Mi Band 1A, Mi Band 1S [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band)
|
||||
* Mi Band 2 [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-2)
|
||||
* Mi Band 3 [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Mi-Band-3)
|
||||
* NO.1 F1 (WIP)
|
||||
* Liveview
|
||||
* Vibratissimo (experimental)
|
||||
* Pebble, Pebble Steel, Pebble Time, Pebble Time Steel, Pebble Time Round [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
* Pebble 2 [Wiki](https://github.com/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
* Teclast H10, H30 (WIP)
|
||||
* XWatch (Affordable Chinese Casio-like smartwatches)
|
||||
* Vibratissimo (experimental)
|
||||
* ZeTime (WIP)
|
||||
|
||||
## Features
|
||||
|
||||
@ -92,6 +97,10 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/
|
||||
* João Paulo Barraca (HPlus)
|
||||
* Vitaly Svyastyn (NO.1 F1)
|
||||
* Sami Alaoui (Teclast H30)
|
||||
* "ladbsoft" (XWatch)
|
||||
* Sebastian Kranz (ZeTime)
|
||||
* Vadim Kaushan (ID115)
|
||||
* "maxirnilian" (Lenovo Watch 9)
|
||||
|
||||
## Contribute
|
||||
|
||||
|
@ -25,8 +25,8 @@ android {
|
||||
targetSdkVersion 27
|
||||
|
||||
// Note: always bump BOTH versionCode and versionName!
|
||||
versionName "0.28.0"
|
||||
versionCode 134
|
||||
versionName "0.29.1"
|
||||
versionCode 137
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
@ -85,7 +85,7 @@ dependencies {
|
||||
implementation "org.greenrobot:greendao:2.2.1"
|
||||
implementation "org.apache.commons:commons-lang3:3.5"
|
||||
implementation "org.cyanogenmod:platform.sdk:6.0"
|
||||
|
||||
implementation 'com.jaredrummler:colorpicker:1.0.2'
|
||||
// implementation project(":DaoCore")
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
-->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
@ -45,7 +46,7 @@
|
||||
<activity
|
||||
android:name=".activities.ControlCenterv2"
|
||||
android:label="@string/title_activity_controlcenter"
|
||||
android:theme="@style/GadgetbridgeTheme.NoActionBar">
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -387,6 +388,12 @@
|
||||
<activity
|
||||
android:name=".devices.pebble.PebblePairingActivity"
|
||||
android:label="@string/title_activity_pebble_pairing" />
|
||||
<activity
|
||||
android:name=".devices.watch9.Watch9PairingActivity"
|
||||
android:label="@string/title_activity_watch9_pairing" />
|
||||
<activity
|
||||
android:name=".devices.watch9.Watch9CalibrationActivity"
|
||||
android:label="@string/title_activity_watch9_calibration" />
|
||||
<activity
|
||||
android:name=".activities.charts.ChartsActivity"
|
||||
android:label="@string/title_activity_charts"
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Martin, Normano64, Taavi Eomäe
|
||||
Gobbetti, Martin, Normano64, Pavel Elagin, Taavi Eomäe
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -228,6 +228,10 @@ public class GBApplication extends Application {
|
||||
logging.setupLogging(enabled);
|
||||
}
|
||||
|
||||
public static String getLogPath(){
|
||||
return logging.getLogPath();
|
||||
}
|
||||
|
||||
private void setupExceptionHandler() {
|
||||
LoggingExceptionHandler handler = new LoggingExceptionHandler(Thread.getDefaultUncaughtExceptionHandler());
|
||||
Thread.setDefaultUncaughtExceptionHandler(handler);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti, Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -54,6 +54,13 @@ public abstract class Logging {
|
||||
}
|
||||
}
|
||||
|
||||
public String getLogPath() {
|
||||
if (fileLogger != null)
|
||||
return fileLogger.getFile();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void debugLoggingConfiguration() {
|
||||
// For debugging problems with the logback configuration
|
||||
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
|
@ -53,15 +53,15 @@ public class AlarmDetails extends AbstractGBActivity {
|
||||
alarm = getIntent().getParcelableExtra("alarm");
|
||||
device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
|
||||
timePicker = (TimePicker) findViewById(R.id.alarm_time_picker);
|
||||
cbSmartWakeup = (CheckedTextView) findViewById(R.id.alarm_cb_smart_wakeup);
|
||||
cbMonday = (CheckedTextView) findViewById(R.id.alarm_cb_monday);
|
||||
cbTuesday = (CheckedTextView) findViewById(R.id.alarm_cb_tuesday);
|
||||
cbWednesday = (CheckedTextView) findViewById(R.id.alarm_cb_wednesday);
|
||||
cbThursday = (CheckedTextView) findViewById(R.id.alarm_cb_thursday);
|
||||
cbFriday = (CheckedTextView) findViewById(R.id.alarm_cb_friday);
|
||||
cbSaturday = (CheckedTextView) findViewById(R.id.alarm_cb_saturday);
|
||||
cbSunday = (CheckedTextView) findViewById(R.id.alarm_cb_sunday);
|
||||
timePicker = findViewById(R.id.alarm_time_picker);
|
||||
cbSmartWakeup = findViewById(R.id.alarm_cb_smart_wakeup);
|
||||
cbMonday = findViewById(R.id.alarm_cb_monday);
|
||||
cbTuesday = findViewById(R.id.alarm_cb_tuesday);
|
||||
cbWednesday = findViewById(R.id.alarm_cb_wednesday);
|
||||
cbThursday = findViewById(R.id.alarm_cb_thursday);
|
||||
cbFriday = findViewById(R.id.alarm_cb_friday);
|
||||
cbSaturday = findViewById(R.id.alarm_cb_saturday);
|
||||
cbSunday = findViewById(R.id.alarm_cb_sunday);
|
||||
|
||||
|
||||
cbSmartWakeup.setOnClickListener(new View.OnClickListener() {
|
||||
|
@ -297,6 +297,8 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
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_CALL_LOG) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.READ_CALL_LOG);
|
||||
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)
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Frank Slezak, ivanovlev, Kasha, Lem Dulfo, Steffen Liebergeld
|
||||
Gobbetti, Frank Slezak, ivanovlev, Kasha, Lem Dulfo, Pavel Elagin, Steffen
|
||||
Liebergeld
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -17,12 +18,15 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
@ -39,6 +43,7 @@ import android.widget.Toast;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -53,9 +58,9 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static android.content.Intent.EXTRA_SUBJECT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
|
||||
|
||||
|
||||
public class DebugActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
|
||||
|
||||
@ -235,12 +240,58 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
testNewFunctionality();
|
||||
}
|
||||
});
|
||||
|
||||
Button shareLogButton = findViewById(R.id.shareLog);
|
||||
shareLogButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showWarning();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showWarning() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.share_log_warning)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String fileName = GBApplication.getLogPath();
|
||||
if (fileName != null && fileName.length() > 0) {
|
||||
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
emailIntent.setType("*/*");
|
||||
emailIntent.putExtra(EXTRA_SUBJECT, "Gadgetbridge log file");
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileName)));
|
||||
startActivity(Intent.createChooser(emailIntent, "Share File"));
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.Cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void testNewFunctionality() {
|
||||
GBApplication.deviceService().onTestNewFunction();
|
||||
}
|
||||
|
||||
private void shareLog() {
|
||||
String fileName = GBApplication.getLogPath();
|
||||
if(fileName != null && fileName.length() > 0) {
|
||||
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
emailIntent.setType("*/*");
|
||||
emailIntent.putExtra(EXTRA_SUBJECT, "Gadgetbridge log file");
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileName)));
|
||||
startActivity(Intent.createChooser(emailIntent, "Share File"));
|
||||
}
|
||||
}
|
||||
|
||||
private void testNotification() {
|
||||
Intent notificationIntent = new Intent(getApplicationContext(), DebugActivity.class);
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun, Lem Dulfo, Taavi Eomäe, Uwe Hermann
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, boun, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, JohnnySun, jonnsoft, Lem Dulfo, Taavi Eomäe, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -231,9 +231,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
|
||||
public void logMessageContent(byte[] value) {
|
||||
if (value != null) {
|
||||
for (byte b : value) {
|
||||
LOG.warn("DATA: " + String.format("0x%2x", b) + " - " + (char) (b & 0xff));
|
||||
}
|
||||
LOG.warn("DATA: " + GB.hexdump(value, 0, value.length));
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,7 +628,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
super.onPause();
|
||||
stopBTDiscovery();
|
||||
stopBTLEDiscovery();
|
||||
if (GB.supportsBluetoothLE()) {
|
||||
if (GBApplication.isRunningLollipopOrLater()) {
|
||||
stopNewBTLEDiscovery();
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.ItemWithDetailsAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
@ -191,7 +192,7 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
}
|
||||
|
||||
private InstallHandler findInstallHandlerFor(Uri uri) {
|
||||
for (DeviceCoordinator coordinator : DeviceHelper.getInstance().getAllCoordinators()) {
|
||||
for (DeviceCoordinator coordinator : getAllCoordinatorsConnectedFirst()) {
|
||||
InstallHandler handler = coordinator.findInstallHandler(uri, this);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
@ -200,6 +201,29 @@ public class FwAppInstallerActivity extends AbstractGBActivity implements Instal
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<DeviceCoordinator> getAllCoordinatorsConnectedFirst() {
|
||||
DeviceManager deviceManager = ((GBApplication) getApplicationContext()).getDeviceManager();
|
||||
List<DeviceCoordinator> connectedCoordinators = new ArrayList<>();
|
||||
List<DeviceCoordinator> allCoordinators = DeviceHelper.getInstance().getAllCoordinators();
|
||||
List<DeviceCoordinator> sortedCoordinators = new ArrayList<>(allCoordinators.size());
|
||||
|
||||
GBDevice connectedDevice = deviceManager.getSelectedDevice();
|
||||
if (connectedDevice != null && connectedDevice.isConnected()) {
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(connectedDevice);
|
||||
if (coordinator != null) {
|
||||
connectedCoordinators.add(coordinator);
|
||||
}
|
||||
}
|
||||
|
||||
sortedCoordinators.addAll(connectedCoordinators);
|
||||
for (DeviceCoordinator coordinator : allCoordinators) {
|
||||
if (!connectedCoordinators.contains(coordinator)) {
|
||||
sortedCoordinators.add(coordinator);
|
||||
}
|
||||
}
|
||||
return sortedCoordinators;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
@ -16,9 +16,14 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class HeartRateUtils {
|
||||
public static final int MAX_HEART_RATE_VALUE = 250;
|
||||
public static final int MIN_HEART_RATE_VALUE = 0;
|
||||
public static final int MIN_HEART_RATE_VALUE = 10;
|
||||
|
||||
/**
|
||||
* The maxiumum gap between two hr measurements in which
|
||||
* we interpolate between the measurements. Otherwise, two
|
||||
@ -28,7 +33,37 @@ public class HeartRateUtils {
|
||||
*/
|
||||
public static final int MAX_HR_MEASUREMENTS_GAP_MINUTES = 10;
|
||||
|
||||
public static boolean isValidHeartRateValue(int value) {
|
||||
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
|
||||
private int maxHeartRateValue;
|
||||
private int minHeartRateValue;
|
||||
|
||||
private static final HeartRateUtils instance = new HeartRateUtils();
|
||||
|
||||
public static HeartRateUtils getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton - to access this class use the static #getInstance()
|
||||
*/
|
||||
private HeartRateUtils() {
|
||||
updateCachedHeartRatePreferences();
|
||||
}
|
||||
|
||||
public void updateCachedHeartRatePreferences(){
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
maxHeartRateValue = prefs.getInt(GBPrefs.CHART_MAX_HEART_RATE, MAX_HEART_RATE_VALUE);
|
||||
minHeartRateValue = prefs.getInt(GBPrefs.CHART_MIN_HEART_RATE, MIN_HEART_RATE_VALUE);
|
||||
}
|
||||
|
||||
public int getMaxHeartRate(){
|
||||
return maxHeartRateValue;
|
||||
}
|
||||
|
||||
public int getMinHeartRate(){
|
||||
return minHeartRateValue;
|
||||
}
|
||||
|
||||
public boolean isValidHeartRateValue(int value) {
|
||||
return value >= getMinHeartRate() && value <= getMaxHeartRate();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, Felix Konstantin Maurer, Normano64
|
||||
Daniele Gobbetti, Felix Konstantin Maurer, José Rebelo, Martin, Normano64,
|
||||
Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -52,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
@ -60,7 +62,15 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DISPLAY_ITEMS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ENABLE_TEXT_NOTIFICATIONS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_BAND_SCREEN_UNLOCK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_END;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_OFF;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_SCHEDULED;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI3_NIGHT_MODE_START;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_HEIGHT_CM;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_SLEEP_DURATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivityUser.PREF_USER_STEPS_GOAL;
|
||||
@ -84,6 +94,8 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
|
||||
Preference pref = findPreference("notifications_generic");
|
||||
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
@ -341,7 +353,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
int autoFetchInterval = GBApplication.getPrefs().getInt("auto_fetch_interval_limit", 0);
|
||||
summary = String.format(
|
||||
getApplicationContext().getString(R.string.pref_auto_fetch_limit_fetches_summary),
|
||||
(int) autoFetchInterval);
|
||||
autoFetchInterval);
|
||||
pref.setSummary(summary);
|
||||
|
||||
|
||||
@ -360,6 +372,130 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
});
|
||||
|
||||
final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT);
|
||||
setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand2DisplayItems = findPreference(PREF_MI2_DISPLAY_ITEMS);
|
||||
miBand2DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand3ScreenUnlock = findPreference(PREF_MI3_BAND_SCREEN_UNLOCK);
|
||||
miBand3ScreenUnlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI3_BAND_SCREEN_UNLOCK);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand3DisplayItems = findPreference("miband3_display_items");
|
||||
miBand3DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
String nightModeState = prefs.getString(MiBandConst.PREF_MI3_NIGHT_MODE, PREF_MI3_NIGHT_MODE_OFF);
|
||||
boolean nightModeScheduled = nightModeState.equals(PREF_MI3_NIGHT_MODE_SCHEDULED);
|
||||
|
||||
final Preference nightModeStart = findPreference(PREF_MI3_NIGHT_MODE_START);
|
||||
nightModeStart.setEnabled(nightModeScheduled);
|
||||
nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE_START);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
final Preference nightModeEnd = findPreference(PREF_MI3_NIGHT_MODE_END);
|
||||
nightModeEnd.setEnabled(nightModeScheduled);
|
||||
nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE_END);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
final Preference nightMode = findPreference(PREF_MI3_NIGHT_MODE);
|
||||
nightMode.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
final boolean scheduled = PREF_MI3_NIGHT_MODE_SCHEDULED.equals(newVal.toString());
|
||||
|
||||
nightModeStart.setEnabled(scheduled);
|
||||
nightModeEnd.setEnabled(scheduled);
|
||||
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI3_NIGHT_MODE);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference corDisplayItems = findPreference("cor_display_items");
|
||||
corDisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Get all receivers of Media Buttons
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
|
||||
@ -488,6 +624,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
PREF_USER_WEIGHT_KG,
|
||||
PREF_USER_SLEEP_DURATION,
|
||||
PREF_USER_STEPS_GOAL,
|
||||
PREF_MI2_ENABLE_TEXT_NOTIFICATIONS,
|
||||
"weather_city",
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
Gobbetti, Konrad Iturbe, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -70,8 +70,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils.isValidHeartRateValue;
|
||||
|
||||
/**
|
||||
* A base class fragment to be used with ChartsActivity. The fragment can supply
|
||||
* a title to be displayed in the activity by returning non-null in #getTitle()
|
||||
@ -443,6 +441,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
List<Entry> heartrateEntries = hr ? new ArrayList<Entry>(numEntries) : null;
|
||||
List<Integer> colors = new ArrayList<>(numEntries); // this is kinda inefficient...
|
||||
int lastHrSampleIndex = -1;
|
||||
HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance();
|
||||
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
ActivitySample sample = samples.get(i);
|
||||
@ -512,7 +511,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
}
|
||||
activityEntries.add(createLineEntry(value, ts));
|
||||
}
|
||||
if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && HeartRateUtils.isValidHeartRateValue(sample.getHeartRate())) {
|
||||
if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
|
||||
if (lastHrSampleIndex > -1 && ts - lastHrSampleIndex > 1800*HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) {
|
||||
heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1));
|
||||
heartrateEntries.add(createLineEntry(0, ts - 1));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Vebryn
|
||||
Gobbetti, Pavel Elagin, Vebryn
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -19,7 +19,6 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -95,6 +94,9 @@ class ActivityAnalysis {
|
||||
}
|
||||
}
|
||||
|
||||
amount.setStartDate(sample.getTimestamp());
|
||||
amount.setEndDate(sample.getTimestamp());
|
||||
|
||||
previousAmount = amount;
|
||||
previousSample = sample;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
Gobbetti, Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -27,6 +27,7 @@ import android.view.ViewGroup;
|
||||
import com.github.mikephil.charting.animation.Easing;
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.charts.LineChart;
|
||||
import com.github.mikephil.charting.components.Legend;
|
||||
import com.github.mikephil.charting.components.LegendEntry;
|
||||
import com.github.mikephil.charting.components.XAxis;
|
||||
import com.github.mikephil.charting.components.YAxis;
|
||||
@ -103,8 +104,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaximum(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinimum(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMaximum(HeartRateUtils.getInstance().getMaxHeartRate());
|
||||
yAxisRight.setAxisMinimum(HeartRateUtils.getInstance().getMinHeartRate());
|
||||
|
||||
// refresh immediately instead of use refreshIfVisible(), for perceived performance
|
||||
refresh();
|
||||
@ -177,6 +178,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
|
||||
legendEntries.add(hrEntry);
|
||||
}
|
||||
chart.getLegend().setCustom(legendEntries);
|
||||
chart.getLegend().setWordWrapEnabled(true);
|
||||
chart.getLegend().setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,7 +168,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
private void addSample(ActivitySample sample) {
|
||||
int heartRate = sample.getHeartRate();
|
||||
int timestamp = tsTranslation.shorten(sample.getTimestamp());
|
||||
if (HeartRateUtils.isValidHeartRateValue(heartRate)) {
|
||||
if (HeartRateUtils.getInstance().isValidHeartRateValue(heartRate)) {
|
||||
setCurrentHeartRate(heartRate, timestamp);
|
||||
}
|
||||
int steps = sample.getSteps();
|
||||
@ -470,8 +470,8 @@ public class LiveActivityFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(false);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaximum(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinimum(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMaximum(HeartRateUtils.getInstance().getMaxHeartRate());
|
||||
yAxisRight.setAxisMinimum(HeartRateUtils.getInstance().getMinHeartRate());
|
||||
|
||||
mHistorySet = new LineDataSet(new ArrayList<Entry>(), getString(R.string.live_activity_steps_history));
|
||||
mHistorySet.setAxisDependency(YAxis.AxisDependency.LEFT);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti
|
||||
Daniele Gobbetti, Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -20,9 +20,11 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.github.mikephil.charting.animation.Easing;
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
@ -43,6 +45,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -62,6 +65,7 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
|
||||
private LineChart mActivityChart;
|
||||
private PieChart mSleepAmountChart;
|
||||
private TextView mSleepchartInfo;
|
||||
|
||||
private int mSmartAlarmFrom = -1;
|
||||
private int mSmartAlarmTo = -1;
|
||||
@ -86,9 +90,25 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
List<Integer> colors = new ArrayList<>();
|
||||
// int index = 0;
|
||||
long totalSeconds = 0;
|
||||
|
||||
Date startSleep = null;
|
||||
Date endSleep = null;
|
||||
|
||||
for (ActivityAmount amount : amounts.getAmounts()) {
|
||||
if ((amount.getActivityKind() & ActivityKind.TYPE_SLEEP) != 0) {
|
||||
long value = amount.getTotalSeconds();
|
||||
if(startSleep == null){
|
||||
startSleep = amount.getStartDate();
|
||||
} else {
|
||||
if(startSleep.after(amount.getStartDate()))
|
||||
startSleep = amount.getStartDate();
|
||||
}
|
||||
if(endSleep == null){
|
||||
endSleep = amount.getEndDate();
|
||||
} else {
|
||||
if(endSleep.before(amount.getEndDate()))
|
||||
endSleep = amount.getEndDate();
|
||||
}
|
||||
totalSeconds += value;
|
||||
// entries.add(new PieEntry(value, index++));
|
||||
entries.add(new PieEntry(value, amount.getName(getActivity())));
|
||||
@ -112,7 +132,7 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
data.setDataSet(set);
|
||||
|
||||
//setupLegend(pieChart);
|
||||
return new MySleepChartsData(totalSleep, data);
|
||||
return new MySleepChartsData(totalSleep, data, startSleep, endSleep);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,6 +144,15 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
mActivityChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
|
||||
mActivityChart.getXAxis().setValueFormatter(mcd.getChartsData().getXValueFormatter());
|
||||
mActivityChart.setData(mcd.getChartsData().getData());
|
||||
|
||||
if (mcd.getPieData().getStartSleep() != null && mcd.getPieData().getEndSleep() != null) {
|
||||
mSleepchartInfo.setText(getContext().getString(
|
||||
R.string.you_slept,
|
||||
DateTimeUtils.timeToString(mcd.getPieData().getStartSleep()),
|
||||
DateTimeUtils.timeToString(mcd.getPieData().getEndSleep())));
|
||||
} else {
|
||||
mSleepchartInfo.setText(getContext().getString(R.string.you_did_not_sleep));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -136,8 +165,9 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_sleepchart, container, false);
|
||||
|
||||
mActivityChart = (LineChart) rootView.findViewById(R.id.sleepchart);
|
||||
mSleepAmountChart = (PieChart) rootView.findViewById(R.id.sleepchart_pie_light_deep);
|
||||
mActivityChart = rootView.findViewById(R.id.sleepchart);
|
||||
mSleepAmountChart = rootView.findViewById(R.id.sleepchart_pie_light_deep);
|
||||
mSleepchartInfo = rootView.findViewById(R.id.sleepchart_info);
|
||||
|
||||
setupActivityChart();
|
||||
setupSleepAmountChart();
|
||||
@ -203,8 +233,8 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
yAxisRight.setDrawLabels(true);
|
||||
yAxisRight.setDrawTopYLabelEntry(true);
|
||||
yAxisRight.setTextColor(CHART_TEXT_COLOR);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.MAX_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.MIN_HEART_RATE_VALUE);
|
||||
yAxisRight.setAxisMaxValue(HeartRateUtils.getInstance().getMaxHeartRate());
|
||||
yAxisRight.setAxisMinValue(HeartRateUtils.getInstance().getMinHeartRate());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -246,10 +276,14 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
private static class MySleepChartsData extends ChartsData {
|
||||
private String totalSleep;
|
||||
private final PieData pieData;
|
||||
private @Nullable Date startSleep;
|
||||
private @Nullable Date endSleep;
|
||||
|
||||
public MySleepChartsData(String totalSleep, PieData pieData) {
|
||||
public MySleepChartsData(String totalSleep, PieData pieData, @Nullable Date startSleep, @Nullable Date endSleep) {
|
||||
this.totalSleep = totalSleep;
|
||||
this.pieData = pieData;
|
||||
this.startSleep = startSleep;
|
||||
this.endSleep = endSleep;
|
||||
}
|
||||
|
||||
public PieData getPieData() {
|
||||
@ -259,6 +293,16 @@ public class SleepChartFragment extends AbstractChartFragment {
|
||||
public CharSequence getTotalSleep() {
|
||||
return totalSleep;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Date getStartSleep() {
|
||||
return startSleep;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Date getEndSleep() {
|
||||
return endSleep;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyChartsData extends ChartsData {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||
|
||||
import com.github.mikephil.charting.charts.Chart;
|
||||
import com.github.mikephil.charting.components.AxisBase;
|
||||
import com.github.mikephil.charting.components.Legend;
|
||||
import com.github.mikephil.charting.components.LegendEntry;
|
||||
import com.github.mikephil.charting.data.Entry;
|
||||
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
|
||||
@ -132,5 +133,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
|
||||
|
||||
chart.getLegend().setCustom(legendEntries);
|
||||
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
|
||||
chart.getLegend().setWordWrapEnabled(true);
|
||||
chart.getLegend().setHorizontalAlignment(Legend.LegendHorizontalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
Gobbetti, José Rebelo, Lem Dulfo, maxirnilian
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -22,16 +22,19 @@ import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.InputType;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
@ -40,7 +43,11 @@ import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
@ -50,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9CalibrationActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
@ -108,9 +116,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
return true;
|
||||
}
|
||||
});
|
||||
holder.deviceImageView.setImageResource(R.drawable.level_list_device);
|
||||
//level-list does not allow negative values, hence we always add 100 to the key.
|
||||
holder.deviceImageView.setImageLevel(device.getType().getKey() + 100 + (device.isInitialized() ? 100 : 0));
|
||||
holder.deviceImageView.setImageResource(device.isInitialized() ? device.getType().getIcon() : device.getType().getDisabledIcon());
|
||||
|
||||
holder.deviceNameLabel.setText(getUniqueDeviceName(device));
|
||||
|
||||
@ -126,16 +132,22 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
//battery
|
||||
holder.batteryStatusBox.setVisibility(View.GONE);
|
||||
short batteryLevel = device.getBatteryLevel();
|
||||
float batteryVoltage = device.getBatteryVoltage();
|
||||
BatteryState batteryState = device.getBatteryState();
|
||||
|
||||
if (batteryLevel != GBDevice.BATTERY_UNKNOWN) {
|
||||
holder.batteryStatusBox.setVisibility(View.VISIBLE);
|
||||
holder.batteryStatusLabel.setText(device.getBatteryLevel() + "%");
|
||||
BatteryState batteryState = device.getBatteryState();
|
||||
if (BatteryState.BATTERY_CHARGING.equals(batteryState) ||
|
||||
BatteryState.BATTERY_CHARGING_FULL.equals(batteryState)) {
|
||||
holder.batteryIcon.setImageLevel(device.getBatteryLevel() + 100);
|
||||
} else {
|
||||
holder.batteryIcon.setImageLevel(device.getBatteryLevel());
|
||||
}
|
||||
} else if (BatteryState.NO_BATTERY.equals(batteryState) && batteryVoltage != GBDevice.BATTERY_UNKNOWN) {
|
||||
holder.batteryStatusBox.setVisibility(View.VISIBLE);
|
||||
holder.batteryStatusLabel.setText(String.format(Locale.getDefault(), "%.1fV", batteryVoltage));
|
||||
holder.batteryIcon.setImageLevel(200);
|
||||
}
|
||||
|
||||
//fetch activity data
|
||||
@ -249,7 +261,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
|
||||
);
|
||||
|
||||
holder.findDevice.setVisibility(device.isInitialized() ? View.VISIBLE : View.GONE);
|
||||
holder.findDevice.setVisibility(device.isInitialized() && coordinator.supportsFindDevice() ? View.VISIBLE : View.GONE);
|
||||
holder.findDevice.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
{
|
||||
@ -292,6 +304,111 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
|
||||
);
|
||||
|
||||
holder.calibrateDevice.setVisibility(device.isInitialized() && device.getType() == DeviceType.WATCH9 ? View.VISIBLE : View.GONE);
|
||||
holder.calibrateDevice.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent startIntent = new Intent(context, Watch9CalibrationActivity.class);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
context.startActivity(startIntent);
|
||||
}
|
||||
});
|
||||
|
||||
holder.fmFrequencyBox.setVisibility(View.GONE);
|
||||
if (device.isInitialized() && device.getExtraInfo("fm_frequency") != null) {
|
||||
holder.fmFrequencyBox.setVisibility(View.VISIBLE);
|
||||
holder.fmFrequencyLabel.setText(String.format(Locale.getDefault(), "%.1f", (float) device.getExtraInfo("fm_frequency")));
|
||||
}
|
||||
final TextView fmFrequencyLabel = holder.fmFrequencyLabel;
|
||||
holder.fmFrequencyBox.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.preferences_fm_frequency);
|
||||
|
||||
final EditText input = new EditText(context);
|
||||
|
||||
input.setSelection(input.getText().length());
|
||||
input.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
|
||||
input.setText(String.format(Locale.getDefault(), "%.1f", (float) device.getExtraInfo("fm_frequency")));
|
||||
builder.setView(input);
|
||||
|
||||
builder.setPositiveButton(context.getResources().getString(android.R.string.ok),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
float frequency = Float.valueOf(input.getText().toString());
|
||||
// Trim to 1 decimal place, discard the rest
|
||||
frequency = Float.valueOf(String.format(Locale.getDefault(), "%.1f", frequency));
|
||||
if (frequency < 87.5 || frequency > 108.0) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.pref_invalid_frequency_title)
|
||||
.setMessage(R.string.pref_invalid_frequency_message)
|
||||
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
device.setExtraInfo("fm_frequency", frequency);
|
||||
fmFrequencyLabel.setText(String.format(Locale.getDefault(), "%.1f", (float) device.getExtraInfo("fm_frequency")));
|
||||
GBApplication.deviceService().onSetFmFrequency(frequency);
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(context.getResources().getString(R.string.Cancel), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
builder.show();
|
||||
}
|
||||
});
|
||||
|
||||
holder.ledColor.setVisibility(View.GONE);
|
||||
if (device.isInitialized() && device.getExtraInfo("led_color") != null && coordinator.supportsLedColor()) {
|
||||
holder.ledColor.setVisibility(View.VISIBLE);
|
||||
final GradientDrawable ledColor = (GradientDrawable) holder.ledColor.getDrawable().mutate();
|
||||
ledColor.setColor((int) device.getExtraInfo("led_color"));
|
||||
holder.ledColor.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ColorPickerDialog.Builder builder = ColorPickerDialog.newBuilder();
|
||||
builder.setDialogTitle(R.string.preferences_led_color);
|
||||
|
||||
builder.setColor((int) device.getExtraInfo("led_color"));
|
||||
if (coordinator.supportsRgbLedColor()) {
|
||||
builder.setAllowCustom(true);
|
||||
builder.setShowAlphaSlider(false);
|
||||
builder.setAllowPresets(true);
|
||||
} else {
|
||||
builder.setAllowCustom(false);
|
||||
builder.setAllowPresets(true);
|
||||
builder.setShowColorShades(false);
|
||||
builder.setPresets(coordinator.getColorPresets());
|
||||
}
|
||||
|
||||
ColorPickerDialog dialog = builder.create();
|
||||
dialog.setColorPickerDialogListener(new ColorPickerDialogListener() {
|
||||
@Override
|
||||
public void onColorSelected(int dialogId, int color) {
|
||||
ledColor.setColor(color);
|
||||
device.setExtraInfo("led_color", color);
|
||||
GBApplication.deviceService().onSetLedColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogDismissed(int dialogId) {
|
||||
// Nothing to do
|
||||
}
|
||||
});
|
||||
dialog.show(((Activity) context).getFragmentManager(), "color-picker-dialog");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//remove device, hidden under details
|
||||
holder.removeDevice.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
@ -356,6 +473,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
ImageView setAlarmsView;
|
||||
ImageView showActivityGraphs;
|
||||
ImageView showActivityTracks;
|
||||
ImageView calibrateDevice;
|
||||
|
||||
ImageView deviceInfoView;
|
||||
//overflow
|
||||
@ -363,6 +481,9 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
ListView deviceInfoList;
|
||||
ImageView findDevice;
|
||||
ImageView removeDevice;
|
||||
LinearLayout fmFrequencyBox;
|
||||
TextView fmFrequencyLabel;
|
||||
ImageView ledColor;
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
@ -385,12 +506,16 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
showActivityGraphs = view.findViewById(R.id.device_action_show_activity_graphs);
|
||||
showActivityTracks = view.findViewById(R.id.device_action_show_activity_tracks);
|
||||
deviceInfoView = view.findViewById(R.id.device_info_image);
|
||||
calibrateDevice = view.findViewById(R.id.device_action_calibrate);
|
||||
|
||||
deviceInfoBox = view.findViewById(R.id.device_item_infos_box);
|
||||
//overflow
|
||||
deviceInfoList = view.findViewById(R.id.device_item_infos);
|
||||
findDevice = view.findViewById(R.id.device_action_find);
|
||||
removeDevice = view.findViewById(R.id.device_action_remove);
|
||||
fmFrequencyBox = view.findViewById(R.id.device_fm_frequency_box);
|
||||
fmFrequencyLabel = view.findViewById(R.id.fm_frequency);
|
||||
ledColor = view.findViewById(R.id.device_led_color);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -18,6 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
|
||||
public abstract class GBDeviceEvent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": ";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ public class GBDeviceEventBatteryInfo extends GBDeviceEvent {
|
||||
public BatteryState state = BatteryState.UNKNOWN;
|
||||
public short level = 50;
|
||||
public int numCharges = -1;
|
||||
public float voltage = -1f;
|
||||
|
||||
public boolean extendedInfoAvailable() {
|
||||
if (numCharges != -1 && lastChargeTime != null) {
|
||||
|
@ -0,0 +1,21 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
public class GBDeviceEventFmFrequency extends GBDeviceEvent {
|
||||
public float frequency;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
public class GBDeviceEventLEDColor extends GBDeviceEvent {
|
||||
public int color;
|
||||
}
|
@ -22,4 +22,9 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
public class GBDeviceEventVersionInfo extends GBDeviceEvent {
|
||||
public String fwVersion = GBApplication.getContext().getString(R.string.n_a);
|
||||
public String hwVersion = GBApplication.getContext().getString(R.string.n_a);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "fwVersion: " + fwVersion + "; hwVersion: " + hwVersion;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -129,4 +130,23 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
||||
public boolean supportsActivityTracks() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean supportsLedColor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRgbLedColor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getColorPresets() {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun, Uwe Hermann
|
||||
Gobbetti, JohnnySun, José Rebelo, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -242,4 +242,33 @@ public interface DeviceCoordinator {
|
||||
* forecast display.
|
||||
*/
|
||||
boolean supportsWeather();
|
||||
|
||||
/**
|
||||
* Indicates whether the device supports being found by vibrating,
|
||||
* making some sound or lighting up
|
||||
*/
|
||||
boolean supportsFindDevice();
|
||||
|
||||
/**
|
||||
* Indicates whether the device supports displaying music information
|
||||
* like artist, title, album, play state etc.
|
||||
*/
|
||||
boolean supportsMusicInfo();
|
||||
|
||||
/**
|
||||
* Indicates whether the device has an led which supports custom colors
|
||||
*/
|
||||
boolean supportsLedColor();
|
||||
|
||||
/**
|
||||
* Indicates whether the device's led supports any RGB color,
|
||||
* or only preset colors
|
||||
*/
|
||||
boolean supportsRgbLedColor();
|
||||
|
||||
/**
|
||||
* Returns the preset colors supported by the device, if any, in ARGB, with alpha = 255
|
||||
*/
|
||||
@NonNull
|
||||
int[] getColorPresets();
|
||||
}
|
||||
|
@ -99,4 +99,12 @@ public interface EventHandler {
|
||||
void onTestNewFunction();
|
||||
|
||||
void onSendWeather(WeatherSpec weatherSpec);
|
||||
|
||||
void onSetFmFrequency(float frequency);
|
||||
|
||||
/**
|
||||
* Set the device's led color.
|
||||
* @param color the new color, in ARGB, with alpha = 255
|
||||
*/
|
||||
void onSetLedColor(int color);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
Gobbetti, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -181,4 +181,24 @@ public class UnknownDeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLedColor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRgbLedColor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getColorPresets() {
|
||||
return new int[0];
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, João
|
||||
Paulo Barraca
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, João Paulo Barraca, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -103,6 +103,11 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.HPLUS;
|
||||
|
@ -263,4 +263,9 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator {
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.miband;
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -23,7 +23,7 @@ import java.util.UUID;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
|
||||
|
||||
public class MiBand2Service {
|
||||
public class HuamiService {
|
||||
|
||||
|
||||
public static final UUID UUID_SERVICE_MIBAND_SERVICE = UUID.fromString(String.format(BASE_UUID, "FEE0"));
|
||||
@ -50,6 +50,8 @@ public class MiBand2Service {
|
||||
public static final UUID UUID_CHARACTERISTIC_AUTH = UUID.fromString("00000009-0000-3512-2118-0009af100700");
|
||||
public static final UUID UUID_CHARACTERISTIC_DEVICEEVENT = UUID.fromString("00000010-0000-3512-2118-0009af100700");
|
||||
|
||||
public static final UUID UUID_CHARACTERISTIC_CHUNKEDTRANSFER = UUID.fromString("00000020-0000-3512-2118-0009af100700");
|
||||
|
||||
public static final int ALERT_LEVEL_NONE = 0;
|
||||
public static final int ALERT_LEVEL_MESSAGE = 1;
|
||||
public static final int ALERT_LEVEL_PHONE_CALL = 2;
|
||||
@ -101,7 +103,7 @@ public class MiBand2Service {
|
||||
/**
|
||||
* In some logs it's 0x0...
|
||||
*/
|
||||
public static final byte AUTH_BYTE = 0x8;
|
||||
public static final byte AUTH_BYTE = 0x08;
|
||||
|
||||
// maybe not really activity data, but steps?
|
||||
public static final byte COMMAND_FETCH_DATA = 0x02;
|
||||
@ -146,6 +148,7 @@ public class MiBand2Service {
|
||||
public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x1 };
|
||||
public static final byte[] COMMAND_DISTANCE_UNIT_METRIC = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x00 };
|
||||
public static final byte[] COMMAND_DISTANCE_UNIT_IMPERIAL = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x01 };
|
||||
public static final byte[] COMMAND_SET_LANGUAGE_NEW_TEMPLATE = new byte[]{ENDPOINT_DISPLAY, 0x17, 0x00, 0, 0, 0, 0, 0};
|
||||
|
||||
// The third byte controls the threshold, in minutes
|
||||
// The last 8 bytes represent 2 separate time intervals for the inactivity warnings
|
@ -19,9 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.DISPLAY_ITEM_BIT_CLOCK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.ENDPOINT_DISPLAY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.ENDPOINT_DISPLAY_ITEMS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.DISPLAY_ITEM_BIT_CLOCK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY_ITEMS;
|
||||
|
||||
public class AmazfitBipService {
|
||||
public static final UUID UUID_CHARACTERISTIC_WEATHER = UUID.fromString("0000000e-0000-3512-2118-0009af100700");
|
||||
@ -35,8 +35,6 @@ public class AmazfitBipService {
|
||||
public static final byte[] COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x01};
|
||||
public static final byte[] COMMAND_SET_LANGUAGE_ENGLISH = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x02};
|
||||
public static final byte[] COMMAND_SET_LANGUAGE_SPANISH = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x03};
|
||||
public static final byte[] COMMAND_SET_LANGUAGE_NEW_TEMPLATE = new byte[]{ENDPOINT_DISPLAY, 0x17, 0x00, 0, 0, 0, 0, 0};
|
||||
|
||||
|
||||
public static final byte COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES = 0x05;
|
||||
public static final byte COMMAND_ACTIVITY_DATA_TYPE_SPORTS_DETAILS = 0x06;
|
||||
|
@ -68,4 +68,9 @@ public class AmazfitCorCoordinator extends HuamiCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.DISPLAY_ITEM_BIT_CLOCK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY_ITEMS;
|
||||
|
||||
public class AmazfitCorService {
|
||||
public static final byte[] COMMAND_CHANGE_SCREENS = new byte[]{ENDPOINT_DISPLAY_ITEMS, DISPLAY_ITEM_BIT_CLOCK, 0x20, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
|
||||
}
|
@ -27,7 +27,7 @@ import org.slf4j.LoggerFactory;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
@ -43,7 +43,7 @@ public class MiBand2Coordinator extends HuamiCoordinator {
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
if (candidate.supportsService(MiBand2Service.UUID_SERVICE_MIBAND2_SERVICE)) {
|
||||
if (candidate.supportsService(HuamiService.UUID_SERVICE_MIBAND2_SERVICE)) {
|
||||
return DeviceType.MIBAND2;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, João Paulo Barraca
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, João Paulo Barraca
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -24,12 +24,17 @@ import android.support.annotation.NonNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
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 MiBand3Coordinator extends HuamiCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MiBand3Coordinator.class);
|
||||
@ -70,4 +75,23 @@ public class MiBand3Coordinator extends HuamiCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean getBandScreenUnlock() {
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
return prefs.getBoolean(MiBandConst.PREF_MI3_BAND_SCREEN_UNLOCK, false);
|
||||
}
|
||||
|
||||
public static String getNightMode() {
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
|
||||
return prefs.getString(MiBandConst.PREF_MI3_NIGHT_MODE, MiBandConst.PREF_MI3_NIGHT_MODE_OFF);
|
||||
}
|
||||
|
||||
public static Date getNightModeStart() {
|
||||
return getTimePreference( MiBandConst.PREF_MI3_NIGHT_MODE_START, "16:00");
|
||||
}
|
||||
|
||||
public static Date getNightModeEnd() {
|
||||
return getTimePreference(MiBandConst.PREF_MI3_NIGHT_MODE_END, "07:00");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.DISPLAY_ITEM_BIT_CLOCK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY_ITEMS;
|
||||
|
||||
public class MiBand3Service {
|
||||
public static final byte[] COMMAND_CHANGE_SCREENS = new byte[]{ENDPOINT_DISPLAY_ITEMS, DISPLAY_ITEM_BIT_CLOCK, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00};
|
||||
public static final byte[] COMMAND_ENABLE_BAND_SCREEN_UNLOCK = new byte[]{ENDPOINT_DISPLAY, 0x16, 0x00, 0x01};
|
||||
public static final byte[] COMMAND_DISABLE_BAND_SCREEN_UNLOCK = new byte[]{ENDPOINT_DISPLAY, 0x16, 0x00, 0x00};
|
||||
public static final byte[] COMMAND_NIGHT_MODE_OFF = new byte[]{0x1a, 0x00};
|
||||
public static final byte[] COMMAND_NIGHT_MODE_SUNSET = new byte[]{0x1a, 0x02};
|
||||
public static final byte[] COMMAND_NIGHT_MODE_SCHEDULED = new byte[]{0x1a, 0x01, 0x10, 0x00, 0x07, 0x00};
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/* Copyright (C) 2018 Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.id115;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
|
||||
|
||||
public class ID115Constants {
|
||||
public static final String PREF_WRIST = "id115_wrist";
|
||||
public static final String PREF_SCREEN_ORIENTATION = "id115_screen_orientation";
|
||||
|
||||
public static final UUID UUID_SERVICE_ID115 = UUID.fromString(String.format(BASE_UUID, "0AF0"));
|
||||
public static final UUID UUID_CHARACTERISTIC_WRITE_NORMAL = UUID.fromString(String.format(BASE_UUID, "0AF6"));
|
||||
public static final UUID UUID_CHARACTERISTIC_NOTIFY_NORMAL = UUID.fromString(String.format(BASE_UUID, "0AF7"));
|
||||
public static final UUID UUID_CHARACTERISTIC_WRITE_HEALTH = UUID.fromString(String.format(BASE_UUID, "0AF1"));
|
||||
public static final UUID UUID_CHARACTERISTIC_NOTIFY_HEALTH = UUID.fromString(String.format(BASE_UUID, "0AF2"));
|
||||
|
||||
public static final byte CMD_ID_WARE_UPDATE = 0x01;
|
||||
public static final byte CMD_ID_GET_INFO = 0x02;
|
||||
public static final byte CMD_ID_SETTINGS = 0x03;
|
||||
public static final byte CMD_ID_BIND_UNBIND = 0x04;
|
||||
public static final byte CMD_ID_NOTIFY = 0x05;
|
||||
public static final byte CMD_ID_APP_CONTROL = 0x06;
|
||||
public static final byte CMD_ID_BLE_CONTROL = 0x07;
|
||||
public static final byte CMD_ID_HEALTH_DATA = 0x08;
|
||||
public static final byte CMD_ID_DUMP_STACK = 0x20;
|
||||
public static final byte CMD_ID_LOG = 0x21;
|
||||
public static final byte CMD_ID_FACTORY = (byte)0xaa;
|
||||
public static final byte CMD_ID_DEVICE_RESTART = (byte)0xf0;
|
||||
|
||||
// CMD_ID_SETTINGS
|
||||
public static final byte CMD_KEY_SET_TIME = 0x01;
|
||||
public static final byte CMD_KEY_SET_GOAL = 0x03;
|
||||
public static final byte CMD_KEY_SET_HAND = 0x22;
|
||||
public static final byte CMD_ARG_LEFT = 0x00;
|
||||
public static final byte CMD_ARG_RIGHT = 0x01;
|
||||
public static final byte CMD_KEY_SET_DISPLAY_MODE = 0x2B;
|
||||
public static final byte CMD_ARG_HORIZONTAL = 0x00;
|
||||
public static final byte CMD_ARG_VERTICAL = 0x02;
|
||||
|
||||
// CMD_ID_NOTIFY
|
||||
public static final byte CMD_KEY_NOTIFY_CALL = 0x01;
|
||||
public static final byte CMD_KEY_NOTIFY_STOP = 0x02;
|
||||
public static final byte CMD_KEY_NOTIFY_MSG = 0x03;
|
||||
|
||||
// CMD_ID_HEALTH_DATA
|
||||
public static final byte CMD_KEY_FETCH_ACTIVITY_TODAY = 0x03;
|
||||
|
||||
// CMD_ID_DEVICE_RESTART
|
||||
public static final byte CMD_KEY_REBOOT = 0x01;
|
||||
|
||||
public static byte getNotificationType(NotificationType type) {
|
||||
switch (type) {
|
||||
// case GENERIC_EMAIL:
|
||||
// return 2; // Icon is not supported
|
||||
case WECHAT:
|
||||
return 3;
|
||||
// case QQ:
|
||||
// return 4;
|
||||
case FACEBOOK:
|
||||
return 6;
|
||||
case TWITTER:
|
||||
return 7;
|
||||
case WHATSAPP:
|
||||
return 8;
|
||||
case FACEBOOK_MESSENGER:
|
||||
return 9;
|
||||
case INSTAGRAM:
|
||||
return 10;
|
||||
case LINKEDIN:
|
||||
return 11;
|
||||
// case GENERIC_CALENDAR:
|
||||
// return 12; // Icon is not supported
|
||||
// case SKYPE:
|
||||
// return 13; // Icon is not supported
|
||||
// case LINE:
|
||||
// return 17; // Icon is not supported
|
||||
// case VIBER:
|
||||
// return 18; // Icon is not supported
|
||||
// case KAKAO_TALK:
|
||||
// return 19; // Icon is not supported
|
||||
// case VK:
|
||||
// return 16; // Icon is not supported
|
||||
// case GMAIL:
|
||||
// return 20; // Icon is not supported
|
||||
// case OUTLOOK:
|
||||
// return 21; // Icon is not supported
|
||||
// case SNAPCHAT:
|
||||
// return 22; // Icon is not supported
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.id115;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class ID115Coordinator extends AbstractDeviceCoordinator {
|
||||
@NonNull
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public Collection<? extends ScanFilter> createBLEScanFilters() {
|
||||
ParcelUuid service = new ParcelUuid(ID115Constants.UUID_SERVICE_ID115);
|
||||
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(service).build();
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
if (candidate.supportsService(ID115Constants.UUID_SERVICE_ID115)) {
|
||||
return DeviceType.ID115;
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDevice deviceCandidate){
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.ID115;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return new ID115SampleProvider(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmConfiguration() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "VeryFit";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/* Copyright (C) 2018 Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.id115;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.Property;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.ID115ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.ID115ActivitySampleDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class ID115SampleProvider extends AbstractSampleProvider<ID115ActivitySample> {
|
||||
public ID115SampleProvider(GBDevice device, DaoSession session) {
|
||||
super(device, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<ID115ActivitySample, ?> getSampleDao() {
|
||||
return getSession().getID115ActivitySampleDao();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Property getRawKindSampleProperty() {
|
||||
return ID115ActivitySampleDao.Properties.RawKind;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getTimestampSampleProperty() {
|
||||
return ID115ActivitySampleDao.Properties.Timestamp;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getDeviceIdentifierSampleProperty() {
|
||||
return ID115ActivitySampleDao.Properties.DeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int normalizeType(int rawType) {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toRawActivityKind(int activityKind) {
|
||||
return activityKind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalizeIntensity(int rawIntensity) {
|
||||
return rawIntensity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ID115ActivitySample createActivitySample() {
|
||||
return new ID115ActivitySample();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, protomors, Sami Alaoui
|
||||
Gobbetti, Dougal19, José Rebelo, protomors, Sami Alaoui
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -45,11 +45,16 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
|
||||
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(TeclastH30Coordinator.class);
|
||||
|
||||
// e.g. H3-B20F
|
||||
private Pattern deviceNamePattern = Pattern.compile("^H[13]-[ABCDEF0123456789]{4}$");
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
@ -62,10 +67,20 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
String name = candidate.getDevice().getName();
|
||||
if (name != null && (name.startsWith("TECLAST_H30") || name.startsWith("TECLAST_H10"))) {
|
||||
if (candidate.supportsService(JYouConstants.UUID_SERVICE_JYOU)) {
|
||||
return DeviceType.TECLASTH30;
|
||||
}
|
||||
|
||||
String name = candidate.getDevice().getName();
|
||||
if (name != null) {
|
||||
if (name.startsWith("TECLAST_H30") || name.startsWith("TECLAST_H10")) {
|
||||
return DeviceType.TECLASTH30;
|
||||
}
|
||||
Matcher deviceNameMatcher = deviceNamePattern.matcher(name);
|
||||
if (deviceNameMatcher.matches()) {
|
||||
return DeviceType.TECLASTH30;
|
||||
}
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@ -89,6 +104,11 @@ public class TeclastH30Coordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.TECLASTH30;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
Gobbetti, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -124,6 +124,11 @@ public class LiveviewCoordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
// nothing to delete, yet
|
||||
|
@ -69,6 +69,13 @@ public final class MiBandConst {
|
||||
public static final String PREF_MI2_INACTIVITY_WARNINGS_DND_END = "mi2_inactivity_warnings_dnd_end";
|
||||
public static final String PREF_MIBAND_SETUP_BT_PAIRING = "mi_setup_bt_pairing";
|
||||
|
||||
public static final String PREF_MI3_BAND_SCREEN_UNLOCK = "mi3_band_screen_unlock";
|
||||
public static final String PREF_MI3_NIGHT_MODE = "mi3_night_mode";
|
||||
public static final String PREF_MI3_NIGHT_MODE_START = "mi3_night_mode_start";
|
||||
public static final String PREF_MI3_NIGHT_MODE_END = "mi3_night_mode_end";
|
||||
public static final String PREF_MI3_NIGHT_MODE_OFF = "off";
|
||||
public static final String PREF_MI3_NIGHT_MODE_SUNSET = "sunset";
|
||||
public static final String PREF_MI3_NIGHT_MODE_SCHEDULED = "scheduled";
|
||||
|
||||
public static final String ORIGIN_INCOMING_CALL = "incoming_call";
|
||||
public static final String ORIGIN_ALARM_CLOCK = "alarm_clock";
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Christian
|
||||
Fischer, Daniele Gobbetti, Szymon Tomasz Stefanek
|
||||
Fischer, Daniele Gobbetti, José Rebelo, Szymon Tomasz Stefanek
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -176,6 +176,11 @@ public class MiBandCoordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean hasValidUserInfo() {
|
||||
String dummyMacAddress = MiBandService.MAC_ADDRESS_FILTER_1_1A + ":00:00:00";
|
||||
try {
|
||||
|
@ -109,34 +109,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
});
|
||||
|
||||
final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT);
|
||||
setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference displayPages = findPreference(PREF_MI2_DISPLAY_ITEMS);
|
||||
displayPages.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference activateDisplayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT);
|
||||
activateDisplayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
@ -440,7 +412,6 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity {
|
||||
prefKeys.add(ActivityUser.PREF_USER_STEPS_GOAL);
|
||||
prefKeys.add(PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR);
|
||||
prefKeys.add(PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS);
|
||||
prefKeys.add(PREF_MI2_ENABLE_TEXT_NOTIFICATIONS);
|
||||
prefKeys.add(PREF_MI2_INACTIVITY_WARNINGS_THRESHOLD);
|
||||
prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_ALARM_CLOCK));
|
||||
prefKeys.add(getNotificationPrefKey(VIBRATION_COUNT, ORIGIN_INCOMING_CALL));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, protomors
|
||||
Gobbetti, José Rebelo, protomors
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -152,6 +152,11 @@ public class No1F1Coordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
Long deviceId = device.getId();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
Gobbetti, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -160,4 +160,14 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,57 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.roidmi;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class Roidmi1Coordinator extends RoidmiCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Roidmi1Coordinator.class);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
try {
|
||||
BluetoothDevice device = candidate.getDevice();
|
||||
String name = device.getName();
|
||||
|
||||
if (name != null && name.contains("睿米车载蓝牙播放器")) {
|
||||
return DeviceType.ROIDMI;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("unable to check device support", ex);
|
||||
}
|
||||
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.ROIDMI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getColorPresets() {
|
||||
return RoidmiConst.COLOR_PRESETS;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.roidmi;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class Roidmi3Coordinator extends RoidmiCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Roidmi3Coordinator.class);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
try {
|
||||
BluetoothDevice device = candidate.getDevice();
|
||||
String name = device.getName();
|
||||
|
||||
if (name != null && name.contains("Roidmi Music Blue C")) {
|
||||
LOG.warn("Found a Roidmi 3, but support is disabled.");
|
||||
return DeviceType.UNKNOWN; // TODO Roidmi 3 is not working atm
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("unable to check device support", ex);
|
||||
}
|
||||
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.ROIDMI3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRgbLedColor() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.roidmi;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
public class RoidmiConst {
|
||||
public static final String ACTION_GET_LED_COLOR = "roidmi_get_led_color";
|
||||
public static final String ACTION_GET_FM_FREQUENCY = "roidmi_get_frequency";
|
||||
public static final String ACTION_GET_VOLTAGE = "roidmi_get_voltage";
|
||||
|
||||
public static final int[] COLOR_PRESETS = new int[]{
|
||||
Color.rgb(0xFF, 0x00, 0x00), // red
|
||||
Color.rgb(0x00, 0xFF, 0x00), // green
|
||||
Color.rgb(0x00, 0x00, 0xFF), // blue
|
||||
Color.rgb(0xFF, 0xFF, 0x01), // yellow
|
||||
Color.rgb(0x00, 0xAA, 0xE5), // sky blue
|
||||
Color.rgb(0xF0, 0x6E, 0xAA), // pink
|
||||
Color.rgb(0xFF, 0xFF, 0xFF), // white
|
||||
Color.rgb(0x00, 0x00, 0x00), // black
|
||||
};
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/* Copyright (C) 2018 José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.roidmi;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
|
||||
public abstract class RoidmiCoordinator extends AbstractDeviceCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RoidmiCoordinator.class);
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Roidmi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDevice device) {
|
||||
return BONDING_STYLE_BOND;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmConfiguration() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLedColor() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
Gobbetti, José Rebelo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -125,6 +125,11 @@ public class VibratissimoCoordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
// nothing to delete, yet
|
||||
|
@ -0,0 +1,123 @@
|
||||
/* Copyright (C) 2018 maxirnilian
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.NumberPicker;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
public class Watch9CalibrationActivity extends AbstractGBActivity {
|
||||
|
||||
private static final String STATE_DEVICE = "stateDevice";
|
||||
GBDevice device;
|
||||
|
||||
NumberPicker pickerHour, pickerMinute, pickerSecond;
|
||||
|
||||
Handler handler;
|
||||
Runnable holdCalibration;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_watch9_calibration);
|
||||
|
||||
pickerHour = findViewById(R.id.np_hour);
|
||||
pickerMinute = findViewById(R.id.np_minute);
|
||||
pickerSecond = findViewById(R.id.np_second);
|
||||
|
||||
pickerHour.setMinValue(1);
|
||||
pickerHour.setMaxValue(12);
|
||||
pickerHour.setValue(12);
|
||||
pickerMinute.setMinValue(0);
|
||||
pickerMinute.setMaxValue(59);
|
||||
pickerMinute.setValue(0);
|
||||
pickerSecond.setMinValue(0);
|
||||
pickerSecond.setMaxValue(59);
|
||||
pickerSecond.setValue(0);
|
||||
|
||||
handler = new Handler();
|
||||
holdCalibration = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(Watch9Constants.ACTION_CALIBRATION_HOLD));
|
||||
handler.postDelayed(this, 10000);
|
||||
}
|
||||
};
|
||||
|
||||
Intent intent = getIntent();
|
||||
device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
if (device == null && savedInstanceState != null) {
|
||||
device = savedInstanceState.getParcelable(STATE_DEVICE);
|
||||
}
|
||||
if (device == null) {
|
||||
finish();
|
||||
}
|
||||
|
||||
final Button btCalibrate = findViewById(R.id.watch9_bt_calibrate);
|
||||
btCalibrate.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
btCalibrate.setEnabled(false);
|
||||
handler.removeCallbacks(holdCalibration);
|
||||
Intent calibrationData = new Intent(Watch9Constants.ACTION_CALIBRATION_SEND);
|
||||
calibrationData.putExtra(Watch9Constants.VALUE_CALIBRATION_HOUR, pickerHour.getValue());
|
||||
calibrationData.putExtra(Watch9Constants.VALUE_CALIBRATION_MINUTE, pickerMinute.getValue());
|
||||
calibrationData.putExtra(Watch9Constants.VALUE_CALIBRATION_SECOND, pickerSecond.getValue());
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(calibrationData);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(STATE_DEVICE, device);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
device = savedInstanceState.getParcelable(STATE_DEVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
Intent calibration = new Intent(Watch9Constants.ACTION_CALIBRATION);
|
||||
calibration.putExtra(Watch9Constants.ACTION_ENABLE, true);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(calibration);
|
||||
handler.postDelayed(holdCalibration, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
Intent calibration = new Intent(Watch9Constants.ACTION_CALIBRATION);
|
||||
calibration.putExtra(Watch9Constants.ACTION_ENABLE, false);
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(calibration);
|
||||
handler.removeCallbacks(holdCalibration);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/* Copyright (C) 2018 maxirnilian
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class Watch9Constants {
|
||||
public static final UUID UUID_SERVICE_WATCH9 = UUID.fromString("0000a800-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static final UUID UUID_UNKNOWN_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static final UUID UUID_CHARACTERISTIC_WRITE = UUID.fromString("0000a801-0000-1000-8000-00805f9b34fb");
|
||||
public static final UUID UUID_CHARACTERISTIC_UNKNOWN_2 = UUID.fromString("0000a802-0000-1000-8000-00805f9b34fb");
|
||||
public static final UUID UUID_CHARACTERISTIC_UNKNOWN_3 = UUID.fromString("0000a803-0000-1000-8000-00805f9b34fb");
|
||||
public static final UUID UUID_CHARACTERISTIC_UNKNOWN_4 = UUID.fromString("0000a804-0000-1000-8000-00805f9b34fb");
|
||||
|
||||
public static final int NOTIFICATION_CHANNEL_DEFAULT = 128;
|
||||
public static final int NOTIFICATION_CHANNEL_PHONE_CALL = 1024;
|
||||
|
||||
public static final byte RESPONSE = 0x13;
|
||||
public static final byte REQUEST = 0x31;
|
||||
|
||||
public static final byte WRITE_VALUE = 0x01;
|
||||
public static final byte READ_VALUE = 0x02;
|
||||
public static final byte TASK = 0x04;
|
||||
public static final byte KEEP_ALIVE = -0x80;
|
||||
|
||||
public static final byte[] CMD_HEADER = new byte[]{0x23, 0x01, 0x00, 0x00, 0x00};
|
||||
|
||||
// byte[] COMMAND = new byte[]{0x23, 0x01, 0x00, 0x31, 0x00, ... , 0x00}
|
||||
// | | | | | | └ Checksum
|
||||
// | | | | | └ Command + value
|
||||
// | | | | └ Sequence number
|
||||
// | | | └ Response/Request indicator
|
||||
// | | └ Value length
|
||||
// | |
|
||||
// └-----└ Header
|
||||
|
||||
public static final byte[] CMD_FIRMWARE_INFO = new byte[]{0x01, 0x02};
|
||||
public static final byte[] CMD_AUTHORIZATION_TASK = new byte[]{0x01, 0x05};
|
||||
public static final byte[] CMD_TIME_SETTINGS = new byte[]{0x01, 0x08};
|
||||
public static final byte[] CMD_ALARM_SETTINGS = new byte[]{0x01, 0x0A};
|
||||
public static final byte[] CMD_BATTERY_INFO = new byte[]{0x01, 0x14};
|
||||
|
||||
public static final byte[] CMD_NOTIFICATION_TASK = new byte[]{0x03, 0x01};
|
||||
public static final byte[] CMD_NOTIFICATION_SETTINGS = new byte[]{0x03, 0x02};
|
||||
public static final byte[] CMD_CALIBRATION_INIT_TASK = new byte[]{0x03, 0x31};
|
||||
public static final byte[] CMD_CALIBRATION_TASK = new byte[]{0x03, 0x33, 0x01};
|
||||
public static final byte[] CMD_CALIBRATION_KEEP_ALIVE = new byte[]{0x03, 0x34};
|
||||
public static final byte[] CMD_DO_NOT_DISTURB_SETTINGS = new byte[]{0x03, 0x61};
|
||||
|
||||
public static final byte[] CMD_FITNESS_GOAL_SETTINGS = new byte[]{0x10, 0x02};
|
||||
|
||||
public static final byte[] RESP_AUTHORIZATION_TASK = new byte[]{0x01, 0x01, 0x05};
|
||||
public static final byte[] RESP_BUTTON_INDICATOR = new byte[]{0x04, 0x03, 0x11};
|
||||
public static final byte[] RESP_ALARM_INDICATOR = new byte[]{-0x80, 0x01, 0x0A};
|
||||
|
||||
public static final byte[] RESP_FIRMWARE_INFO = new byte[]{0x08, 0x01, 0x02};
|
||||
public static final byte[] RESP_TIME_SETTINGS = new byte[]{0x08, 0x01, 0x08};
|
||||
public static final byte[] RESP_BATTERY_INFO = new byte[]{0x08, 0x01, 0x14};
|
||||
public static final byte[] RESP_NOTIFICATION_SETTINGS = new byte[]{0x08, 0x03, 0x02};
|
||||
|
||||
public static final String ACTION_ENABLE = "action.watch9.enable";
|
||||
|
||||
public static final String ACTION_CALIBRATION
|
||||
= "nodomain.freeyourgadget.gadgetbridge.devices.action.watch9.start_calibration";
|
||||
public static final String ACTION_CALIBRATION_SEND
|
||||
= "nodomain.freeyourgadget.gadgetbridge.devices.action.watch9.send_calibration";
|
||||
public static final String ACTION_CALIBRATION_HOLD
|
||||
= "nodomain.freeyourgadget.gadgetbridge.devices.action.watch9.keep_calibrating";
|
||||
public static final String VALUE_CALIBRATION_HOUR
|
||||
= "value.watch9.calibration_hour";
|
||||
public static final String VALUE_CALIBRATION_MINUTE
|
||||
= "value.watch9.calibration_minute";
|
||||
public static final String VALUE_CALIBRATION_SECOND
|
||||
= "value.watch9.calibration_second";
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, maxirnilian, Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
|
||||
public class Watch9DeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public Collection<? extends ScanFilter> createBLEScanFilters() {
|
||||
ParcelUuid watch9Service = new ParcelUuid(Watch9Constants.UUID_SERVICE_WATCH9);
|
||||
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(watch9Service).build();
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
String macAddress = candidate.getMacAddress().toUpperCase();
|
||||
String deviceName = candidate.getName().toUpperCase();
|
||||
if (candidate.supportsService(Watch9Constants.UUID_SERVICE_WATCH9)) {
|
||||
return DeviceType.WATCH9;
|
||||
} else if (macAddress.startsWith("1C:87:79")) {
|
||||
return DeviceType.WATCH9;
|
||||
} else if (deviceName.equals("WATCH 9")) {
|
||||
return DeviceType.WATCH9;
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.WATCH9;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDevice deviceCandidate) {
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return Watch9PairingActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAlarmConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "Lenovo";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/* Copyright (C) 2018 maxirnilian
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.watch9;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.DiscoveryActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class Watch9PairingActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Watch9PairingActivity.class);
|
||||
|
||||
private static final String STATE_DEVICE_CANDIDATE = "stateDeviceCandidate";
|
||||
|
||||
private TextView message;
|
||||
private GBDeviceCandidate deviceCandidate;
|
||||
|
||||
private final BroadcastReceiver mPairingReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (GBDevice.ACTION_DEVICE_CHANGED.equals(intent.getAction())) {
|
||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
LOG.debug("pairing activity: device changed: " + device);
|
||||
if (deviceCandidate.getMacAddress().equals(device.getAddress())) {
|
||||
if (device.isInitialized()) {
|
||||
pairingFinished();
|
||||
} else if (device.isConnecting() || device.isInitializing()) {
|
||||
LOG.info("still connecting/initializing device...");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_watch9_pairing);
|
||||
|
||||
message = findViewById(R.id.watch9_pair_message);
|
||||
Intent intent = getIntent();
|
||||
deviceCandidate = intent.getParcelableExtra(DeviceCoordinator.EXTRA_DEVICE_CANDIDATE);
|
||||
if (deviceCandidate == null && savedInstanceState != null) {
|
||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||
}
|
||||
if (deviceCandidate == null) {
|
||||
Toast.makeText(this, getString(R.string.message_cannot_pair_no_mac), Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(this, DiscoveryActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
startPairing();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putParcelable(STATE_DEVICE_CANDIDATE, deviceCandidate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
deviceCandidate = savedInstanceState.getParcelable(STATE_DEVICE_CANDIDATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void startPairing() {
|
||||
message.setText(getString(R.string.pairing, deviceCandidate));
|
||||
|
||||
IntentFilter filter = new IntentFilter(GBDevice.ACTION_DEVICE_CHANGED);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mPairingReceiver, filter);
|
||||
|
||||
GBApplication.deviceService().disconnect();
|
||||
GBDevice device = DeviceHelper.getInstance().toSupportedDevice(deviceCandidate);
|
||||
if (device != null) {
|
||||
GBApplication.deviceService().connect(device, true);
|
||||
} else {
|
||||
GB.toast(this, "Unable to connect, can't recognize the device type: " + deviceCandidate, Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void pairingFinished() {
|
||||
AndroidUtils.safeUnregisterBroadcastReceiver(LocalBroadcastManager.getInstance(this), mPairingReceiver);
|
||||
|
||||
Intent intent = new Intent(this, ControlCenterv2.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, ladbsoft
|
||||
Gobbetti, José Rebelo, ladbsoft
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -130,4 +130,9 @@ public class XWatchCoordinator extends AbstractDeviceCoordinator {
|
||||
public boolean supportsWeather() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,19 @@
|
||||
/* Copyright (C) 2018 Kranz, Sebastian Kranz
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.zetime;
|
||||
|
||||
/**
|
||||
|
@ -1,3 +1,20 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, José Rebelo, Kranz, Sebastian Kranz
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.zetime;
|
||||
|
||||
import android.app.Activity;
|
||||
@ -67,6 +84,11 @@ public class ZeTimeCoordinator extends AbstractDeviceCoordinator {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
@ -127,6 +149,11 @@ public class ZeTimeCoordinator extends AbstractDeviceCoordinator {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(GBDevice device) {
|
||||
return BONDING_STYLE_NONE;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/* Copyright (C) 2018 Sebastian Kranz
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.zetime;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
@ -131,7 +131,7 @@ public class GPXExporter implements ActivityTrackExporter {
|
||||
ser.attribute(NS_DEFAULT, "lon", formatLocation(location.getLongitude()));
|
||||
ser.attribute(NS_DEFAULT, "lat", formatLocation(location.getLatitude()));
|
||||
ser.startTag(NS_DEFAULT, "ele").text(formatLocation(location.getAltitude())).endTag(NS_DEFAULT, "ele");
|
||||
ser.startTag(NS_DEFAULT, "time").text(formatTime(point.getTime())).endTag(NS_DEFAULT, "time");
|
||||
ser.startTag(NS_DEFAULT, "time").text(DateTimeUtils.formatIso8601UTC(point.getTime())).endTag(NS_DEFAULT, "time");
|
||||
String description = point.getDescription();
|
||||
if (description != null) {
|
||||
ser.startTag(NS_DEFAULT, "desc").text(description).endTag(NS_DEFAULT, "desc");
|
||||
@ -151,7 +151,7 @@ public class GPXExporter implements ActivityTrackExporter {
|
||||
}
|
||||
|
||||
int hr = point.getHeartRate();
|
||||
if (!HeartRateUtils.isValidHeartRateValue(hr)) {
|
||||
if (!HeartRateUtils.getInstance().isValidHeartRateValue(hr)) {
|
||||
if (!includeHeartRateOfNearestSample) {
|
||||
return;
|
||||
}
|
||||
@ -162,7 +162,7 @@ public class GPXExporter implements ActivityTrackExporter {
|
||||
}
|
||||
|
||||
hr = closestPointItem.getHeartRate();
|
||||
if (!HeartRateUtils.isValidHeartRateValue(hr)) {
|
||||
if (!HeartRateUtils.getInstance().isValidHeartRateValue(hr)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -177,11 +177,12 @@ public class GPXExporter implements ActivityTrackExporter {
|
||||
|
||||
private @Nullable ActivityPoint findClosestSensibleActivityPoint(Date time, List<ActivityPoint> trackPoints) {
|
||||
ActivityPoint closestPointItem = null;
|
||||
HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance();
|
||||
|
||||
long lowestDifference = 60 * 2 * 1000; // minimum distance is 2min
|
||||
for (ActivityPoint pointItem : trackPoints) {
|
||||
int hrItem = pointItem.getHeartRate();
|
||||
if (HeartRateUtils.isValidHeartRateValue(hrItem)) {
|
||||
if (heartRateUtilsInstance.isValidHeartRateValue(hrItem)) {
|
||||
Date timeItem = pointItem.getTime();
|
||||
if (timeItem.after(time) || timeItem.equals(time)) {
|
||||
break; // we assume that the given trackPoints are sorted in time ascending order (oldest first)
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -40,18 +41,20 @@ public class AlarmClockReceiver extends BroadcastReceiver {
|
||||
|
||||
/** A public action sent by AlarmService when the alarm has started. */
|
||||
public static final String ALARM_ALERT_ACTION = "com.android.deskclock.ALARM_ALERT";
|
||||
public static final String GOOGLE_CLOCK_ALARM_ALERT_ACTION = "com.google.android.deskclock.action.ALARM_ALERT";
|
||||
|
||||
/** A public action sent by AlarmService when the alarm has stopped for any reason. */
|
||||
public static final String ALARM_DONE_ACTION = "com.android.deskclock.ALARM_DONE";
|
||||
public static final String GOOGLE_CLOCK_ALARM_DONE_ACTION = "com.google.android.deskclock.action.ALARM_DONE";
|
||||
private int lastId;
|
||||
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (ALARM_ALERT_ACTION.equals(action)) {
|
||||
if (ALARM_ALERT_ACTION.equals(action) || GOOGLE_CLOCK_ALARM_ALERT_ACTION.equals(action)) {
|
||||
sendAlarm(true);
|
||||
} else if (ALARM_DONE_ACTION.equals(action)) {
|
||||
} else if (ALARM_DONE_ACTION.equals(action) || GOOGLE_CLOCK_ALARM_DONE_ACTION.equals(action)) {
|
||||
sendAlarm(false);
|
||||
}
|
||||
}
|
||||
|
@ -76,11 +76,11 @@ public class MusicPlaybackReceiver extends BroadcastReceiver {
|
||||
stateSpec.state = (byte) (((Boolean) incoming) ? MusicStateSpec.STATE_PLAYING : MusicStateSpec.STATE_PAUSED);
|
||||
stateSpec.playRate = (byte) (((Boolean) incoming) ? 100 : 0);
|
||||
} else if (incoming instanceof String && "duration".equals(key)) {
|
||||
musicSpec.duration = Integer.valueOf((String) incoming) / 1000;
|
||||
musicSpec.duration = Integer.parseInt((String) incoming) / 1000;
|
||||
} else if (incoming instanceof String && "trackno".equals(key)) {
|
||||
musicSpec.trackNr = Integer.valueOf((String) incoming);
|
||||
musicSpec.trackNr = Integer.parseInt((String) incoming);
|
||||
} else if (incoming instanceof String && "totaltrack".equals(key)) {
|
||||
musicSpec.trackCount = Integer.valueOf((String) incoming);
|
||||
musicSpec.trackCount = Integer.parseInt((String) incoming);
|
||||
} else if (incoming instanceof Integer && "pos".equals(key)) {
|
||||
stateSpec.position = (Integer) incoming;
|
||||
} else if (incoming instanceof Integer && "repeat".equals(key)) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Frank Slezak, Hasan Ammar, Julien Pivotto, Kevin Richter, Normano64,
|
||||
Steffen Liebergeld, Taavi Eomäe, Zhong Jianxin
|
||||
Gobbetti, Frank Slezak, Hasan Ammar, José Rebelo, Julien Pivotto, Kevin
|
||||
Richter, Normano64, Steffen Liebergeld, Taavi Eomäe, Zhong Jianxin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -50,6 +50,7 @@ import android.support.v7.graphics.Palette;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@ -86,6 +87,8 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
private LimitedQueue mActionLookup = new LimitedQueue(16);
|
||||
|
||||
private HashMap<String, Long> notificationTimes = new HashMap<>();
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
@ -191,6 +194,8 @@ public class NotificationListener extends NotificationListenerService {
|
||||
if (shouldIgnore(sbn))
|
||||
return;
|
||||
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
|
||||
switch (GBApplication.getGrantedInterruptionFilter()) {
|
||||
case NotificationManager.INTERRUPTION_FILTER_ALL:
|
||||
break;
|
||||
@ -265,6 +270,18 @@ public class NotificationListener extends NotificationListenerService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore too frequent notifications, according to user preference
|
||||
long min_timeout = prefs.getInt("notifications_timeout", 0) * 1000;
|
||||
Long cur_time = System.currentTimeMillis();
|
||||
if (notificationTimes.containsKey(source)) {
|
||||
Long last_time = notificationTimes.get(source);
|
||||
if (cur_time - last_time < min_timeout) {
|
||||
LOG.info("Ignoring frequent notification, last one was " + (cur_time - last_time) + "ms ago");
|
||||
return;
|
||||
}
|
||||
}
|
||||
notificationTimes.put(source, cur_time);
|
||||
|
||||
GBApplication.deviceService().onNotification(notificationSpec);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017-2018 Daniele Gobbetti
|
||||
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -121,7 +121,7 @@ public class OmniJawsObserver extends ContentObserver {
|
||||
|
||||
weatherSpec.windSpeed = toKmh(c.getFloat(11));
|
||||
weatherSpec.windDirection = c.getInt(12);
|
||||
weatherSpec.timestamp = (int) (Long.valueOf(c.getString(9)) / 1000);
|
||||
weatherSpec.timestamp = (int) (Long.parseLong(c.getString(9)) / 1000);
|
||||
} else if (i == 1) {
|
||||
weatherSpec.todayMinTemp = toKelvin(c.getFloat(5));
|
||||
weatherSpec.todayMaxTemp = toKelvin(c.getFloat(6));
|
||||
|
@ -41,9 +41,11 @@ public class PhoneCallReceiver extends BroadcastReceiver {
|
||||
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
|
||||
mSavedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
|
||||
} else {
|
||||
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
|
||||
int state = tm.getCallState();
|
||||
onCallStateChanged(context, state, number);
|
||||
if (intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)) {
|
||||
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
|
||||
int state = tm.getCallState();
|
||||
onCallStateChanged(context, state, number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -73,11 +74,13 @@ public class GBDevice implements Parcelable {
|
||||
private String mModel;
|
||||
private State mState = State.NOT_CONNECTED;
|
||||
private short mBatteryLevel = BATTERY_UNKNOWN;
|
||||
private float mBatteryVoltage = BATTERY_UNKNOWN;
|
||||
private short mBatteryThresholdPercent = BATTERY_THRESHOLD_PERCENT;
|
||||
private BatteryState mBatteryState;
|
||||
private short mRssi = RSSI_UNKNOWN;
|
||||
private String mBusyTask;
|
||||
private List<ItemWithDetails> mDeviceInfos;
|
||||
private HashMap<String, Object> mExtraInfos;
|
||||
|
||||
public GBDevice(String address, String name, DeviceType deviceType) {
|
||||
this(address, null, name, deviceType);
|
||||
@ -106,6 +109,7 @@ public class GBDevice implements Parcelable {
|
||||
mRssi = (short) in.readInt();
|
||||
mBusyTask = in.readString();
|
||||
mDeviceInfos = in.readArrayList(getClass().getClassLoader());
|
||||
mExtraInfos = (HashMap) in.readSerializable();
|
||||
|
||||
validate();
|
||||
}
|
||||
@ -126,6 +130,7 @@ public class GBDevice implements Parcelable {
|
||||
dest.writeInt(mRssi);
|
||||
dest.writeString(mBusyTask);
|
||||
dest.writeList(mDeviceInfos);
|
||||
dest.writeSerializable(mExtraInfos);
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
@ -371,6 +376,33 @@ public class GBDevice implements Parcelable {
|
||||
return mAddress.hashCode() ^ 37;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the extra info value if it is set, null otherwise
|
||||
* @param key the extra info key
|
||||
* @return the extra info value if set, null otherwise
|
||||
*/
|
||||
public Object getExtraInfo(String key) {
|
||||
if (mExtraInfos == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mExtraInfos.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an extra info value, overwriting the current one, if any
|
||||
* @param key the extra info key
|
||||
* @param info the extra info value
|
||||
*/
|
||||
public void setExtraInfo(String key, Object info) {
|
||||
if (mExtraInfos == null) {
|
||||
mExtraInfos = new HashMap<>();
|
||||
}
|
||||
|
||||
mExtraInfos.put(key, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ranges from 0-100 (percent), or -1 if unknown
|
||||
*
|
||||
@ -388,6 +420,23 @@ public class GBDevice implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setBatteryVoltage(float batteryVoltage) {
|
||||
if (batteryVoltage >= 0 || batteryVoltage == BATTERY_UNKNOWN) {
|
||||
mBatteryVoltage = batteryVoltage;
|
||||
} else {
|
||||
LOG.error("Battery voltage must be > 0: " + batteryVoltage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Voltage greater than zero (unit: Volt), or -1 if unknown
|
||||
*
|
||||
* @return the battery voltage, or -1 if unknown
|
||||
*/
|
||||
public float getBatteryVoltage() {
|
||||
return mBatteryVoltage;
|
||||
}
|
||||
|
||||
public BatteryState getBatteryState() {
|
||||
return mBatteryState;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
criogenic, Frank Slezak, ivanovlev, Julien Pivotto, Kasha, Steffen Liebergeld
|
||||
criogenic, dakhnod, Frank Slezak, ivanovlev, Julien Pivotto, Kasha, Steffen
|
||||
Liebergeld
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -41,9 +42,11 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.RtlUtils;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.JavaExtensions.coalesce;
|
||||
|
||||
|
||||
public class GBDeviceService implements DeviceService {
|
||||
protected final Context mContext;
|
||||
private final Class<? extends Service> mServiceClass;
|
||||
@ -81,6 +84,14 @@ public class GBDeviceService implements DeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
if (RtlUtils.rtlSupport()) {
|
||||
for (String extra : transliterationExtras) {
|
||||
if (intent.hasExtra(extra)) {
|
||||
intent.putExtra(extra, RtlUtils.fixRtl(intent.getStringExtra(extra)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mContext.startService(intent);
|
||||
}
|
||||
|
||||
@ -142,7 +153,8 @@ public class GBDeviceService implements DeviceService {
|
||||
.putExtra(EXTRA_NOTIFICATION_ID, notificationSpec.id)
|
||||
.putExtra(EXTRA_NOTIFICATION_TYPE, notificationSpec.type)
|
||||
.putExtra(EXTRA_NOTIFICATION_SOURCENAME, notificationSpec.sourceName)
|
||||
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor);
|
||||
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor)
|
||||
.putExtra(EXTRA_NOTIFICATION_SOURCEAPPID, notificationSpec.sourceAppId);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@ -403,4 +415,18 @@ public class GBDeviceService implements DeviceService {
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetFmFrequency(float frequency) {
|
||||
Intent intent = createIntent().setAction(ACTION_SET_FM_FREQUENCY)
|
||||
.putExtra(EXTRA_FM_FREQUENCY, frequency);
|
||||
invokeService(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetLedColor(int color) {
|
||||
Intent intent = createIntent().setAction(ACTION_SET_LED_COLOR)
|
||||
.putExtra(EXTRA_LED_COLOR, color);
|
||||
invokeService(intent);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Pavel Elagin
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -18,6 +18,8 @@ package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
public class ActivityAmount {
|
||||
@ -25,6 +27,8 @@ public class ActivityAmount {
|
||||
private short percent;
|
||||
private long totalSeconds;
|
||||
private long totalSteps;
|
||||
private Date startDate = null;
|
||||
private Date endDate = null;
|
||||
|
||||
public ActivityAmount(int activityKind) {
|
||||
this.activityKind = activityKind;
|
||||
@ -67,4 +71,21 @@ public class ActivityAmount {
|
||||
}
|
||||
return context.getString(R.string.abstract_chart_fragment_kind_activity);
|
||||
}
|
||||
|
||||
public Date getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
||||
public void setStartDate(int seconds) {
|
||||
if(startDate == null)
|
||||
this.startDate = new Date((long)seconds * 1000);
|
||||
}
|
||||
|
||||
public Date getEndDate() {
|
||||
return endDate;
|
||||
}
|
||||
|
||||
public void setEndDate(int seconds) {
|
||||
this.endDate = new Date((long)seconds * 1000);
|
||||
}
|
||||
}
|
||||
|
@ -22,5 +22,6 @@ public enum BatteryState {
|
||||
BATTERY_LOW,
|
||||
BATTERY_CHARGING,
|
||||
BATTERY_CHARGING_FULL,
|
||||
BATTERY_NOT_CHARGING_FULL
|
||||
BATTERY_NOT_CHARGING_FULL,
|
||||
NO_BATTERY
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Frank Slezak,
|
||||
ivanovlev, JohnnySun, Julien Pivotto, Kasha, Steffen Liebergeld
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, dakhnod,
|
||||
Frank Slezak, ivanovlev, JohnnySun, Julien Pivotto, Kasha, Steffen Liebergeld
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -65,12 +65,15 @@ public interface DeviceService extends EventHandler {
|
||||
String ACTION_SEND_CONFIGURATION = PREFIX + ".action.send_configuration";
|
||||
String ACTION_SEND_WEATHER = PREFIX + ".action.send_weather";
|
||||
String ACTION_TEST_NEW_FUNCTION = PREFIX + ".action.test_new_function";
|
||||
String ACTION_SET_FM_FREQUENCY = PREFIX + ".action.set_fm_frequency";
|
||||
String ACTION_SET_LED_COLOR = PREFIX + ".action.set_led_color";
|
||||
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
||||
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
||||
String EXTRA_NOTIFICATION_ID = "notification_id";
|
||||
String EXTRA_NOTIFICATION_PHONENUMBER = "notification_phonenumber";
|
||||
String EXTRA_NOTIFICATION_SENDER = "notification_sender";
|
||||
String EXTRA_NOTIFICATION_SOURCENAME = "notification_sourcename";
|
||||
String EXTRA_NOTIFICATION_SOURCEAPPID = "notification_sourceappid";
|
||||
String EXTRA_NOTIFICATION_SUBJECT = "notification_subject";
|
||||
String EXTRA_NOTIFICATION_TITLE = "notification_title";
|
||||
String EXTRA_NOTIFICATION_TYPE = "notification_type";
|
||||
@ -105,6 +108,8 @@ public interface DeviceService extends EventHandler {
|
||||
String EXTRA_INTERVAL_SECONDS = "interval_seconds";
|
||||
String EXTRA_WEATHER = "weather";
|
||||
String EXTRA_RECORDED_DATA_TYPES = "data_types";
|
||||
String EXTRA_FM_FREQUENCY = "fm_frequency";
|
||||
String EXTRA_LED_COLOR = "led_color";
|
||||
|
||||
/**
|
||||
* Use EXTRA_REALTIME_SAMPLE instead
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, Sami
|
||||
Alaoui, tiparega
|
||||
Gobbetti, João Paulo Barraca, José Rebelo, Kranz, ladbsoft, maxirnilian,
|
||||
protomors, Quallenauge, Sami Alaoui, tiparega, Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -33,10 +33,10 @@ public enum DeviceType {
|
||||
UNKNOWN(-1, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_unknown),
|
||||
PEBBLE(1, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_pebble),
|
||||
MIBAND(10, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband),
|
||||
MIBAND2(11, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband2),
|
||||
MIBAND2(11, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband2),
|
||||
AMAZFITBIP(12, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_amazfit_bip),
|
||||
AMAZFITCOR(13, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_amazfit_cor),
|
||||
MIBAND3(14, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband3),
|
||||
MIBAND3(14, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband3),
|
||||
VIBRATISSIMO(20, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo),
|
||||
LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview),
|
||||
HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus),
|
||||
@ -47,6 +47,10 @@ public enum DeviceType {
|
||||
TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_teclast_h30),
|
||||
XWATCH(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_xwatch),
|
||||
ZETIME(80, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_mykronoz_zetime),
|
||||
ID115(90, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_id115),
|
||||
WATCH9(100, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_watch9),
|
||||
ROIDMI(110, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi),
|
||||
ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3),
|
||||
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
|
||||
|
||||
private final int key;
|
||||
|
@ -75,4 +75,16 @@ public class MusicSpec {
|
||||
result = 31 * result + trackNr;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MusicSpec{" +
|
||||
"artist='" + artist + '\'' +
|
||||
", album='" + album + '\'' +
|
||||
", track='" + track + '\'' +
|
||||
", duration=" + duration +
|
||||
", trackCount=" + trackCount +
|
||||
", trackNr=" + trackNr +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -71,4 +71,15 @@ public class MusicStateSpec {
|
||||
result = 31 * result + (int) repeat;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MusicStateSpec{" +
|
||||
"state=" + state +
|
||||
", position=" + position +
|
||||
", playRate=" + playRate +
|
||||
", shuffle=" + shuffle +
|
||||
", repeat=" + repeat +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti, Sebastian
|
||||
Kranz
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -433,7 +434,7 @@ public class Weather {
|
||||
return 801;
|
||||
case 45: //thundershowers
|
||||
case 47: //isolated thundershowers
|
||||
return 621;
|
||||
return 211;
|
||||
case 3200: //not available
|
||||
default:
|
||||
return -1;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Taavi Eomäe
|
||||
Gobbetti, Sebastian Kranz, Taavi Eomäe
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -52,7 +52,9 @@ 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.GBDeviceEventFmFrequency;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventLEDColor;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventScreenshot;
|
||||
@ -159,6 +161,10 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
handleGBDeviceEvent((GBDeviceEventBatteryInfo) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceEventFindPhone) {
|
||||
handleGBDeviceEvent((GBDeviceEventFindPhone) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceEventLEDColor) {
|
||||
handleGBDeviceEvent((GBDeviceEventLEDColor) deviceEvent);
|
||||
} else if (deviceEvent instanceof GBDeviceEventFmFrequency) {
|
||||
handleGBDeviceEvent((GBDeviceEventFmFrequency) deviceEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +205,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
|
||||
protected void handleGBDeviceEvent(GBDeviceEventVersionInfo infoEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for VERSION_INFO");
|
||||
LOG.info("Got event for VERSION_INFO: " + infoEvent);
|
||||
if (gbDevice == null) {
|
||||
return;
|
||||
}
|
||||
@ -208,6 +214,26 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
gbDevice.sendDeviceUpdateIntent(context);
|
||||
}
|
||||
|
||||
protected void handleGBDeviceEvent(GBDeviceEventLEDColor colorEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for LED Color");
|
||||
if (gbDevice == null) {
|
||||
return;
|
||||
}
|
||||
gbDevice.setExtraInfo("led_color", colorEvent.color);
|
||||
gbDevice.sendDeviceUpdateIntent(context);
|
||||
}
|
||||
|
||||
protected void handleGBDeviceEvent(GBDeviceEventFmFrequency frequencyEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for FM Frequency");
|
||||
if (gbDevice == null) {
|
||||
return;
|
||||
}
|
||||
gbDevice.setExtraInfo("fm_frequency", frequencyEvent.frequency);
|
||||
gbDevice.sendDeviceUpdateIntent(context);
|
||||
}
|
||||
|
||||
private void handleGBDeviceEvent(GBDeviceEventAppInfo appInfoEvent) {
|
||||
Context context = getContext();
|
||||
LOG.info("Got event for APP_INFO");
|
||||
@ -328,21 +354,37 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
LOG.info("Got BATTERY_INFO device event");
|
||||
gbDevice.setBatteryLevel(deviceEvent.level);
|
||||
gbDevice.setBatteryState(deviceEvent.state);
|
||||
gbDevice.setBatteryVoltage(deviceEvent.voltage);
|
||||
|
||||
//show the notification if the battery level is below threshold and only if not connected to charger
|
||||
if (deviceEvent.level <= gbDevice.getBatteryThresholdPercent() &&
|
||||
(BatteryState.BATTERY_LOW.equals(deviceEvent.state) ||
|
||||
BatteryState.BATTERY_NORMAL.equals(deviceEvent.state))
|
||||
) {
|
||||
GB.updateBatteryNotification(context.getString(R.string.notif_battery_low_percent, gbDevice.getName(), String.valueOf(deviceEvent.level)),
|
||||
deviceEvent.extendedInfoAvailable() ?
|
||||
context.getString(R.string.notif_battery_low_percent, gbDevice.getName(), String.valueOf(deviceEvent.level)) + "\n" +
|
||||
context.getString(R.string.notif_battery_low_bigtext_last_charge_time, DateFormat.getDateTimeInstance().format(deviceEvent.lastChargeTime.getTime())) +
|
||||
context.getString(R.string.notif_battery_low_bigtext_number_of_charges, String.valueOf(deviceEvent.numCharges))
|
||||
: ""
|
||||
, context);
|
||||
if (deviceEvent.level == GBDevice.BATTERY_UNKNOWN) {
|
||||
// no level available, just "high" or "low"
|
||||
if (BatteryState.BATTERY_LOW.equals(deviceEvent.state)) {
|
||||
GB.updateBatteryNotification(context.getString(R.string.notif_battery_low, gbDevice.getName()),
|
||||
deviceEvent.extendedInfoAvailable() ?
|
||||
context.getString(R.string.notif_battery_low_extended, gbDevice.getName(),
|
||||
context.getString(R.string.notif_battery_low_bigtext_last_charge_time, DateFormat.getDateTimeInstance().format(deviceEvent.lastChargeTime.getTime())) +
|
||||
context.getString(R.string.notif_battery_low_bigtext_number_of_charges, String.valueOf(deviceEvent.numCharges)))
|
||||
: ""
|
||||
, context);
|
||||
} else {
|
||||
GB.removeBatteryNotification(context);
|
||||
}
|
||||
} else {
|
||||
GB.removeBatteryNotification(context);
|
||||
//show the notification if the battery level is below threshold and only if not connected to charger
|
||||
if (deviceEvent.level <= gbDevice.getBatteryThresholdPercent() &&
|
||||
(BatteryState.BATTERY_LOW.equals(deviceEvent.state) ||
|
||||
BatteryState.BATTERY_NORMAL.equals(deviceEvent.state))
|
||||
) {
|
||||
GB.updateBatteryNotification(context.getString(R.string.notif_battery_low_percent, gbDevice.getName(), String.valueOf(deviceEvent.level)),
|
||||
deviceEvent.extendedInfoAvailable() ?
|
||||
context.getString(R.string.notif_battery_low_percent, gbDevice.getName(), String.valueOf(deviceEvent.level)) + "\n" +
|
||||
context.getString(R.string.notif_battery_low_bigtext_last_charge_time, DateFormat.getDateTimeInstance().format(deviceEvent.lastChargeTime.getTime())) +
|
||||
context.getString(R.string.notif_battery_low_bigtext_number_of_charges, String.valueOf(deviceEvent.numCharges))
|
||||
: ""
|
||||
, context);
|
||||
} else {
|
||||
GB.removeBatteryNotification(context);
|
||||
}
|
||||
}
|
||||
|
||||
gbDevice.sendDeviceUpdateIntent(context);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Avamander, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, Daniel Hauck, Frank Slezak, ivanovlev, João Paulo Barraca,
|
||||
Julien Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld, Taavi Eomäe,
|
||||
Uwe Hermann
|
||||
dakhnod, Daniele Gobbetti, Daniel Hauck, Frank Slezak, ivanovlev, João Paulo
|
||||
Barraca, Julien Pivotto, Kasha, Martin, Sergey Trofimov, Steffen Liebergeld,
|
||||
Taavi Eomäe, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -47,6 +47,7 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmClockReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.AlarmReceiver;
|
||||
@ -105,7 +106,9 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SE
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SETTIME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_ALARMS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_CONSTANT_VIBRATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_FM_FREQUENCY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_HEARTRATE_MEASUREMENT_INTERVAL;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_SET_LED_COLOR;
|
||||
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.ACTION_TEST_NEW_FUNCTION;
|
||||
@ -130,7 +133,9 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CAN
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONFIG;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CONNECT_FIRST_TIME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FIND_START;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_FM_FREQUENCY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_INTERVAL_SECONDS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_LED_COLOR;
|
||||
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;
|
||||
@ -148,6 +153,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PEBBLE_COLOR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PHONENUMBER;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SENDER;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SOURCEAPPID;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SOURCENAME;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE;
|
||||
@ -348,6 +354,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
notificationSpec.pebbleColor = (byte) intent.getSerializableExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR);
|
||||
notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||
notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
||||
notificationSpec.sourceAppId = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCEAPPID);
|
||||
|
||||
if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) {
|
||||
notificationSpec.id = mRandom.nextInt(); // FIXME: add this in external SMS Receiver?
|
||||
@ -547,6 +554,18 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_SET_LED_COLOR:
|
||||
int color = intent.getIntExtra(EXTRA_LED_COLOR, 0);
|
||||
if (color != 0) {
|
||||
mDeviceSupport.onSetLedColor(color);
|
||||
}
|
||||
break;
|
||||
case ACTION_SET_FM_FREQUENCY:
|
||||
float frequency = intent.getFloatExtra(EXTRA_FM_FREQUENCY, -1);
|
||||
if (frequency != -1) {
|
||||
mDeviceSupport.onSetFmFrequency(frequency);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
@ -637,7 +656,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
mPebbleReceiver = new PebbleReceiver();
|
||||
registerReceiver(mPebbleReceiver, new IntentFilter("com.getpebble.action.SEND_NOTIFICATION"));
|
||||
}
|
||||
if (mMusicPlaybackReceiver == null) {
|
||||
if (mMusicPlaybackReceiver == null && coordinator != null && coordinator.supportsMusicInfo()) {
|
||||
mMusicPlaybackReceiver = new MusicPlaybackReceiver();
|
||||
IntentFilter filter = new IntentFilter();
|
||||
for (String action : mMusicActions) {
|
||||
@ -665,6 +684,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(AlarmClockReceiver.ALARM_ALERT_ACTION);
|
||||
filter.addAction(AlarmClockReceiver.ALARM_DONE_ACTION);
|
||||
filter.addAction(AlarmClockReceiver.GOOGLE_CLOCK_ALARM_ALERT_ACTION);
|
||||
filter.addAction(AlarmClockReceiver.GOOGLE_CLOCK_ALARM_DONE_ACTION);
|
||||
registerReceiver(mAlarmClockReceiver, filter);
|
||||
}
|
||||
if (mCMWeatherReceiver == null && coordinator != null && coordinator.supportsWeather()) {
|
||||
@ -764,6 +785,9 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
mDeviceSupport.setAutoReconnect(autoReconnect);
|
||||
}
|
||||
}
|
||||
if (GBPrefs.CHART_MAX_HEART_RATE.equals(key) || GBPrefs.CHART_MIN_HEART_RATE.equals(key)) {
|
||||
HeartRateUtils.getInstance().updateCachedHeartRatePreferences();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean hasPrefs() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge,
|
||||
Sami Alaoui, Sergey Trofimov, tiparega
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, João Paulo Barraca, José Rebelo, Kranz, ladbsoft, maxirnilian,
|
||||
protomors, Quallenauge, Sami Alaoui, Sergey Trofimov, tiparega, Vadim Kaushan
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -29,19 +29,22 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.id115.ID115Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch.XWatchSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.zetime.ZeTimeDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class DeviceSupportFactory {
|
||||
@ -115,7 +118,7 @@ public class DeviceSupportFactory {
|
||||
deviceSupport = new ServiceDeviceSupport(new MiBandSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case MIBAND2:
|
||||
deviceSupport = new ServiceDeviceSupport(new MiBand2Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
deviceSupport = new ServiceDeviceSupport(new HuamiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case MIBAND3:
|
||||
deviceSupport = new ServiceDeviceSupport(new MiBand3Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
@ -152,9 +155,22 @@ public class DeviceSupportFactory {
|
||||
break;
|
||||
case XWATCH:
|
||||
deviceSupport = new ServiceDeviceSupport(new XWatchSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
case ZETIME:
|
||||
break;
|
||||
case ZETIME:
|
||||
deviceSupport = new ServiceDeviceSupport(new ZeTimeDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case ID115:
|
||||
deviceSupport = new ServiceDeviceSupport(new ID115Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case WATCH9:
|
||||
deviceSupport = new ServiceDeviceSupport(new Watch9DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case ROIDMI:
|
||||
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case ROIDMI3:
|
||||
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
}
|
||||
if (deviceSupport != null) {
|
||||
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
|
||||
|
@ -374,4 +374,20 @@ public class ServiceDeviceSupport implements DeviceSupport {
|
||||
}
|
||||
delegate.onSendWeather(weatherSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetFmFrequency(float frequency) {
|
||||
if (checkBusy("set frequency event")) {
|
||||
return;
|
||||
}
|
||||
delegate.onSetFmFrequency(frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetLedColor(int color) {
|
||||
if (checkBusy("set led color event")) {
|
||||
return;
|
||||
}
|
||||
delegate.onSetLedColor(color);
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,10 @@ public abstract class BtClassicIoThread extends GBDeviceIoThread {
|
||||
public synchronized void write(byte[] bytes) {
|
||||
if (null == bytes)
|
||||
return;
|
||||
if (mOutStream == null) {
|
||||
LOG.error("mOutStream is null");
|
||||
return;
|
||||
}
|
||||
LOG.debug("writing:" + GB.hexdump(bytes, 0, bytes.length));
|
||||
try {
|
||||
mOutStream.write(bytes);
|
||||
|
@ -318,4 +318,14 @@ public abstract class AbstractBTLEDeviceSupport extends AbstractDeviceSupport im
|
||||
profile.onReadRemoteRssi(gatt, rssi, status);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetFmFrequency(float frequency) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetLedColor(int color) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ import android.content.Intent;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -44,16 +47,36 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
public abstract class AbstractBleProfile<T extends AbstractBTLEDeviceSupport> extends AbstractGattCallback {
|
||||
private final T mSupport;
|
||||
|
||||
private List<IntentListener> listeners = new ArrayList<IntentListener>(1);
|
||||
|
||||
public AbstractBleProfile(T support) {
|
||||
this.mSupport = support;
|
||||
}
|
||||
|
||||
public void addListener(IntentListener listener) {
|
||||
if (listener != null && !listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeListener(IntentListener listener) {
|
||||
return listeners.remove(listener);
|
||||
}
|
||||
|
||||
protected List<IntentListener> getListeners() {
|
||||
return Collections.unmodifiableList(listeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* All notifications should be sent through this methods to make them testable.
|
||||
* @param intent the intent to broadcast
|
||||
*/
|
||||
protected void notify(Intent intent) {
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
// note: we send synchronously in order to keep the processing order of BLE events
|
||||
// in BtleQueue and the reception of results.
|
||||
for (IntentListener listener : listeners) {
|
||||
listener.notify(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,26 @@
|
||||
/* Copyright (C) 2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.btle.profiles;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Callback interface for delivering results of ble requests.
|
||||
*/
|
||||
public interface IntentListener {
|
||||
void notify(Intent intent);
|
||||
}
|
@ -23,12 +23,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@ -63,7 +58,6 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
@ -79,35 +73,17 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
|
||||
private HPlusHandlerThread syncHelper;
|
||||
private DeviceType deviceType = DeviceType.UNKNOWN;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String s = intent.getAction();
|
||||
if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) {
|
||||
handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public HPlusSupport(DeviceType type) {
|
||||
super(LOG);
|
||||
LOG.info("HPlusSupport Instance Created");
|
||||
deviceType = type;
|
||||
|
||||
addSupportedService(HPlusConstants.UUID_SERVICE_HP);
|
||||
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
|
||||
broadcastManager.registerReceiver(mReceiver, intentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LOG.info("Dispose");
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(mReceiver);
|
||||
|
||||
close();
|
||||
|
||||
super.dispose();
|
||||
|
@ -14,13 +14,14 @@
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2;
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.AbstractMiBandOperation;
|
||||
|
||||
public abstract class AbstractMiBand2Operation extends AbstractMiBandOperation<MiBand2Support> {
|
||||
protected AbstractMiBand2Operation(MiBand2Support support) {
|
||||
public abstract class AbstractHuamiOperation extends AbstractMiBandOperation<HuamiSupport> {
|
||||
protected AbstractHuamiOperation(HuamiSupport support) {
|
||||
super(support);
|
||||
}
|
||||
|
@ -30,4 +30,5 @@ public class HuamiDeviceEvent {
|
||||
public static final byte BUTTON_PRESSED_LONG = 0x0b;
|
||||
public static final byte TICK_30MIN = 0x0e; // unsure
|
||||
public static final byte FIND_PHONE_STOP = 0x0f;
|
||||
public static final byte MUSIC_CONTROL = (byte) 0xfe;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2;
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
@ -34,6 +34,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@ -54,22 +56,24 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLift;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2FWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.DoNotDisturb;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample;
|
||||
@ -84,6 +88,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
@ -97,19 +102,21 @@ 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.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2NotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions.StopNotificationAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchSportsSummaryOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.InitOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.actions.StopNotificationAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchActivityOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchSportsSummaryOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.InitOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.UpdateFirmwareOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
@ -134,7 +141,7 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.VI
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefIntValue;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.getNotificationPrefStringValue;
|
||||
|
||||
public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
// We introduce key press counter for notification purposes
|
||||
private static int currentButtonActionId = 0;
|
||||
@ -142,20 +149,21 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
private static long currentButtonPressTime = 0;
|
||||
private static long currentButtonTimerActivationTime = 0;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MiBand2Support.class);
|
||||
private final DeviceInfoProfile<MiBand2Support> deviceInfoProfile;
|
||||
private final HeartRateProfile<MiBand2Support> heartRateProfile;
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HuamiSupport.class);
|
||||
private final DeviceInfoProfile<HuamiSupport> deviceInfoProfile;
|
||||
private final HeartRateProfile<HuamiSupport> heartRateProfile;
|
||||
private final IntentListener mListener = new IntentListener() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
public void notify(Intent intent) {
|
||||
String s = intent.getAction();
|
||||
if (s.equals(DeviceInfoProfile.ACTION_DEVICE_INFO)) {
|
||||
if (DeviceInfoProfile.ACTION_DEVICE_INFO.equals(s)) {
|
||||
handleDeviceInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfo) intent.getParcelableExtra(DeviceInfoProfile.EXTRA_DEVICE_INFO));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BluetoothGattCharacteristic characteristicHRControlPoint;
|
||||
private BluetoothGattCharacteristic characteristicHRControlPoint;
|
||||
protected BluetoothGattCharacteristic characteristicChunked;
|
||||
|
||||
private boolean needsAuth;
|
||||
private volatile boolean telephoneRinging;
|
||||
@ -168,11 +176,15 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
private RealtimeSamplesSupport realtimeSamplesSupport;
|
||||
private boolean alarmClockRinging;
|
||||
|
||||
public MiBand2Support() {
|
||||
private boolean isMusicAppStarted = false;
|
||||
private MusicSpec bufferMusicSpec = null;
|
||||
private MusicStateSpec bufferMusicStateSpec = null;
|
||||
|
||||
public HuamiSupport() {
|
||||
this(LOG);
|
||||
}
|
||||
|
||||
public MiBand2Support(Logger logger) {
|
||||
public HuamiSupport(Logger logger) {
|
||||
super(logger);
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE);
|
||||
@ -183,25 +195,13 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
addSupportedService(MiBandService.UUID_SERVICE_MIBAND_SERVICE);
|
||||
addSupportedService(MiBandService.UUID_SERVICE_MIBAND2_SERVICE);
|
||||
addSupportedService(MiBand2Service.UUID_SERVICE_FIRMWARE_SERVICE);
|
||||
addSupportedService(HuamiService.UUID_SERVICE_FIRMWARE_SERVICE);
|
||||
|
||||
deviceInfoProfile = new DeviceInfoProfile<>(this);
|
||||
deviceInfoProfile.addListener(mListener);
|
||||
addSupportedProfile(deviceInfoProfile);
|
||||
heartRateProfile = new HeartRateProfile<>(this);
|
||||
addSupportedProfile(heartRateProfile);
|
||||
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(DeviceInfoProfile.ACTION_DEVICE_INFO);
|
||||
intentFilter.addAction(DeviceService.ACTION_MIBAND2_AUTH);
|
||||
broadcastManager.registerReceiver(mReceiver, intentFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
|
||||
broadcastManager.unregisterReceiver(mReceiver);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -209,8 +209,13 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
try {
|
||||
boolean authenticate = needsAuth;
|
||||
needsAuth = false;
|
||||
new InitOperation(authenticate, this, builder).perform();
|
||||
byte authFlags = HuamiService.AUTH_BYTE;
|
||||
if (gbDevice.getType() == DeviceType.MIBAND3) {
|
||||
authFlags = 0x00;
|
||||
}
|
||||
new InitOperation(authenticate, authFlags, this, builder).perform();
|
||||
characteristicHRControlPoint = getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_CONTROL_POINT);
|
||||
characteristicChunked = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_CHUNKEDTRANSFER);
|
||||
} catch (IOException e) {
|
||||
GB.toast(getContext(), "Initializing Mi Band 2 failed", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
}
|
||||
@ -219,7 +224,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
/**
|
||||
* Returns the given date/time (calendar) as a byte sequence, suitable for sending to the
|
||||
* Mi Band 2 (or derivative). The band appears to not handle DST offsets, so we simply add this
|
||||
* Mi Band 2 (or derivative). The band appears to not handle DST offsets, so we simply add this
|
||||
* to the timezone.
|
||||
* @param calendar
|
||||
* @param precision
|
||||
@ -246,19 +251,19 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public MiBand2Support setCurrentTimeWithService(TransactionBuilder builder) {
|
||||
public HuamiSupport setCurrentTimeWithService(TransactionBuilder builder) {
|
||||
GregorianCalendar now = BLETypeConversions.createCalendar();
|
||||
byte[] bytes = getTimeBytes(now, TimeUnit.SECONDS);
|
||||
builder.write(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_CURRENT_TIME), bytes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MiBand2Support setLowLatency(TransactionBuilder builder) {
|
||||
public HuamiSupport setLowLatency(TransactionBuilder builder) {
|
||||
// TODO: low latency?
|
||||
return this;
|
||||
}
|
||||
|
||||
public MiBand2Support setHighLatency(TransactionBuilder builder) {
|
||||
public HuamiSupport setHighLatency(TransactionBuilder builder) {
|
||||
// TODO: high latency?
|
||||
return this;
|
||||
}
|
||||
@ -276,18 +281,18 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
// MB2: AVL
|
||||
// TODO: tear down the notifications on quit
|
||||
public MiBand2Support enableNotifications(TransactionBuilder builder, boolean enable) {
|
||||
public HuamiSupport enableNotifications(TransactionBuilder builder, boolean enable) {
|
||||
builder.notify(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_NOTIFICATION), enable);
|
||||
builder.notify(getCharacteristic(GattService.UUID_SERVICE_CURRENT_TIME), enable);
|
||||
// Notify CHARACTERISTIC9 to receive random auth code
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_AUTH), enable);
|
||||
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_AUTH), enable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MiBand2Support enableFurtherNotifications(TransactionBuilder builder, boolean enable) {
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), enable);
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable);
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_DEVICEEVENT), enable);
|
||||
public HuamiSupport enableFurtherNotifications(TransactionBuilder builder, boolean enable) {
|
||||
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), enable);
|
||||
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_6_BATTERY_INFO), enable);
|
||||
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_DEVICEEVENT), enable);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -303,7 +308,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return super.connect();
|
||||
}
|
||||
|
||||
private MiBand2Support sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
||||
private HuamiSupport sendDefaultNotification(TransactionBuilder builder, SimpleNotification simpleNotification, short repeat, BtLEAction extraAction) {
|
||||
LOG.info("Sending notification to MiBand: (" + repeat + " times)");
|
||||
NotificationStrategy strategy = getNotificationStrategy();
|
||||
for (short i = 0; i < repeat; i++) {
|
||||
@ -323,7 +328,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
* @param extraAction an extra action to be executed after every vibration and flash sequence. Allows to abort the repetition, for example.
|
||||
* @param builder
|
||||
*/
|
||||
private MiBand2Support sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
|
||||
private HuamiSupport sendCustomNotification(VibrationProfile vibrationProfile, SimpleNotification simpleNotification, int flashTimes, int flashColour, int originalColour, long flashDuration, BtLEAction extraAction, TransactionBuilder builder) {
|
||||
getNotificationStrategy().sendCustomNotification(vibrationProfile, simpleNotification, flashTimes, flashColour, originalColour, flashDuration, extraAction, builder);
|
||||
LOG.info("Sending notification to MiBand");
|
||||
return this;
|
||||
@ -348,14 +353,14 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
private static final byte[] startHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 1};
|
||||
private static final byte[] stopHeartMeasurementContinuous = new byte[]{0x15, MiBandService.COMMAND_SET__HR_CONTINUOUS, 0};
|
||||
|
||||
private MiBand2Support requestBatteryInfo(TransactionBuilder builder) {
|
||||
private HuamiSupport requestBatteryInfo(TransactionBuilder builder) {
|
||||
LOG.debug("Requesting Battery Info!");
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO);
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_6_BATTERY_INFO);
|
||||
builder.read(characteristic);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MiBand2Support requestDeviceInfo(TransactionBuilder builder) {
|
||||
public HuamiSupport requestDeviceInfo(TransactionBuilder builder) {
|
||||
LOG.debug("Requesting Device Info!");
|
||||
deviceInfoProfile.requestDeviceInfo(builder);
|
||||
return this;
|
||||
@ -368,16 +373,16 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
* @return
|
||||
*/
|
||||
|
||||
private MiBand2Support setFitnessGoal(TransactionBuilder transaction) {
|
||||
private HuamiSupport setFitnessGoal(TransactionBuilder transaction) {
|
||||
LOG.info("Attempting to set Fitness Goal...");
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
if (characteristic != null) {
|
||||
int fitnessGoal = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, 10000);
|
||||
byte[] bytes = ArrayUtils.addAll(
|
||||
MiBand2Service.COMMAND_SET_FITNESS_GOAL_START,
|
||||
HuamiService.COMMAND_SET_FITNESS_GOAL_START,
|
||||
BLETypeConversions.fromUint16(fitnessGoal));
|
||||
bytes = ArrayUtils.addAll(bytes,
|
||||
MiBand2Service.COMMAND_SET_FITNESS_GOAL_END);
|
||||
HuamiService.COMMAND_SET_FITNESS_GOAL_END);
|
||||
transaction.write(characteristic, bytes);
|
||||
} else {
|
||||
LOG.info("Unable to set Fitness Goal");
|
||||
@ -392,8 +397,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
* @return
|
||||
*/
|
||||
|
||||
private MiBand2Support setUserInfo(TransactionBuilder transaction) {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
private HuamiSupport setUserInfo(TransactionBuilder transaction) {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
if (characteristic == null) {
|
||||
return this;
|
||||
}
|
||||
@ -425,7 +430,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
// FIXME: Do encoding like in PebbleProtocol, this is ugly
|
||||
byte bytes[] = new byte[]{
|
||||
MiBand2Service.COMMAND_SET_USERINFO,
|
||||
HuamiService.COMMAND_SET_USERINFO,
|
||||
0,
|
||||
0,
|
||||
(byte) (birth_year & 0xff),
|
||||
@ -453,18 +458,18 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
private MiBand2Support setWearLocation(TransactionBuilder builder) {
|
||||
private HuamiSupport setWearLocation(TransactionBuilder builder) {
|
||||
LOG.info("Attempting to set wear location...");
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_8_USER_SETTINGS);
|
||||
if (characteristic != null) {
|
||||
builder.notify(characteristic, true);
|
||||
int location = MiBandCoordinator.getWearLocation(getDevice().getAddress());
|
||||
switch (location) {
|
||||
case 0: // left hand
|
||||
builder.write(characteristic, MiBand2Service.WEAR_LOCATION_LEFT_WRIST);
|
||||
builder.write(characteristic, HuamiService.WEAR_LOCATION_LEFT_WRIST);
|
||||
break;
|
||||
case 1: // right hand
|
||||
builder.write(characteristic, MiBand2Service.WEAR_LOCATION_RIGHT_WRIST);
|
||||
builder.write(characteristic, HuamiService.WEAR_LOCATION_RIGHT_WRIST);
|
||||
break;
|
||||
}
|
||||
builder.notify(characteristic, false); // TODO: this should actually be in some kind of finally-block in the queue. It should also be sent asynchronously after the notifications have completely arrived and processed.
|
||||
@ -512,27 +517,27 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
*
|
||||
* @param builder
|
||||
*/
|
||||
private MiBand2Support setHeartrateSleepSupport(TransactionBuilder builder) {
|
||||
private HuamiSupport setHeartrateSleepSupport(TransactionBuilder builder) {
|
||||
final boolean enableHrSleepSupport = MiBandCoordinator.getHeartrateSleepSupport(getDevice().getAddress());
|
||||
if (characteristicHRControlPoint != null) {
|
||||
builder.notify(characteristicHRControlPoint, true);
|
||||
if (enableHrSleepSupport) {
|
||||
LOG.info("Enabling heartrate sleep support...");
|
||||
builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_ENABLE_HR_SLEEP_MEASUREMENT);
|
||||
builder.write(characteristicHRControlPoint, HuamiService.COMMAND_ENABLE_HR_SLEEP_MEASUREMENT);
|
||||
} else {
|
||||
LOG.info("Disabling heartrate sleep support...");
|
||||
builder.write(characteristicHRControlPoint, MiBand2Service.COMMAND_DISABLE_HR_SLEEP_MEASUREMENT);
|
||||
builder.write(characteristicHRControlPoint, HuamiService.COMMAND_DISABLE_HR_SLEEP_MEASUREMENT);
|
||||
}
|
||||
builder.notify(characteristicHRControlPoint, false); // TODO: this should actually be in some kind of finally-block in the queue. It should also be sent asynchronously after the notifications have completely arrived and processed.
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setHeartrateMeasurementInterval(TransactionBuilder builder, int minutes) {
|
||||
private HuamiSupport setHeartrateMeasurementInterval(TransactionBuilder builder, int minutes) {
|
||||
if (characteristicHRControlPoint != null) {
|
||||
builder.notify(characteristicHRControlPoint, true);
|
||||
LOG.info("Setting heart rate measurement interval to " + minutes + " minutes");
|
||||
builder.write(characteristicHRControlPoint, new byte[]{MiBand2Service.COMMAND_SET_PERIODIC_HR_MEASUREMENT_INTERVAL, (byte) minutes});
|
||||
builder.write(characteristicHRControlPoint, new byte[]{HuamiService.COMMAND_SET_PERIODIC_HR_MEASUREMENT_INTERVAL, (byte) minutes});
|
||||
builder.notify(characteristicHRControlPoint, false); // TODO: this should actually be in some kind of finally-block in the queue. It should also be sent asynchronously after the notifications have completely arrived and processed.
|
||||
}
|
||||
return this;
|
||||
@ -608,7 +613,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
try {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION);
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION);
|
||||
TransactionBuilder builder = performInitialized("Set alarm");
|
||||
boolean anyAlarmEnabled = false;
|
||||
for (Alarm alarm : alarms) {
|
||||
@ -632,9 +637,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
onAlarmClock(notificationSpec);
|
||||
return;
|
||||
}
|
||||
int alertLevel = MiBand2Service.ALERT_LEVEL_MESSAGE;
|
||||
int alertLevel = HuamiService.ALERT_LEVEL_MESSAGE;
|
||||
if (notificationSpec.type == NotificationType.UNKNOWN) {
|
||||
alertLevel = MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY;
|
||||
alertLevel = HuamiService.ALERT_LEVEL_VIBRATE_ONLY;
|
||||
}
|
||||
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext()).trim();
|
||||
String origin = notificationSpec.type.getGenericType();
|
||||
@ -652,7 +657,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
};
|
||||
String message = NotificationUtils.getPreferredTextFor(notificationSpec, 40, 40, getContext());
|
||||
SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.HighPriorityAlert, notificationSpec.type);
|
||||
performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, simpleNotification, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, abortAction);
|
||||
performPreferredNotification("alarm clock ringing", MiBandConst.ORIGIN_ALARM_CLOCK, simpleNotification, HuamiService.ALERT_LEVEL_VIBRATE_ONLY, abortAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -685,7 +690,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
};
|
||||
String message = NotificationUtils.getPreferredTextFor(callSpec);
|
||||
SimpleNotification simpleNotification = new SimpleNotification(message, AlertCategory.IncomingCall, null);
|
||||
performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, simpleNotification, MiBand2Service.ALERT_LEVEL_PHONE_CALL, abortAction);
|
||||
performPreferredNotification("incoming call", MiBandConst.ORIGIN_INCOMING_CALL, simpleNotification, HuamiService.ALERT_LEVEL_PHONE_CALL, abortAction);
|
||||
} else if ((callSpec.command == CallSpec.CALL_START) || (callSpec.command == CallSpec.CALL_END)) {
|
||||
telephoneRinging = false;
|
||||
stopCurrentNotification();
|
||||
@ -718,12 +723,113 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
// not supported
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
||||
if (!coordinator.supportsMusicInfo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferMusicStateSpec != stateSpec) {
|
||||
bufferMusicStateSpec = stateSpec;
|
||||
sendMusicStateToDevice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||
// not supported
|
||||
DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(gbDevice);
|
||||
if (!coordinator.supportsMusicInfo()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bufferMusicSpec != musicSpec) {
|
||||
bufferMusicSpec = musicSpec;
|
||||
if (isMusicAppStarted) {
|
||||
sendMusicStateToDevice();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void sendMusicStateToDevice() {
|
||||
|
||||
|
||||
if (characteristicChunked == null) {
|
||||
return;
|
||||
}
|
||||
if (bufferMusicSpec == null || bufferMusicStateSpec == null) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("send dummy playback info to enable music controls");
|
||||
writeToChunked(builder, 3, new byte[]{1, 0, 1, 0, 0, 0, 1, 0});
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to send dummy music controls");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
byte flags = 0x00;
|
||||
flags |= 0x01;
|
||||
int length = 8;
|
||||
if (bufferMusicSpec.track != null && bufferMusicSpec.track.getBytes().length > 0) {
|
||||
length += bufferMusicSpec.track.getBytes().length + 1;
|
||||
flags |= 0x02;
|
||||
}
|
||||
if (bufferMusicSpec.album != null && bufferMusicSpec.album.getBytes().length > 0) {
|
||||
length += bufferMusicSpec.album.getBytes().length + 1;
|
||||
flags |= 0x04;
|
||||
}
|
||||
if (bufferMusicSpec.artist != null && bufferMusicSpec.artist.getBytes().length > 0) {
|
||||
length += bufferMusicSpec.artist.getBytes().length + 1;
|
||||
flags |= 0x08;
|
||||
}
|
||||
|
||||
|
||||
// LOG.info("Music flags are: " + (flags & 0xff));
|
||||
try {
|
||||
ByteBuffer buf = ByteBuffer.allocate(length);
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.put(flags);
|
||||
byte state;
|
||||
switch (bufferMusicStateSpec.state) {
|
||||
case MusicStateSpec.STATE_PLAYING:
|
||||
state = 1;
|
||||
break;
|
||||
default:
|
||||
state = 0;
|
||||
}
|
||||
|
||||
buf.put(state);
|
||||
buf.put(new byte[]{0x1, 0x0, 0x0, 0x0}); //unknown
|
||||
buf.put(new byte[]{0x1,0x0}); //show track
|
||||
// buf.put(new byte[]{0x1,0x1}); //show album
|
||||
|
||||
|
||||
if (bufferMusicSpec.track != null && bufferMusicSpec.track.getBytes().length > 0) {
|
||||
buf.put(bufferMusicSpec.track.getBytes());
|
||||
buf.put((byte) 0);
|
||||
}
|
||||
if (bufferMusicSpec.album != null && bufferMusicSpec.album.getBytes().length > 0) {
|
||||
buf.put(bufferMusicSpec.album.getBytes());
|
||||
buf.put((byte) 0);
|
||||
}
|
||||
if (bufferMusicSpec.artist != null && bufferMusicSpec.artist.getBytes().length > 0) {
|
||||
buf.put(bufferMusicSpec.artist.getBytes());
|
||||
buf.put((byte) 0);
|
||||
}
|
||||
|
||||
|
||||
TransactionBuilder builder = performInitialized("send playback info");
|
||||
writeToChunked(builder, 3, buf.array());
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Unable to send playback state");
|
||||
}
|
||||
|
||||
// LOG.info("Sent music: " + bufferMusicSpec.toString() + " " + bufferMusicStateSpec.toString());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -737,8 +843,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public MiBand2Support sendReboot(TransactionBuilder builder) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_FIRMWARE), new byte[] { MiBand2Service.COMMAND_FIRMWARE_REBOOT});
|
||||
public HuamiSupport sendReboot(TransactionBuilder builder) {
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_FIRMWARE), new byte[] { HuamiService.COMMAND_FIRMWARE_REBOOT});
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -817,9 +923,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized(enable ? "Enabling realtime steps notifications" : "Disabling realtime steps notifications");
|
||||
if (enable) {
|
||||
builder.read(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_7_REALTIME_STEPS));
|
||||
builder.read(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_7_REALTIME_STEPS));
|
||||
}
|
||||
builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_7_REALTIME_STEPS), enable);
|
||||
builder.notify(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_7_REALTIME_STEPS), enable);
|
||||
builder.queue(getQueue());
|
||||
enableRealtimeSamplesTimer(enable);
|
||||
} catch (IOException e) {
|
||||
@ -920,7 +1026,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
LOG.info("Sending " + requiredButtonPressMessage + " with button_id " + currentButtonActionId);
|
||||
this.getContext().getApplicationContext().sendBroadcast(in);
|
||||
if (prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_VIBRATE, false)) {
|
||||
performPreferredNotification(null, null, null, MiBand2Service.ALERT_LEVEL_VIBRATE_ONLY, null);
|
||||
performPreferredNotification(null, null, null, HuamiService.ALERT_LEVEL_VIBRATE_ONLY, null);
|
||||
}
|
||||
|
||||
currentButtonActionId = 0;
|
||||
@ -930,7 +1036,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
|
||||
public void handleDeviceEvent(byte[] value) {
|
||||
if (value == null || value.length != 1) {
|
||||
if (value == null || value.length == 0) {
|
||||
return;
|
||||
}
|
||||
GBDeviceEventCallControl callCmd = new GBDeviceEventCallControl();
|
||||
@ -981,6 +1087,44 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
|
||||
evaluateGBDeviceEvent(findPhoneEvent);
|
||||
break;
|
||||
case HuamiDeviceEvent.MUSIC_CONTROL:
|
||||
LOG.info("got music control");
|
||||
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();
|
||||
|
||||
switch (value[1]) {
|
||||
case 0:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAY;
|
||||
break;
|
||||
case 1:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PAUSE;
|
||||
break;
|
||||
case 3:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.NEXT;
|
||||
break;
|
||||
case 4:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS;
|
||||
break;
|
||||
case 5:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
|
||||
break;
|
||||
case 6:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
|
||||
break;
|
||||
case (byte) 224:
|
||||
LOG.info("Music app started");
|
||||
isMusicAppStarted = true;
|
||||
sendMusicStateToDevice();
|
||||
break;
|
||||
case (byte) 225:
|
||||
LOG.info("Music app terminated");
|
||||
isMusicAppStarted = false;
|
||||
break;
|
||||
default:
|
||||
LOG.info("unhandled music control event " + value[1]);
|
||||
return;
|
||||
}
|
||||
evaluateGBDeviceEvent(deviceEventMusicControl);
|
||||
break;
|
||||
default:
|
||||
LOG.warn("unhandled event " + value[0]);
|
||||
}
|
||||
@ -990,7 +1134,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("acknowledge find phone");
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), AmazfitBipService.COMMAND_ACK_FIND_PHONE_IN_PROGRESS);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), AmazfitBipService.COMMAND_ACK_FIND_PHONE_IN_PROGRESS);
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending current weather", ex);
|
||||
@ -1051,7 +1195,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
super.onCharacteristicChanged(gatt, characteristic);
|
||||
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
if (HuamiService.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
handleBatteryInfo(characteristic.getValue(), BluetoothGatt.GATT_SUCCESS);
|
||||
return true;
|
||||
} else if (MiBandService.UUID_CHARACTERISTIC_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
@ -1060,14 +1204,14 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
handleHeartrate(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
LOG.info("AUTHENTICATION?? " + characteristicUUID);
|
||||
logMessageContent(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_DEVICEEVENT.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_DEVICEEVENT.equals(characteristicUUID)) {
|
||||
handleDeviceEvent(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_7_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_7_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
handleRealtimeSteps(characteristic.getValue());
|
||||
return true;
|
||||
} else {
|
||||
@ -1087,16 +1231,16 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
if (GattCharacteristic.UUID_CHARACTERISTIC_GAP_DEVICE_NAME.equals(characteristicUUID)) {
|
||||
handleDeviceName(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_6_BATTERY_INFO.equals(characteristicUUID)) {
|
||||
handleBatteryInfo(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (GattCharacteristic.UUID_CHARACTERISTIC_HEART_RATE_MEASUREMENT.equals(characteristicUUID)) {
|
||||
logHeartrate(characteristic.getValue(), status);
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_7_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_7_REALTIME_STEPS.equals(characteristicUUID)) {
|
||||
handleRealtimeSteps(characteristic.getValue());
|
||||
return true;
|
||||
} else if (MiBand2Service.UUID_CHARACTERISTIC_DEVICEEVENT.equals(characteristicUUID)) {
|
||||
} else if (HuamiService.UUID_CHARACTERISTIC_DEVICEEVENT.equals(characteristicUUID)) {
|
||||
handleDeviceEvent(characteristic.getValue());
|
||||
return true;
|
||||
} else {
|
||||
@ -1111,7 +1255,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
public boolean onCharacteristicWrite(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic, int status) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (MiBand2Service.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
if (HuamiService.UUID_CHARACTERISTIC_AUTH.equals(characteristicUUID)) {
|
||||
LOG.info("KEY AES SEND");
|
||||
logMessageContent(characteristic.getValue());
|
||||
return true;
|
||||
@ -1282,8 +1426,10 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
LOG.warn("Device info: " + info);
|
||||
versionCmd.hwVersion = info.getHardwareRevision();
|
||||
// versionCmd.fwVersion = info.getFirmwareRevision(); // always null
|
||||
versionCmd.fwVersion = info.getSoftwareRevision();
|
||||
versionCmd.fwVersion = info.getFirmwareRevision();
|
||||
if (versionCmd.fwVersion == null) {
|
||||
versionCmd.fwVersion = info.getSoftwareRevision();
|
||||
}
|
||||
if (versionCmd.fwVersion != null && versionCmd.fwVersion.length() > 0 && versionCmd.fwVersion.charAt(0) == 'V') {
|
||||
versionCmd.fwVersion = versionCmd.fwVersion.substring(1);
|
||||
}
|
||||
@ -1305,8 +1451,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
* Fetch the events from the android device calendars and set the alarms on the miband.
|
||||
* @param builder
|
||||
*/
|
||||
private MiBand2Support sendCalendarEvents(TransactionBuilder builder) {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION);
|
||||
private HuamiSupport sendCalendarEvents(TransactionBuilder builder) {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION);
|
||||
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
int availableSlots = prefs.getInt(MiBandConst.PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR, 0);
|
||||
@ -1395,55 +1541,55 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
}
|
||||
|
||||
private MiBand2Support setDateDisplay(TransactionBuilder builder) {
|
||||
private HuamiSupport setDateDisplay(TransactionBuilder builder) {
|
||||
DateTimeDisplay dateTimeDisplay = HuamiCoordinator.getDateDisplay(getContext());
|
||||
LOG.info("Setting date display to " + dateTimeDisplay);
|
||||
switch (dateTimeDisplay) {
|
||||
case TIME:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.DATEFORMAT_TIME);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.DATEFORMAT_TIME);
|
||||
break;
|
||||
case DATE_TIME:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.DATEFORMAT_DATE_TIME);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.DATEFORMAT_DATE_TIME);
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setTimeFormat(TransactionBuilder builder) {
|
||||
private HuamiSupport setTimeFormat(TransactionBuilder builder) {
|
||||
boolean is24Format = DateFormat.is24HourFormat(getContext());
|
||||
LOG.info("Setting 24h time format to " + is24Format);
|
||||
if (is24Format) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.DATEFORMAT_TIME_24_HOURS);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.DATEFORMAT_TIME_24_HOURS);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.DATEFORMAT_TIME_12_HOURS);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.DATEFORMAT_TIME_12_HOURS);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setGoalNotification(TransactionBuilder builder) {
|
||||
private HuamiSupport setGoalNotification(TransactionBuilder builder) {
|
||||
boolean enable = HuamiCoordinator.getGoalNotification();
|
||||
LOG.info("Setting goal notification to " + enable);
|
||||
if (enable) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_GOAL_NOTIFICATION);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_ENABLE_GOAL_NOTIFICATION);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_GOAL_NOTIFICATION);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISABLE_GOAL_NOTIFICATION);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setActivateDisplayOnLiftWrist(TransactionBuilder builder) {
|
||||
private HuamiSupport setActivateDisplayOnLiftWrist(TransactionBuilder builder) {
|
||||
ActivateDisplayOnLift displayOnLift = HuamiCoordinator.getActivateDisplayOnLiftWrist(getContext());
|
||||
LOG.info("Setting activate display on lift wrist to " + displayOnLift);
|
||||
|
||||
switch (displayOnLift) {
|
||||
case ON:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST);
|
||||
break;
|
||||
case OFF:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST);
|
||||
break;
|
||||
case SCHEDULED:
|
||||
byte[] cmd = MiBand2Service.COMMAND_SCHEDULE_DISPLAY_ON_LIFT_WRIST.clone();
|
||||
byte[] cmd = HuamiService.COMMAND_SCHEDULE_DISPLAY_ON_LIFT_WRIST.clone();
|
||||
|
||||
Calendar calendar = GregorianCalendar.getInstance();
|
||||
|
||||
@ -1457,81 +1603,81 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
cmd[6] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
cmd[7] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), cmd);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), cmd);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected MiBand2Support setDisplayItems(TransactionBuilder builder) {
|
||||
protected HuamiSupport setDisplayItems(TransactionBuilder builder) {
|
||||
Set<String> pages = HuamiCoordinator.getDisplayItems();
|
||||
LOG.info("Setting display items to " + (pages == null ? "none" : pages));
|
||||
|
||||
byte[] data = MiBand2Service.COMMAND_CHANGE_SCREENS.clone();
|
||||
byte[] data = HuamiService.COMMAND_CHANGE_SCREENS.clone();
|
||||
|
||||
if (pages != null) {
|
||||
if (pages.contains(MiBandConst.PREF_MI2_DISPLAY_ITEM_STEPS)) {
|
||||
data[MiBand2Service.SCREEN_CHANGE_BYTE] |= MiBand2Service.DISPLAY_ITEM_BIT_STEPS;
|
||||
data[HuamiService.SCREEN_CHANGE_BYTE] |= HuamiService.DISPLAY_ITEM_BIT_STEPS;
|
||||
}
|
||||
if (pages.contains(MiBandConst.PREF_MI2_DISPLAY_ITEM_DISTANCE)) {
|
||||
data[MiBand2Service.SCREEN_CHANGE_BYTE] |= MiBand2Service.DISPLAY_ITEM_BIT_DISTANCE;
|
||||
data[HuamiService.SCREEN_CHANGE_BYTE] |= HuamiService.DISPLAY_ITEM_BIT_DISTANCE;
|
||||
}
|
||||
if (pages.contains(MiBandConst.PREF_MI2_DISPLAY_ITEM_CALORIES)) {
|
||||
data[MiBand2Service.SCREEN_CHANGE_BYTE] |= MiBand2Service.DISPLAY_ITEM_BIT_CALORIES;
|
||||
data[HuamiService.SCREEN_CHANGE_BYTE] |= HuamiService.DISPLAY_ITEM_BIT_CALORIES;
|
||||
}
|
||||
if (pages.contains(MiBandConst.PREF_MI2_DISPLAY_ITEM_HEART_RATE)) {
|
||||
data[MiBand2Service.SCREEN_CHANGE_BYTE] |= MiBand2Service.DISPLAY_ITEM_BIT_HEART_RATE;
|
||||
data[HuamiService.SCREEN_CHANGE_BYTE] |= HuamiService.DISPLAY_ITEM_BIT_HEART_RATE;
|
||||
}
|
||||
if (pages.contains(MiBandConst.PREF_MI2_DISPLAY_ITEM_BATTERY)) {
|
||||
data[MiBand2Service.SCREEN_CHANGE_BYTE] |= MiBand2Service.DISPLAY_ITEM_BIT_BATTERY;
|
||||
data[HuamiService.SCREEN_CHANGE_BYTE] |= HuamiService.DISPLAY_ITEM_BIT_BATTERY;
|
||||
}
|
||||
}
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setRotateWristToSwitchInfo(TransactionBuilder builder) {
|
||||
private HuamiSupport setRotateWristToSwitchInfo(TransactionBuilder builder) {
|
||||
boolean enable = HuamiCoordinator.getRotateWristToSwitchInfo();
|
||||
LOG.info("Setting rotate wrist to cycle info to " + enable);
|
||||
if (enable) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_ROTATE_WRIST_TO_SWITCH_INFO);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_ENABLE_ROTATE_WRIST_TO_SWITCH_INFO);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_ROTATE_WRIST_TO_SWITCH_INFO);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISABLE_ROTATE_WRIST_TO_SWITCH_INFO);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setDisplayCaller(TransactionBuilder builder) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_DISPLAY_CALLER);
|
||||
private HuamiSupport setDisplayCaller(TransactionBuilder builder) {
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_ENABLE_DISPLAY_CALLER);
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setDoNotDisturb(TransactionBuilder builder) {
|
||||
private HuamiSupport setDoNotDisturb(TransactionBuilder builder) {
|
||||
DoNotDisturb doNotDisturb = HuamiCoordinator.getDoNotDisturb(getContext());
|
||||
LOG.info("Setting do not disturb to " + doNotDisturb);
|
||||
switch (doNotDisturb) {
|
||||
case OFF:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DO_NOT_DISTURB_OFF);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DO_NOT_DISTURB_OFF);
|
||||
break;
|
||||
case AUTOMATIC:
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DO_NOT_DISTURB_AUTOMATIC);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DO_NOT_DISTURB_AUTOMATIC);
|
||||
break;
|
||||
case SCHEDULED:
|
||||
byte[] data = MiBand2Service.COMMAND_DO_NOT_DISTURB_SCHEDULED.clone();
|
||||
byte[] data = HuamiService.COMMAND_DO_NOT_DISTURB_SCHEDULED.clone();
|
||||
|
||||
Calendar calendar = GregorianCalendar.getInstance();
|
||||
|
||||
Date start = HuamiCoordinator.getDoNotDisturbStart();
|
||||
calendar.setTime(start);
|
||||
data[MiBand2Service.DND_BYTE_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.DND_BYTE_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.DND_BYTE_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.DND_BYTE_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
Date end = HuamiCoordinator.getDoNotDisturbEnd();
|
||||
calendar.setTime(end);
|
||||
data[MiBand2Service.DND_BYTE_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.DND_BYTE_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.DND_BYTE_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.DND_BYTE_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1539,15 +1685,15 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setInactivityWarnings(TransactionBuilder builder) {
|
||||
private HuamiSupport setInactivityWarnings(TransactionBuilder builder) {
|
||||
boolean enable = HuamiCoordinator.getInactivityWarnings();
|
||||
LOG.info("Setting inactivity warnings to " + enable);
|
||||
|
||||
if (enable) {
|
||||
byte[] data = MiBand2Service.COMMAND_ENABLE_INACTIVITY_WARNINGS.clone();
|
||||
byte[] data = HuamiService.COMMAND_ENABLE_INACTIVITY_WARNINGS.clone();
|
||||
|
||||
int threshold = HuamiCoordinator.getInactivityWarningsThreshold();
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_THRESHOLD] = (byte) threshold;
|
||||
data[HuamiService.INACTIVITY_WARNINGS_THRESHOLD] = (byte) threshold;
|
||||
|
||||
Calendar calendar = GregorianCalendar.getInstance();
|
||||
|
||||
@ -1560,50 +1706,78 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport {
|
||||
|
||||
// The first interval always starts when the warnings interval starts
|
||||
calendar.setTime(intervalStart);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
if(enableDnd) {
|
||||
// The first interval ends when the dnd interval starts
|
||||
calendar.setTime(dndStart);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
// The second interval starts when the dnd interval ends
|
||||
calendar.setTime(dndEnd);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_2_START_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_2_START_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
|
||||
// ... and it ends when the warnings interval ends
|
||||
calendar.setTime(intervalEnd);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_2_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_2_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_2_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
} else {
|
||||
// No Dnd, use the first interval
|
||||
calendar.setTime(intervalEnd);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[MiBand2Service.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_END_HOURS] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
|
||||
data[HuamiService.INACTIVITY_WARNINGS_INTERVAL_1_END_MINUTES] = (byte) calendar.get(Calendar.MINUTE);
|
||||
}
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), data);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_INACTIVITY_WARNINGS);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISABLE_INACTIVITY_WARNINGS);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private MiBand2Support setDistanceUnit(TransactionBuilder builder) {
|
||||
private HuamiSupport setDistanceUnit(TransactionBuilder builder) {
|
||||
MiBandConst.DistanceUnit unit = HuamiCoordinator.getDistanceUnit();
|
||||
LOG.info("Setting distance unit to " + unit);
|
||||
if (unit == MiBandConst.DistanceUnit.METRIC) {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISTANCE_UNIT_METRIC);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISTANCE_UNIT_METRIC);
|
||||
} else {
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISTANCE_UNIT_IMPERIAL);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), HuamiService.COMMAND_DISTANCE_UNIT_IMPERIAL);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void writeToChunked(TransactionBuilder builder, int type, byte[] data) {
|
||||
final int MAX_CHUNKLENGTH = 17;
|
||||
int remaining = data.length;
|
||||
byte count = 0;
|
||||
while (remaining > 0) {
|
||||
int copybytes = Math.min(remaining, MAX_CHUNKLENGTH);
|
||||
byte[] chunk = new byte[copybytes + 3];
|
||||
|
||||
byte flags = 0;
|
||||
if (remaining <= MAX_CHUNKLENGTH) {
|
||||
flags |= 0x80; // last chunk
|
||||
if (count == 0) {
|
||||
flags |= 0x40; // weird but true
|
||||
}
|
||||
} else if (count > 0) {
|
||||
flags |= 0x40; // consecutive chunk
|
||||
}
|
||||
|
||||
chunk[0] = 0;
|
||||
chunk[1] = (byte) (flags | type);
|
||||
chunk[2] = (byte) (count & 0xff);
|
||||
|
||||
System.arraycopy(data, count++ * MAX_CHUNKLENGTH, chunk, 3, copybytes);
|
||||
builder.write(characteristicChunked, chunk);
|
||||
remaining -= copybytes;
|
||||
}
|
||||
}
|
||||
|
||||
public void phase2Initialize(TransactionBuilder builder) {
|
||||
LOG.info("phase2Initialize...");
|
||||
requestBatteryInfo(builder);
|
@ -14,12 +14,12 @@
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.actions;
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.actions;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.AbortTransactionAction;
|
||||
|
||||
public abstract class StopNotificationAction extends AbortTransactionAction {
|
||||
@ -34,7 +34,7 @@ public abstract class StopNotificationAction extends AbortTransactionAction {
|
||||
public boolean run(BluetoothGatt gatt) {
|
||||
if (!super.run(gatt)) {
|
||||
// send a signal to stop the vibration
|
||||
alertLevelCharacteristic.setValue(new byte[]{MiBand2Service.ALERT_LEVEL_NONE});
|
||||
alertLevelCharacteristic.setValue(new byte[]{HuamiService.ALERT_LEVEL_NONE});
|
||||
gatt.writeCharacteristic(alertLevelCharacteristic);
|
||||
return false;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, AndrewH, Carsten Pfeiffer
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, AndrewH, Carsten Pfeiffer,
|
||||
szilardx
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -21,7 +22,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
@ -44,7 +48,7 @@ public class ActivityDetailsParser {
|
||||
public static final BigDecimal HUAMI_TO_DECIMAL_DEGREES_DIVISOR = new BigDecimal(3000000.0);
|
||||
private final BaseActivitySummary summary;
|
||||
private final ActivityTrack activityTrack;
|
||||
// private final int version;
|
||||
// private final int version;
|
||||
private final Date baseDate;
|
||||
private long baseLongitude;
|
||||
private long baseLatitude;
|
||||
@ -126,9 +130,49 @@ public class ActivityDetailsParser {
|
||||
throw new GBException("Error parsing activity details: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
fixupMissingTimestamps(activityTrack);
|
||||
|
||||
return activityTrack;
|
||||
}
|
||||
|
||||
private void fixupMissingTimestamps(ActivityTrack activityTrack) {
|
||||
try {
|
||||
int pointer = 0;
|
||||
List<ActivityPoint> activityPointList = activityTrack.getTrackPoints();
|
||||
Date gpsStartTime = null;
|
||||
List<ActivityPoint> entriesToFixUp = new ArrayList<>();
|
||||
while (pointer < activityPointList.size() - 1) {
|
||||
ActivityPoint activityPoint = activityPointList.get(pointer);
|
||||
if (activityPoint.getLocation() == null) {
|
||||
pointer++;
|
||||
continue;
|
||||
}
|
||||
if (activityPoint.getTime().equals(activityPointList.get(pointer + 1).getTime())) {
|
||||
entriesToFixUp.add(activityPoint);
|
||||
} else {
|
||||
// found the first activity point with a proper timestamp
|
||||
entriesToFixUp.add(activityPoint);
|
||||
gpsStartTime = activityPointList.get(pointer + 1).getTime();
|
||||
break;
|
||||
}
|
||||
pointer++;
|
||||
}
|
||||
if (gpsStartTime != null) {
|
||||
// now adjust those entries without a timestamp
|
||||
long differenceInSec = TimeUnit.SECONDS.convert(Math.abs(gpsStartTime.getTime() - baseDate.getTime()), TimeUnit.MILLISECONDS);
|
||||
|
||||
double multiplier = (double) differenceInSec / (double) (entriesToFixUp.size());
|
||||
|
||||
for (int j = 0; j < entriesToFixUp.size(); j++) {
|
||||
long timeOffsetSeconds = Math.round(j * multiplier);
|
||||
entriesToFixUp.get(j).setTime(makeAbsolute(timeOffsetSeconds));
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error cleaning activity details", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private int consumeGPSAndUpdateBaseLocation(byte[] bytes, int offset, long timeOffset) {
|
||||
int i = 0;
|
||||
int longitudeDelta = BLETypeConversions.toInt16(bytes[offset + i++], bytes[offset + i++]);
|
||||
@ -144,7 +188,7 @@ public class ActivityDetailsParser {
|
||||
convertHuamiValueToDecimalDegrees(baseLatitude),
|
||||
baseAltitude);
|
||||
|
||||
ActivityPoint ap = getActivityPointFor(timeOffset);
|
||||
ActivityPoint ap = getActivityPointFor(timeOffset, coordinate);
|
||||
ap.setLocation(coordinate);
|
||||
add(ap);
|
||||
|
||||
@ -197,6 +241,19 @@ public class ActivityDetailsParser {
|
||||
return new ActivityPoint(time);
|
||||
}
|
||||
|
||||
private ActivityPoint getActivityPointFor(long timeOffsetSeconds, GPSCoordinate gpsCoordinate) {
|
||||
Date time = makeAbsolute(timeOffsetSeconds);
|
||||
if (lastActivityPoint != null) {
|
||||
if (lastActivityPoint.getTime().equals(time)) {
|
||||
if (lastActivityPoint.getLocation() != null && !lastActivityPoint.getLocation().equals(gpsCoordinate)) {
|
||||
return new ActivityPoint(time);
|
||||
}
|
||||
return lastActivityPoint;
|
||||
}
|
||||
}
|
||||
return new ActivityPoint(time);
|
||||
}
|
||||
|
||||
private Date makeAbsolute(long timeOffsetSeconds) {
|
||||
return new Date(baseDate.getTime() + timeOffsetSeconds * 1000);
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
|
||||
crcToVersion.put(56670, "0.1.1.41");
|
||||
crcToVersion.put(58736, "0.1.1.45");
|
||||
crcToVersion.put(2602, "1.0.2.00");
|
||||
crcToVersion.put(36157, "1.1.2.05");
|
||||
|
||||
// resources
|
||||
crcToVersion.put(12586, "0.0.8.74");
|
||||
@ -115,6 +116,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
|
||||
crcToVersion.put(21109, "0.1.1.41");
|
||||
crcToVersion.put(23073, "0.1.1.45");
|
||||
crcToVersion.put(59245, "1.0.2.00");
|
||||
crcToVersion.put(20591, "1.1.2.05");
|
||||
|
||||
// gps
|
||||
crcToVersion.put(61520, "9367,8f79a91,0,0,");
|
||||
|
@ -19,6 +19,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -34,10 +36,10 @@ import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiWeatherConditions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipFWHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
@ -50,17 +52,17 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWrit
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations.AmazfitBipFetchLogsOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiIcon;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations.AmazfitBipFetchLogsOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchActivityOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.FetchSportsSummaryOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchActivityOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchSportsSummaryOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Version;
|
||||
|
||||
public class AmazfitBipSupport extends MiBand2Support {
|
||||
public class AmazfitBipSupport extends HuamiSupport {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipSupport.class);
|
||||
|
||||
@ -92,11 +94,8 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("new notification");
|
||||
AlertNotificationProfile<?> profile = new AlertNotificationProfile(this);
|
||||
profile.setMaxLength(230);
|
||||
|
||||
byte customIconId = HuamiIcon.mapToIconId(notificationSpec.type);
|
||||
|
||||
AlertCategory alertCategory = AlertCategory.CustomHuami;
|
||||
|
||||
// The SMS icon for AlertCategory.SMS is unique and not available as iconId
|
||||
@ -108,8 +107,54 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
alertCategory = AlertCategory.Email;
|
||||
}
|
||||
|
||||
NewAlert alert = new NewAlert(alertCategory, 1, message, customIconId);
|
||||
profile.newAlert(builder, alert);
|
||||
int maxLength = 230;
|
||||
if (characteristicChunked != null) {
|
||||
int prefixlength = 2;
|
||||
|
||||
// We also need a (fake) source name for Mi Band 3 for SMS/EMAIL, else the message is not displayed
|
||||
byte[] appSuffix = "\0 \0".getBytes();
|
||||
int suffixlength = appSuffix.length;
|
||||
|
||||
if (alertCategory == AlertCategory.CustomHuami) {
|
||||
String appName;
|
||||
prefixlength = 3;
|
||||
final PackageManager pm = getContext().getPackageManager();
|
||||
ApplicationInfo ai = null;
|
||||
try {
|
||||
ai = pm.getApplicationInfo(notificationSpec.sourceAppId, 0);
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
|
||||
if (ai != null) {
|
||||
appName = "\0" + pm.getApplicationLabel(ai) + "\0";
|
||||
} else {
|
||||
appName = "\0" + "UNKNOWN" + "\0";
|
||||
}
|
||||
appSuffix = appName.getBytes();
|
||||
suffixlength = appSuffix.length;
|
||||
}
|
||||
|
||||
byte[] rawmessage = message.getBytes();
|
||||
int length = Math.min(rawmessage.length, maxLength - prefixlength);
|
||||
|
||||
byte[] command = new byte[length + prefixlength + suffixlength];
|
||||
|
||||
command[0] = (byte) alertCategory.getId();
|
||||
command[1] = 1;
|
||||
if (alertCategory == AlertCategory.CustomHuami) {
|
||||
command[2] = customIconId;
|
||||
}
|
||||
|
||||
System.arraycopy(rawmessage, 0, command, prefixlength, length);
|
||||
System.arraycopy(appSuffix, 0, command, prefixlength + length, appSuffix.length);
|
||||
|
||||
writeToChunked(builder, 0, command);
|
||||
} else {
|
||||
AlertNotificationProfile<?> profile = new AlertNotificationProfile(this);
|
||||
NewAlert alert = new NewAlert(alertCategory, 1, message, customIconId);
|
||||
profile.setMaxLength(maxLength);
|
||||
profile.newAlert(builder, alert);
|
||||
}
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Unable to send notification to Amazfit Bip", ex);
|
||||
@ -131,9 +176,6 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
|
||||
@Override
|
||||
protected AmazfitBipSupport setDisplayItems(TransactionBuilder builder) {
|
||||
if (gbDevice.getType() != DeviceType.AMAZFITBIP) {
|
||||
return this; // Disable for Cor for now
|
||||
}
|
||||
if (gbDevice.getFirmwareVersion() == null) {
|
||||
LOG.warn("Device not initialized yet, won't set menu items");
|
||||
return this;
|
||||
@ -185,7 +227,7 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
shortcut_alipay = true;
|
||||
}
|
||||
}
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), command);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), command);
|
||||
setShortcuts(builder, shortcut_weather, shortcut_alipay);
|
||||
|
||||
return this;
|
||||
@ -201,7 +243,7 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
(byte) ((alipay && weather) ? 0x81 : 0x01), 0x01,
|
||||
};
|
||||
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), command);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -241,13 +283,18 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
buf.put((byte) 0);
|
||||
}
|
||||
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
if (characteristicChunked != null) {
|
||||
writeToChunked(builder, 1, buf.array());
|
||||
} else {
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
}
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending current weather", ex);
|
||||
}
|
||||
|
||||
if (gbDevice.getType() == DeviceType.AMAZFITBIP) {
|
||||
if (gbDevice.getType() != DeviceType.AMAZFITCOR) {
|
||||
try {
|
||||
TransactionBuilder builder;
|
||||
builder = performInitialized("Sending air quality index");
|
||||
@ -266,7 +313,13 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
buf.put(aqiString.getBytes());
|
||||
buf.put((byte) 0);
|
||||
}
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
|
||||
if (characteristicChunked != null) {
|
||||
writeToChunked(builder, 1, buf.array());
|
||||
} else {
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
}
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Error sending air quality");
|
||||
@ -310,7 +363,6 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
|
||||
for (WeatherSpec.Forecast forecast : weatherSpec.forecasts) {
|
||||
condition = HuamiWeatherConditions.mapToAmazfitBipWeatherCode(forecast.conditionCode);
|
||||
|
||||
buf.put(condition);
|
||||
buf.put(condition);
|
||||
buf.put((byte) (forecast.maxTemp - 273));
|
||||
@ -321,7 +373,12 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
}
|
||||
}
|
||||
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
if (characteristicChunked != null) {
|
||||
writeToChunked(builder, 1, buf.array());
|
||||
} else {
|
||||
builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array());
|
||||
}
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending weather forecast", ex);
|
||||
@ -372,7 +429,7 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
boolean handled = super.onCharacteristicChanged(gatt, characteristic);
|
||||
if (!handled) {
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION.equals(characteristicUUID)) {
|
||||
if (HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION.equals(characteristicUUID)) {
|
||||
return handleConfigurationInfo(characteristic.getValue());
|
||||
}
|
||||
}
|
||||
@ -395,11 +452,11 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
// this probably does more than only getting the GPS version...
|
||||
private AmazfitBipSupport requestGPSVersion(TransactionBuilder builder) {
|
||||
LOG.info("Requesting GPS version");
|
||||
builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), AmazfitBipService.COMMAND_REQUEST_GPS_VERSION);
|
||||
builder.write(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION), AmazfitBipService.COMMAND_REQUEST_GPS_VERSION);
|
||||
return this;
|
||||
}
|
||||
|
||||
private AmazfitBipSupport setLanguage(TransactionBuilder builder) {
|
||||
protected AmazfitBipSupport setLanguage(TransactionBuilder builder) {
|
||||
|
||||
String language = Locale.getDefault().getLanguage();
|
||||
String country = Locale.getDefault().getCountry();
|
||||
@ -427,6 +484,10 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH;
|
||||
localeString = "es_ES";
|
||||
break;
|
||||
case 4:
|
||||
command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH;
|
||||
localeString = "ru_RU";
|
||||
break;
|
||||
default:
|
||||
switch (language) {
|
||||
case "zh":
|
||||
@ -442,19 +503,24 @@ public class AmazfitBipSupport extends MiBand2Support {
|
||||
command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH;
|
||||
localeString = "es_ES";
|
||||
break;
|
||||
case "ru":
|
||||
command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH;
|
||||
localeString = "ru_RU";
|
||||
break;
|
||||
default:
|
||||
command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH;
|
||||
localeString = "en_US";
|
||||
break;
|
||||
}
|
||||
}
|
||||
command_new = AmazfitBipService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE;
|
||||
command_new = HuamiService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE.clone();
|
||||
System.arraycopy(localeString.getBytes(), 0, command_new, 3, localeString.getBytes().length);
|
||||
|
||||
builder.add(new ConditionalWriteAction(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION)) {
|
||||
builder.add(new ConditionalWriteAction(getCharacteristic(HuamiService.UUID_CHARACTERISTIC_3_CONFIGURATION)) {
|
||||
@Override
|
||||
protected byte[] checkCondition() {
|
||||
if (gbDevice.getType() == DeviceType.MIBAND3 || (gbDevice.getType() == DeviceType.AMAZFITBIP && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("0.1.0.77")) >= 0)) {
|
||||
if ((gbDevice.getType() == DeviceType.AMAZFITBIP && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("0.1.0.77")) >= 0) ||
|
||||
(gbDevice.getType() == DeviceType.AMAZFITCOR && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("1.0.7.23")) >= 0)) {
|
||||
return command_new;
|
||||
} else {
|
||||
return command_old;
|
||||
|
@ -26,14 +26,14 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.OverflowStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support;
|
||||
|
||||
|
||||
// This class in no longer in use except for incoming calls
|
||||
class AmazfitBipTextNotificationStrategy extends Mi2TextNotificationStrategy {
|
||||
|
||||
AmazfitBipTextNotificationStrategy(MiBand2Support support) {
|
||||
AmazfitBipTextNotificationStrategy(HuamiSupport support) {
|
||||
super(support);
|
||||
}
|
||||
|
||||
|
@ -33,12 +33,12 @@ import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.AbstractFetchOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.AbstractFetchOperation;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
@ -75,12 +75,12 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation {
|
||||
GregorianCalendar sinceWhen = BLETypeConversions.createCalendar();
|
||||
sinceWhen.add(Calendar.DAY_OF_MONTH, -10);
|
||||
builder.write(characteristicFetch, BLETypeConversions.join(new byte[]{
|
||||
MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE,
|
||||
HuamiService.COMMAND_ACTIVITY_DATA_START_DATE,
|
||||
AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_DEBUGLOGS},
|
||||
getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES)));
|
||||
builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply
|
||||
builder.notify(characteristicActivityData, true);
|
||||
builder.write(characteristicFetch, new byte[]{MiBand2Service.COMMAND_FETCH_DATA});
|
||||
builder.write(characteristicFetch, new byte[]{HuamiService.COMMAND_FETCH_DATA});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user