mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-25 03:16:51 +01:00
Merge branch 'master' into patch-2
This commit is contained in:
commit
fabe9834e9
11
.travis.yml
11
.travis.yml
@ -16,10 +16,10 @@ android:
|
||||
- tools
|
||||
|
||||
# The BuildTools version used by your project
|
||||
- build-tools-26.0.2
|
||||
- build-tools-27.0.3
|
||||
|
||||
# The SDK version used to compile your project
|
||||
- android-25
|
||||
- android-27
|
||||
|
||||
# Additional components
|
||||
- extra-android-m2repository
|
||||
@ -30,4 +30,9 @@ android:
|
||||
#- sys-img-armeabi-v7a-android-19
|
||||
#- sys-img-x86-android-17
|
||||
|
||||
script: ./gradlew build connectedCheck --stacktrace
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
|
||||
script:
|
||||
- ./gradlew build connectedCheck --stacktrace
|
||||
- bash config/travis/validate_fastlane_metadata.sh
|
||||
|
129
CHANGELOG.md
129
CHANGELOG.md
@ -1,5 +1,116 @@
|
||||
### Changelog
|
||||
|
||||
#### 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
|
||||
* Mi Band 3: Allow flashing fonts (untested)
|
||||
* Amazfit Bip: Allow flashing latest firmwares
|
||||
* Amazfit Cor: Allow flashing Bip fonts (untested)
|
||||
* Allow to limit auto fetch to a user configurable time interval
|
||||
|
||||
#### Version 0.27.0
|
||||
* Initial support for Mi Band 3 (largely untested, needs to be connected to Mi Fit once)
|
||||
* Option for automatic activity sync after screen unlock
|
||||
* Allow hiding activity transfer notification on Android Oreo and above
|
||||
* Allow blacklisting of pebblekit notifications for individual apps
|
||||
* Allow blacklisting all application at once
|
||||
* Forward Skype notifications to wearable even if "local only" flag is set
|
||||
* Show Gadgetbridge logo behind cards in main activity
|
||||
* Always stop BT/BTLE discovery when exiting the discovery activity
|
||||
* Amazfit Bip/Cor: Fix scheduled setting for "display on lift wrist" preference
|
||||
* Amazfit Bip/Cor: add recent firmwares to whitelist
|
||||
* Pebble: Fix a rare crash in webview
|
||||
|
||||
#### Version 0.26.5
|
||||
* Fix autoreconnect at boot on recent Android versions
|
||||
* Bluetooth connection is more stable on Oreo
|
||||
* Potentially fix the watch continuously vibrating after call pickup
|
||||
* Amazfit Bip: Add setting to configure shortcuts (swipe to right from watchface)
|
||||
* Recognize Q8 as a HPlus device
|
||||
|
||||
#### Version 0.26.4
|
||||
* Fix a bug with Toasts appearing every time a notification arrives when bluetooth is disabled
|
||||
* Pebble 2: Add optional GATT client only mode that might help with connection stability
|
||||
* Amazfit Cor: Fix detection of newer firmwares
|
||||
* Mi Band 2: Fix text notifcations not appearing with short vibration patterns
|
||||
|
||||
#### Version 0.26.3
|
||||
* Amazfit Bip: Add proper mime type to shared gpx files
|
||||
* Amazfit Bip: allow to set displayed menu items
|
||||
* Amazfit Bip: fix fetching logs from device via debug menu
|
||||
* Amazfit Bip: Raise .res limit to 700000 bytes for modded files
|
||||
|
||||
#### Version 0.26.2
|
||||
* Amazfit Bip: Time and timezone fixes for Android <=6 when exporting GPX
|
||||
|
||||
#### Version 0.26.1
|
||||
* Fix crashes and connection problems on Android 6 and lower
|
||||
|
||||
#### Version 0.26.0
|
||||
* Amazfit Bip: Initial support for GPS tracks
|
||||
* Pebble: Wind speed/direction support and bugfixes for weather when using background javascript
|
||||
|
||||
#### Version 0.25.1
|
||||
* Amazfit Cor: Try to send weather location instead of AQI
|
||||
* Amazfit Bip: Support setting start end end time for background light when lifting the arm
|
||||
* Pebble: various fixes and improvements for background javascript
|
||||
* Explicitly ask for RECEIVE_SMS permission to fix problems with Android 8
|
||||
|
||||
#### Version 0.25.0
|
||||
* Initial support for Xwatch
|
||||
* Move the connected device to top in control center
|
||||
* Add adaptive launcher icon for Android 8.x
|
||||
* No longer plot heart rate graph when device was detected as not worn
|
||||
* Pebble: Small fixes for background js (e.g. Pebble-Casio-WV58DE)
|
||||
* Pebble: native (non bg js) support for weather in Simply Light watchface
|
||||
|
||||
#### Version 0.24.6
|
||||
* Display the chat icon for notifications coming from Kontalk and Antox
|
||||
* Pebble: Fix for background js which try to send floats (e.g. TrekVolle)
|
||||
* Mi Band 2: Change the way vibration patterns work, also fixes problems with missing text on newer firmwares
|
||||
|
||||
#### Version 0.24.5
|
||||
* Fix crash in settings activity with export location
|
||||
* Fix notification deletion regression
|
||||
* Add 'Ł' and 'ł' to transliteration map
|
||||
* Omnijaws Weather: correctly pick today's min and max temperature
|
||||
* Fix alarm details activity on small screen
|
||||
* Pebble: mimic online check of TrekVolle when using background js
|
||||
|
||||
#### Version 0.24.4
|
||||
* Amazfit Bip: Fix language setting on new firmwares
|
||||
|
||||
@ -55,7 +166,7 @@
|
||||
* Mi Band 2/Bip/Cor: Whole day HR support
|
||||
* Mi Band 2/Bip/Cor: Prevent writing a lot of HR samples to the database when not using the live activity feature
|
||||
* Pebble: Fix some nasty crashes which occur since 0.22.0
|
||||
* Workround for non-working notifcations from wechat and outlook
|
||||
* Workaround for non-working notifications from wechat and outlook
|
||||
|
||||
#### Version 0.22.3
|
||||
* Amazfit Bip: Allow flashing watchfaces
|
||||
@ -77,7 +188,7 @@
|
||||
* Add experimental support for Amazfit Cor and Mi Band HRX (no firmware update on the latter)
|
||||
* Mi Band 2: Support more icons and textual notifications for more apps
|
||||
* Add some quick action buttons to Gadgetbridge's notification
|
||||
* Add transliteration support for ukranian cyrillic charaters
|
||||
* Add transliteration support for ukrainian cyrillic characters
|
||||
* Fix annoying toast in Mi Band settings
|
||||
|
||||
#### Version 0.21.6
|
||||
@ -124,7 +235,7 @@
|
||||
* Amazfit Bip: Fix call notification with unknown caller
|
||||
* Amazfit Bip: Fix crash when weather is updated and device reconnecting
|
||||
* Mi2/Bip: Fix crash when synchronizing calendar to alarms
|
||||
* Pebble: Fix crash when takeing screenshots on Android 8.0 (Oreo)
|
||||
* Pebble: Fix crash when taking screenshots on Android 8.0 (Oreo)
|
||||
* Pebble: Support some google app icons
|
||||
* Pebble: try to support spotify
|
||||
* Mi Band 2: Support configurable button actions
|
||||
@ -143,7 +254,7 @@
|
||||
* Mi Band: Fix setting smart alarms
|
||||
|
||||
#### Version 0.20.0
|
||||
* Inital Amazfit Bip support (WIP)
|
||||
* Initial Amazfit Bip support (WIP)
|
||||
* Various theming fixes
|
||||
* Add workaround for blacklist not properly persisting
|
||||
* Handle resetting language to default properly
|
||||
@ -214,7 +325,7 @@
|
||||
* Mi Band 2: Fix crash on "chat" or "social network" text notification (#603)
|
||||
|
||||
#### Version 0.18.1
|
||||
* Pebble: Fix Firmware insstallation on Pebble Time Round (broken since 0.16.0)
|
||||
* Pebble: Fix Firmware installation on Pebble Time Round (broken since 0.16.0)
|
||||
* Start VibrationActivity when using "find device" button with Vibratissimo
|
||||
* Support material fork of K9
|
||||
|
||||
@ -256,9 +367,9 @@
|
||||
#### Version 0.17.3
|
||||
* HPlus: Improve display of new messages and phone calls
|
||||
* HPlus: Fix bug related to steps and heart rate
|
||||
* Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability accross versions)
|
||||
* Pebble: Support dynamic keys for natively supported watchfaces and watchapps (more stability across versions)
|
||||
* Pebble: Fix error Toast being displayed when TimeStyle watchface is not installed
|
||||
* Mi Band 1+2: Support for connecting wihout BT pairing (workaround for certain connection problems)
|
||||
* Mi Band 1+2: Support for connecting without BT pairing (workaround for certain connection problems)
|
||||
|
||||
#### Version 0.17.2
|
||||
* Pebble: Fix temperature unit in Timestyle Pebble watchface
|
||||
@ -278,7 +389,7 @@
|
||||
* Pebble: Add option to disable call display
|
||||
* Pebble: Add option to automatically delete notifications that got dismissed on the phone
|
||||
* Pebble: Bugfix for some PebbleKit enabled 3rd party apps (TCW and maybe other)
|
||||
* Pebble 2/LE: Improve reliablitly and transfer speed
|
||||
* Pebble 2/LE: Improve reliability and transfer speed
|
||||
* HPlus: Improved discovery and pairing
|
||||
* HPlus: Improved notifications (display + vibration)
|
||||
* HPlus: Synchronize time and date
|
||||
@ -807,7 +918,7 @@
|
||||
|
||||
#### Version 0.1.4
|
||||
* New AppManager shows installed Apps/Watchfaces (removal possible via context menu)
|
||||
* Allow back navigation in ActionBar (Debug and AppMananger Activities)
|
||||
* Allow back navigation in ActionBar (Debug and AppManager Activities)
|
||||
* Make sure Intent broadcasts do not leave Gadgetbridge
|
||||
* Show hint in the Main Activity (tap to connect etc)
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
names ()
|
||||
{
|
||||
echo -e "\n exit;\n**Contributors (sorted by number of commits):**\n";
|
||||
git log --format='%aN:%ae' origin/master | grep -Ev "FYG_.*_bot_ignore_me" | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$1]+=1;if (length($2) > length(e[$1])) {e[$1]=$2}}END{for (i in e) { n[i]=e[i];c[i]+=ct[i] }; for (a in e) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2-
|
||||
git log --format='%aN:%aE' origin/master | grep -Ev "(anonymous:|FYG_.*_bot_ignore_me)" | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$1]+=1;e[$1]=$2}END{for (i in e) { n[i]=e[i];c[i]+=ct[i] }; for (a in e) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2-
|
||||
}
|
||||
quine ()
|
||||
{
|
||||
@ -12,7 +12,7 @@
|
||||
declare -f quine | sed -e 's/^[[:space:]]*/ /';
|
||||
echo -e " quine\n";
|
||||
names;
|
||||
echo -e "\nAnd all the Transifex translators, which I cannot automatically list, at the moment.\n\n*To update the contributors list just run this file with bash*"
|
||||
echo -e "\nAnd all the Transifex translators, which I cannot automatically list, at the moment.\n\n*To update the contributors list just run this file with bash. Prefix a name with % in .mailmap to set a contact as preferred*"
|
||||
} > CONTRIBUTORS.rst;
|
||||
exit
|
||||
}
|
||||
@ -26,47 +26,124 @@
|
||||
* Carsten Pfeiffer <cpfeiffer@users.noreply.github.com>
|
||||
* Daniele Gobbetti <daniele+github@gobbetti.name>
|
||||
* João Paulo Barraca <jpbarraca@gmail.com>
|
||||
* ivanovlev <lion.ivanov@gmal.com>
|
||||
* Jonas <jonasdcdm@posteo.net>
|
||||
* Yaron Shahrabani <sh.yaron@gmail.com>
|
||||
* postsorino <postsorino@krutt.org>
|
||||
* protomors <protomors@gmail.com>
|
||||
* Allan Nordhøy <epost@anotheragency.no>
|
||||
* 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>
|
||||
* Julien Pivotto <roidelapluie@inuits.eu>
|
||||
* Taavi Eomäe <taavi.eomae+github@gmail.com>
|
||||
* Steffen Liebergeld <perl@gmx.org>
|
||||
* Lem Dulfo <lemuel.dulfo@gmail.com>
|
||||
* Hadrián Candela <hadrian.candela@gmail.com>
|
||||
* Felix Konstantin Maurer <maufl@maufl.de>
|
||||
* Sergey Trofimov <sarg@sarg.org.ru>
|
||||
* Robert Barat <rbarat07@gmail.com>
|
||||
* José Rebelo <joserebelo@outlook.com>
|
||||
* JohnnySun <bmy001@gmail.com>
|
||||
* Uwe Hermann <uwe@hermann-uwe.de>
|
||||
* Edoardo Rosa <edoardo.rosa90@gmail.com>
|
||||
* Alberto <albertsal83@gmail.com>
|
||||
* 0nse <0nse@users.noreply.github.com>
|
||||
* Vladislav Serkov <vladserkoff@protonmail.com>
|
||||
* Vebryn <vebryn@gmail.com>
|
||||
* Gilles Émilien MOREL <contact@gilles-morel.fr>
|
||||
* Gergely Peidl <gergely@peidl.net>
|
||||
* Bożydar <trening302@o2.pl>
|
||||
* 0nse <0nse@users.noreply.github.com>
|
||||
* Максим Якимчук <xpinovo@gmail.com>
|
||||
* Rimas Raguliūnas <rarimas@gmail.com>
|
||||
* masakoodaa <masakoodaa@protonmail.com>
|
||||
* Lukas Veneziano <fs@venezilu.de>
|
||||
* Kompact <joaorafael123@hotmail.com>
|
||||
* 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>
|
||||
* Ted Stein <me@tedstein.net>
|
||||
* 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>
|
||||
* LL <lu.lecocq@free.fr>
|
||||
* Jesús <zaagur@gmail.com>
|
||||
* exit-failure <hakrala@web.de>
|
||||
* Avamander <Avamander@users.noreply.github.com>
|
||||
* AnthonyDiGirolamo <anthony.digirolamo@gmail.com>
|
||||
* Andreas Kromke <Andreas.Kromke@web.de>
|
||||
* Ⲇⲁⲛⲓ Φi <daniphii@outlook.com>
|
||||
* Yar <yaroslav.isakov@gmail.com>
|
||||
* Yaron Shahrabani <sh.yaron@gmail.com>
|
||||
* xzovy <caleb@caleb-cooper.net>
|
||||
* xphnx <xphnx@users.noreply.github.com>
|
||||
* Vitaliy Shuruta <vshuruta@gmail.com>
|
||||
* 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>
|
||||
* Sergio Lopez <slp@sinrega.org>
|
||||
* Sami Alaoui <4ndroidgeek@gmail.com>
|
||||
* Roman Plevka <rplevka@redhat.com>
|
||||
* rober <rober@prtl.nodomain.net>
|
||||
* 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>
|
||||
* Moarc <aldwulf@gmail.com>
|
||||
* 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>
|
||||
* Martin Piatka <chachacha2323@gmail.com>
|
||||
* Marc Schlaich <marc.schlaich@googlemail.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>
|
||||
* kevlarcade <kevlarcade@gmail.com>
|
||||
* Kevin Richter <me@kevinrichter.nl>
|
||||
* Kaz Wolfe <root@kazwolfe.io>
|
||||
* Kasha <kasha_malaga@hotmail.com>
|
||||
* Joseph Kim <official.jkim@gmail.com>
|
||||
* Jan Lolek <janlolek@seznam.cz>
|
||||
* Jakub Jelínek <jakub.jelinek@gmail.com>
|
||||
* Ivan <ivan_tizhanin@mail.ru>
|
||||
* Hasan Ammar <ammarh@gmail.com>
|
||||
* Gilles MOREL <contact@gilles-morel.fr>
|
||||
* Gilles Émilien MOREL <Almtesh@users.noreply.github.com>
|
||||
* 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>
|
||||
* Davis Mosenkovs <davikovs@gmail.com>
|
||||
* Daniel Hauck <maill@dhauck.eu>
|
||||
* criogenic <criogenic@gmail.com>
|
||||
* Chris Perelstein <chris.perelstein@gmail.com>
|
||||
* chabotsi <chabotsi+github@chabotsi.fr>
|
||||
* Carlos Ferreira <calbertoferreira@gmail.com>
|
||||
* bucala <marcel.bucala@gmail.com>
|
||||
* batataspt@gmail.com <batataspt@gmail.com>
|
||||
* atkyritsis <at.kyritsis@gmail.com>
|
||||
* AndrewH <36428679+andrewheadricke@users.noreply.github.com>
|
||||
* andre <andre.buesgen@yahoo.de>
|
||||
* Allen B <28495335+Allen-B1@users.noreply.github.com>
|
||||
* Alexey Afanasev <avafanasiev@gmail.com>
|
||||
|
||||
And all the Transifex translators, which I cannot automatically list, at the moment.
|
||||
|
||||
*To update the contributors list just run this file with bash*
|
||||
*To update the contributors list just run this file with bash. Prefix a name with % in .mailmap to set a contact as preferred*
|
||||
|
@ -19,7 +19,7 @@
|
||||
|Realtime Activity Tracking | NO | NO | YES | YES | YES |
|
||||
|Music Control | YES | YES | NO | NO | NO |
|
||||
|Watchapp/face Installation | YES | YES | NO | NO | YES |
|
||||
|Firmware Installaton | YES | YES | YES | YES | YES |
|
||||
|Firmware Installation | YES | YES | YES | YES | YES |
|
||||
|Taking Screenshots | YES | YES | NO | NO | NO |
|
||||
|Support Android Companion Apps | YES | YES | NO | NO | NO |
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package nodomain.freeyourgadget.gadgetbridge.daogen;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import de.greenrobot.daogenerator.DaoGenerator;
|
||||
import de.greenrobot.daogenerator.Entity;
|
||||
import de.greenrobot.daogenerator.Index;
|
||||
@ -32,6 +34,7 @@ public class GBDaoGenerator {
|
||||
private static final String MAIN_PACKAGE = "nodomain.freeyourgadget.gadgetbridge";
|
||||
private static final String MODEL_PACKAGE = MAIN_PACKAGE + ".model";
|
||||
private static final String VALID_BY_DATE = MODEL_PACKAGE + ".ValidByDate";
|
||||
private static final String ACTIVITY_SUMMARY = MODEL_PACKAGE + ".ActivitySummary";
|
||||
private static final String OVERRIDE = "@Override";
|
||||
private static final String SAMPLE_RAW_INTENSITY = "rawIntensity";
|
||||
private static final String SAMPLE_STEPS = "steps";
|
||||
@ -42,7 +45,7 @@ public class GBDaoGenerator {
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Schema schema = new Schema(17, MAIN_PACKAGE + ".entities");
|
||||
Schema schema = new Schema(18, MAIN_PACKAGE + ".entities");
|
||||
|
||||
Entity userAttributes = addUserAttributes(schema);
|
||||
Entity user = addUserInfo(schema, userAttributes);
|
||||
@ -65,9 +68,14 @@ public class GBDaoGenerator {
|
||||
addHPlusHealthActivityKindOverlay(schema, user, device);
|
||||
addHPlusHealthActivitySample(schema, user, device);
|
||||
addNo1F1ActivitySample(schema, user, device);
|
||||
addXWatchActivitySample(schema, user, device);
|
||||
addZeTimeActivitySample(schema, user, device);
|
||||
addID115ActivitySample(schema, user, device);
|
||||
|
||||
addCalendarSyncState(schema, device);
|
||||
|
||||
addBipActivitySummary(schema, user, device);
|
||||
|
||||
new DaoGenerator().generateAll(schema, "app/src/main/java");
|
||||
}
|
||||
|
||||
@ -269,6 +277,43 @@ public class GBDaoGenerator {
|
||||
return activitySample;
|
||||
}
|
||||
|
||||
private static Entity addXWatchActivitySample(Schema schema, Entity user, Entity device) {
|
||||
Entity activitySample = addEntity(schema, "XWatchActivitySample");
|
||||
activitySample.implementsSerializable();
|
||||
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
addHeartRateProperties(activitySample);
|
||||
return activitySample;
|
||||
}
|
||||
|
||||
private static Entity addZeTimeActivitySample(Schema schema, Entity user, Entity device) {
|
||||
Entity activitySample = addEntity(schema, "ZeTimeActivitySample");
|
||||
activitySample.implementsSerializable();
|
||||
addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
|
||||
activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
|
||||
addHeartRateProperties(activitySample);
|
||||
activitySample.addIntProperty("caloriesBurnt");
|
||||
activitySample.addIntProperty("distanceMeters");
|
||||
activitySample.addIntProperty("activeTimeMinutes");
|
||||
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");
|
||||
@ -297,6 +342,31 @@ public class GBDaoGenerator {
|
||||
calendarSyncState.addIntProperty("hash").notNull();
|
||||
}
|
||||
|
||||
private static void addBipActivitySummary(Schema schema, Entity user, Entity device) {
|
||||
Entity summary = addEntity(schema, "BaseActivitySummary");
|
||||
summary.implementsInterface(ACTIVITY_SUMMARY);
|
||||
summary.addIdProperty();
|
||||
|
||||
summary.setJavaDoc(
|
||||
"This class represents the summary of a user's activity event. I.e. a walk, hike, a bicycle tour, etc.");
|
||||
|
||||
summary.addStringProperty("name").codeBeforeGetter(OVERRIDE);
|
||||
summary.addDateProperty("startTime").notNull().codeBeforeGetter(OVERRIDE);
|
||||
summary.addDateProperty("endTime").notNull().codeBeforeGetter(OVERRIDE);
|
||||
summary.addIntProperty("activityKind").notNull().codeBeforeGetter(OVERRIDE);
|
||||
|
||||
summary.addIntProperty("baseLongitude").javaDocGetterAndSetter("Temporary, bip-specific");
|
||||
summary.addIntProperty("baseLatitude").javaDocGetterAndSetter("Temporary, bip-specific");
|
||||
summary.addIntProperty("baseAltitude").javaDocGetterAndSetter("Temporary, bip-specific");
|
||||
|
||||
summary.addStringProperty("gpxTrack").codeBeforeGetter(OVERRIDE);
|
||||
|
||||
Property deviceId = summary.addLongProperty("deviceId").notNull().codeBeforeGetter(OVERRIDE).getProperty();
|
||||
summary.addToOne(device, deviceId);
|
||||
Property userId = summary.addLongProperty("userId").notNull().codeBeforeGetter(OVERRIDE).getProperty();
|
||||
summary.addToOne(user, userId);
|
||||
}
|
||||
|
||||
private static Property findProperty(Entity entity, String propertyName) {
|
||||
for (Property prop : entity.getProperties()) {
|
||||
if (propertyName.equals(prop.getPropertyName())) {
|
||||
|
25
README.md
25
README.md
@ -2,14 +2,14 @@ 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.
|
||||
|
||||
|
||||
[Homepage](https://gadgetbridge.org)
|
||||
|
||||
[Blog](https://blog.gadgetbridge.org)
|
||||
[Blog](https://blog.freeyourgadget.org)
|
||||
|
||||
[![Donate](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/Gadgetbridge/donate)
|
||||
|
||||
@ -22,17 +22,22 @@ vendor's servers.
|
||||
[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
|
||||
* 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
|
||||
|
||||
@ -91,6 +96,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
|
||||
|
||||
|
136
app/build.gradle
136
app/build.gradle
@ -1,14 +1,13 @@
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'findbugs'
|
||||
apply plugin: 'pmd'
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "findbugs"
|
||||
apply plugin: "pmd"
|
||||
|
||||
def ABORT_ON_CHECK_FAILURE = false
|
||||
|
||||
tasks.withType(Test) {
|
||||
systemProperty 'MiFirmwareDir', System.getProperty('MiFirmwareDir', null)
|
||||
systemProperty 'logback.configurationFile', System.getProperty('user.dir', null) + '/app/src/main/assets/logback.xml'
|
||||
systemProperty 'GB_LOGFILES_DIR', java.nio.file.Files.createTempDirectory('gblog').toString();
|
||||
systemProperty "MiFirmwareDir", System.getProperty("MiFirmwareDir", null)
|
||||
systemProperty "logback.configurationFile", System.getProperty("user.dir", null) + "/app/src/main/assets/logback.xml"
|
||||
systemProperty "GB_LOGFILES_DIR", java.nio.file.Files.createTempDirectory("gblog").toString()
|
||||
}
|
||||
|
||||
android {
|
||||
@ -17,112 +16,115 @@ android {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '26.0.2'
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion "27.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "nodomain.freeyourgadget.gadgetbridge"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 25
|
||||
targetSdkVersion 27
|
||||
|
||||
// note: always bump BOTH versionCode and versionName!
|
||||
versionName "0.24.4"
|
||||
versionCode 121
|
||||
// Note: always bump BOTH versionCode and versionName!
|
||||
versionName "0.29.0"
|
||||
versionCode 136
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError ABORT_ON_CHECK_FAILURE
|
||||
lintConfig file("${project.rootDir}/config/lint/lint.xml")
|
||||
|
||||
// if true, generate an HTML report (with issue explanations, sourcecode, etc)
|
||||
// If true, generate an HTML report (with issue explanations, sourcecode, etc)
|
||||
htmlReport true
|
||||
// optional path to report (default will be lint-results.html in the builddir)
|
||||
// Optional path to report (default will be lint-results.html in the builddir)
|
||||
htmlOutput file("$project.buildDir/reports/lint/lint.html")
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
unitTests {
|
||||
returnDefaultValues = true
|
||||
includeAndroidResources = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pmd {
|
||||
toolVersion = '5.5.5'
|
||||
toolVersion = "5.5.5"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// testCompile 'ch.qos.logback:logback-classic:1.1.3'
|
||||
// testCompile 'ch.qos.logback:logback-core:1.1.3'
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile "org.mockito:mockito-core:1.10.19"
|
||||
testCompile "org.robolectric:robolectric:3.5.1"
|
||||
// testImplementation "ch.qos.logback:logback-classic:1.1.3"
|
||||
// testImplementation "ch.qos.logback:logback-core:1.1.3"
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "org.mockito:mockito-core:1.10.19"
|
||||
testImplementation "org.robolectric:robolectric:3.6.1"
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile 'com.android.support:appcompat-v7:25.4.0'
|
||||
compile 'com.android.support:cardview-v7:25.4.0'
|
||||
compile 'com.android.support:recyclerview-v7:25.4.0'
|
||||
compile 'com.android.support:support-v4:25.4.0'
|
||||
compile 'com.android.support:gridlayout-v7:25.4.0'
|
||||
compile 'com.android.support:design:25.4.0'
|
||||
compile 'com.android.support:palette-v7:25.4.0'
|
||||
compile('com.github.tony19:logback-android-classic:1.1.1-6') {
|
||||
exclude group: 'com.google.android', module: 'android'
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation "com.android.support:appcompat-v7:27.1.1"
|
||||
implementation "com.android.support:cardview-v7:27.1.1"
|
||||
implementation "com.android.support:recyclerview-v7:27.1.1"
|
||||
implementation "com.android.support:support-v4:27.1.1"
|
||||
implementation "com.android.support:gridlayout-v7:27.1.1"
|
||||
implementation "com.android.support:design:27.1.1"
|
||||
implementation "com.android.support:palette-v7:27.1.1"
|
||||
implementation("com.github.tony19:logback-android-classic:1.1.1-6") {
|
||||
exclude group: "com.google.android", module: "android"
|
||||
}
|
||||
compile 'org.slf4j:slf4j-api:1.7.12'
|
||||
compile 'com.github.Freeyourgadget:MPAndroidChart:5e5bd6c1d3e95c515d4853647ae554e48ee1d593'
|
||||
compile 'com.github.pfichtner:durationformatter:0.1.1'
|
||||
compile 'de.cketti.library.changelog:ckchangelog:1.2.2'
|
||||
compile 'net.e175.klaus:solarpositioning:0.0.9'
|
||||
implementation "org.slf4j:slf4j-api:1.7.12"
|
||||
implementation "com.github.Freeyourgadget:MPAndroidChart:5e5bd6c1d3e95c515d4853647ae554e48ee1d593"
|
||||
implementation "com.github.pfichtner:durationformatter:0.1.1"
|
||||
implementation "de.cketti.library.changelog:ckchangelog:1.2.2"
|
||||
implementation "net.e175.klaus:solarpositioning:0.0.9"
|
||||
// use pristine greendao instead of our custom version, since our custom jitpack-packaged
|
||||
// version contains way too much and our custom patches are in the generator only.
|
||||
compile 'org.greenrobot:greendao:2.2.1'
|
||||
compile 'org.apache.commons:commons-lang3:3.5'
|
||||
compile 'org.cyanogenmod:platform.sdk:6.0'
|
||||
implementation "org.greenrobot:greendao:2.2.1"
|
||||
implementation "org.apache.commons:commons-lang3:3.5"
|
||||
implementation "org.cyanogenmod:platform.sdk:6.0"
|
||||
|
||||
// compile project(":DaoCore")
|
||||
// implementation project(":DaoCore")
|
||||
}
|
||||
|
||||
preBuild.dependsOn(":GBDaoGenerator:genSources")
|
||||
|
||||
gradle.beforeProject {
|
||||
preBuild.dependsOn(":GBDaoGenerator:genSources")
|
||||
}
|
||||
|
||||
check.dependsOn 'findbugs', 'pmd', 'lint'
|
||||
check.dependsOn "findbugs", "pmd", "lint"
|
||||
|
||||
task pmd(type: Pmd) {
|
||||
ruleSetFiles = files("${project.rootDir}/config/pmd/pmd-ruleset.xml")
|
||||
ignoreFailures = !ABORT_ON_CHECK_FAILURE
|
||||
ruleSets = [
|
||||
'java-android',
|
||||
'java-basic',
|
||||
'java-braces',
|
||||
'java-clone',
|
||||
'java-codesize',
|
||||
'java-controversial',
|
||||
'java-coupling',
|
||||
'java-design',
|
||||
'java-empty',
|
||||
'java-finalizers',
|
||||
'java-imports',
|
||||
'java-junit',
|
||||
'java-optimizations',
|
||||
'java-strictexception',
|
||||
'java-strings',
|
||||
'java-sunsecure',
|
||||
'java-typeresolution',
|
||||
'java-unnecessary',
|
||||
'java-unusedcode'
|
||||
"java-android",
|
||||
"java-basic",
|
||||
"java-braces",
|
||||
"java-clone",
|
||||
"java-codesize",
|
||||
"java-controversial",
|
||||
"java-coupling",
|
||||
"java-design",
|
||||
"java-empty",
|
||||
"java-finalizers",
|
||||
"java-imports",
|
||||
"java-junit",
|
||||
"java-optimizations",
|
||||
"java-strictexception",
|
||||
"java-strings",
|
||||
"java-sunsecure",
|
||||
"java-typeresolution",
|
||||
"java-unnecessary",
|
||||
"java-unusedcode"
|
||||
]
|
||||
|
||||
source 'src'
|
||||
include '**/*.java'
|
||||
exclude '**/gen/**'
|
||||
source "src"
|
||||
include "**/*.java"
|
||||
exclude "**/gen/**"
|
||||
|
||||
reports {
|
||||
xml.enabled = false
|
||||
@ -142,7 +144,7 @@ task findbugs(type: FindBugs) {
|
||||
reportLevel = "medium"
|
||||
excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-filter.xml")
|
||||
classes = files("${project.rootDir}/app/build/intermediates/classes")
|
||||
source = fileTree('src/main/java/')
|
||||
source = fileTree("src/main/java/")
|
||||
classpath = files()
|
||||
reports {
|
||||
xml.enabled = false
|
||||
|
@ -17,6 +17,7 @@
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<uses-permission android:name="cyanogenmod.permission.ACCESS_WEATHER_MANAGER" />
|
||||
@ -37,13 +38,14 @@
|
||||
android:name=".GBApplication"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/GadgetbridgeTheme">
|
||||
<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" />
|
||||
@ -58,6 +60,10 @@
|
||||
android:name=".devices.miband.MiBandPreferencesActivity"
|
||||
android:label="@string/preferences_miband_settings"
|
||||
android:parentActivityName=".activities.SettingsActivity" />
|
||||
<activity
|
||||
android:name=".activities.ActivitySummariesActivity"
|
||||
android:label="@string/activity_summaries"
|
||||
android:parentActivityName=".activities.ControlCenterv2" />
|
||||
<activity
|
||||
android:launchMode="singleTop"
|
||||
android:name=".activities.appmanager.AppManagerActivity"
|
||||
@ -381,6 +387,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"
|
||||
@ -414,7 +426,7 @@
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/screenshot_provider_paths"/>
|
||||
android:resource="@xml/shared_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver android:name=".SleepAlarmWidget">
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Normano64
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Martin, Normano64, Taavi Eomäe
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -19,11 +19,17 @@ package nodomain.freeyourgadget.gadgetbridge;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.NotificationManager.Policy;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
@ -52,6 +58,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothStateChangeReceiver;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
@ -64,6 +71,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
|
||||
|
||||
/**
|
||||
* Main Application class that initializes and provides access to certain things like
|
||||
* logging and DB access.
|
||||
@ -109,6 +118,7 @@ public class GBApplication extends Application {
|
||||
private static Locale language;
|
||||
|
||||
private DeviceManager deviceManager;
|
||||
private BluetoothStateChangeReceiver bluetoothStateChangeReceiver;
|
||||
|
||||
public static void quit() {
|
||||
GB.log("Quitting Gadgetbridge...", GB.INFO, null);
|
||||
@ -166,12 +176,25 @@ public class GBApplication extends Application {
|
||||
setLanguage(language);
|
||||
|
||||
deviceService = createDeviceService();
|
||||
loadAppsBlackList();
|
||||
loadAppsNotifBlackList();
|
||||
loadAppsPebbleBlackList();
|
||||
loadCalendarsBlackList();
|
||||
|
||||
if (isRunningMarshmallowOrLater()) {
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
//the following will ensure the notification manager is kept alive
|
||||
if(isRunningOreoOrLater()) {
|
||||
NotificationChannel channel = notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
|
||||
if(channel == null) {
|
||||
channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
|
||||
getString(R.string.notification_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
bluetoothStateChangeReceiver = new BluetoothStateChangeReceiver();
|
||||
registerReceiver(bluetoothStateChangeReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||
}
|
||||
startService(new Intent(this, NotificationCollectorMonitorService.class));
|
||||
}
|
||||
}
|
||||
@ -289,6 +312,13 @@ public class GBApplication extends Application {
|
||||
public static boolean isRunningMarshmallowOrLater() {
|
||||
return VERSION.SDK_INT >= Build.VERSION_CODES.M;
|
||||
}
|
||||
public static boolean isRunningNougatOrLater() {
|
||||
return VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||
}
|
||||
|
||||
public static boolean isRunningOreoOrLater(){
|
||||
return VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
}
|
||||
|
||||
private static boolean isPrioritySender(int prioritySenders, String number) {
|
||||
if (prioritySenders == Policy.PRIORITY_SENDERS_ANY) {
|
||||
@ -343,56 +373,117 @@ public class GBApplication extends Application {
|
||||
return NotificationManager.INTERRUPTION_FILTER_ALL;
|
||||
}
|
||||
|
||||
private static HashSet<String> apps_blacklist = null;
|
||||
private static HashSet<String> apps_notification_blacklist = null;
|
||||
|
||||
public static boolean appIsBlacklisted(String packageName) {
|
||||
if (apps_blacklist == null) {
|
||||
GB.log("appIsBlacklisted: apps_blacklist is null!", GB.INFO, null);
|
||||
public static boolean appIsNotifBlacklisted(String packageName) {
|
||||
if (apps_notification_blacklist == null) {
|
||||
GB.log("appIsNotifBlacklisted: apps_notification_blacklist is null!", GB.INFO, null);
|
||||
}
|
||||
return apps_blacklist != null && apps_blacklist.contains(packageName);
|
||||
return apps_notification_blacklist != null && apps_notification_blacklist.contains(packageName);
|
||||
}
|
||||
|
||||
public static void setAppsBlackList(Set<String> packageNames) {
|
||||
public static void setAppsNotifBlackList(Set<String> packageNames) {
|
||||
if (packageNames == null) {
|
||||
GB.log("Set null apps_blacklist", GB.INFO, null);
|
||||
apps_blacklist = new HashSet<>();
|
||||
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
|
||||
apps_notification_blacklist = new HashSet<>();
|
||||
} else {
|
||||
apps_blacklist = new HashSet<>(packageNames);
|
||||
apps_notification_blacklist = new HashSet<>(packageNames);
|
||||
}
|
||||
GB.log("New apps_blacklist has " + apps_blacklist.size() + " entries", GB.INFO, null);
|
||||
saveAppsBlackList();
|
||||
GB.log("New apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
|
||||
saveAppsNotifBlackList();
|
||||
}
|
||||
|
||||
private static void loadAppsBlackList() {
|
||||
GB.log("Loading apps_blacklist", GB.INFO, null);
|
||||
apps_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
|
||||
if (apps_blacklist == null) {
|
||||
apps_blacklist = new HashSet<>();
|
||||
private static void loadAppsNotifBlackList() {
|
||||
GB.log("Loading apps_notification_blacklist", GB.INFO, null);
|
||||
apps_notification_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
|
||||
if (apps_notification_blacklist == null) {
|
||||
apps_notification_blacklist = new HashSet<>();
|
||||
}
|
||||
GB.log("Loaded apps_blacklist has " + apps_blacklist.size() + " entries", GB.INFO, null);
|
||||
GB.log("Loaded apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
|
||||
}
|
||||
|
||||
private static void saveAppsBlackList() {
|
||||
GB.log("Saving apps_blacklist with " + apps_blacklist.size() + " entries", GB.INFO, null);
|
||||
private static void saveAppsNotifBlackList() {
|
||||
GB.log("Saving apps_notification_blacklist with " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
|
||||
SharedPreferences.Editor editor = sharedPrefs.edit();
|
||||
if (apps_blacklist.isEmpty()) {
|
||||
if (apps_notification_blacklist.isEmpty()) {
|
||||
editor.putStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
|
||||
} else {
|
||||
Prefs.putStringSet(editor, GBPrefs.PACKAGE_BLACKLIST, apps_blacklist);
|
||||
Prefs.putStringSet(editor, GBPrefs.PACKAGE_BLACKLIST, apps_notification_blacklist);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void addAppToBlacklist(String packageName) {
|
||||
if (apps_blacklist.add(packageName)) {
|
||||
saveAppsBlackList();
|
||||
public static void addAppToNotifBlacklist(String packageName) {
|
||||
if (apps_notification_blacklist.add(packageName)) {
|
||||
saveAppsNotifBlackList();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void removeFromAppsBlacklist(String packageName) {
|
||||
GB.log("Removing from apps_blacklist: " + packageName, GB.INFO, null);
|
||||
apps_blacklist.remove(packageName);
|
||||
saveAppsBlackList();
|
||||
public static synchronized void removeFromAppsNotifBlacklist(String packageName) {
|
||||
GB.log("Removing from apps_notification_blacklist: " + packageName, GB.INFO, null);
|
||||
apps_notification_blacklist.remove(packageName);
|
||||
saveAppsNotifBlackList();
|
||||
}
|
||||
|
||||
private static HashSet<String> apps_pebblemsg_blacklist = null;
|
||||
|
||||
public static boolean appIsPebbleBlacklisted(String sender) {
|
||||
if (apps_pebblemsg_blacklist == null) {
|
||||
GB.log("appIsPebbleBlacklisted: apps_pebblemsg_blacklist is null!", GB.INFO, null);
|
||||
}
|
||||
return apps_pebblemsg_blacklist != null && apps_pebblemsg_blacklist.contains(sender);
|
||||
}
|
||||
|
||||
public static void setAppsPebbleBlackList(Set<String> packageNames) {
|
||||
if (packageNames == null) {
|
||||
GB.log("Set null apps_pebblemsg_blacklist", GB.INFO, null);
|
||||
apps_pebblemsg_blacklist = new HashSet<>();
|
||||
} else {
|
||||
apps_pebblemsg_blacklist = new HashSet<>(packageNames);
|
||||
}
|
||||
GB.log("New apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
|
||||
saveAppsPebbleBlackList();
|
||||
}
|
||||
|
||||
private static void loadAppsPebbleBlackList() {
|
||||
GB.log("Loading apps_pebblemsg_blacklist", GB.INFO, null);
|
||||
apps_pebblemsg_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null);
|
||||
if (apps_pebblemsg_blacklist == null) {
|
||||
apps_pebblemsg_blacklist = new HashSet<>();
|
||||
}
|
||||
GB.log("Loaded apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
|
||||
}
|
||||
|
||||
private static void saveAppsPebbleBlackList() {
|
||||
GB.log("Saving apps_pebblemsg_blacklist with " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
|
||||
SharedPreferences.Editor editor = sharedPrefs.edit();
|
||||
if (apps_pebblemsg_blacklist.isEmpty()) {
|
||||
editor.putStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null);
|
||||
} else {
|
||||
Prefs.putStringSet(editor, GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, apps_pebblemsg_blacklist);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static void addAppToPebbleBlacklist(String packageName) {
|
||||
if (apps_pebblemsg_blacklist.add(packageNameToPebbleMsgSender(packageName))) {
|
||||
saveAppsPebbleBlackList();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void removeFromAppsPebbleBlacklist(String packageName) {
|
||||
GB.log("Removing from apps_pebblemsg_blacklist: " + packageName, GB.INFO, null);
|
||||
apps_pebblemsg_blacklist.remove(packageNameToPebbleMsgSender(packageName));
|
||||
saveAppsPebbleBlackList();
|
||||
}
|
||||
|
||||
public static String packageNameToPebbleMsgSender(String packageName) {
|
||||
if ("eu.siacs.conversations".equals(packageName)){
|
||||
return("Conversations");
|
||||
} else if ("net.osmand.plus".equals(packageName)) {
|
||||
return("OsmAnd");
|
||||
}
|
||||
return packageName;
|
||||
}
|
||||
|
||||
private static HashSet<String> calendars_blacklist = null;
|
||||
@ -406,7 +497,7 @@ public class GBApplication extends Application {
|
||||
|
||||
public static void setCalendarsBlackList(Set<String> calendarNames) {
|
||||
if (calendarNames == null) {
|
||||
GB.log("Set null apps_blacklist", GB.INFO, null);
|
||||
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
|
||||
calendars_blacklist = new HashSet<>();
|
||||
} else {
|
||||
calendars_blacklist = new HashSet<>(calendarNames);
|
||||
@ -490,7 +581,7 @@ public class GBApplication extends Application {
|
||||
case 0:
|
||||
String legacyGender = sharedPrefs.getString("mi_user_gender", null);
|
||||
String legacyHeight = sharedPrefs.getString("mi_user_height_cm", null);
|
||||
String legacyWeigth = sharedPrefs.getString("mi_user_weight_kg", null);
|
||||
String legacyWeight = sharedPrefs.getString("mi_user_weight_kg", null);
|
||||
String legacyYOB = sharedPrefs.getString("mi_user_year_of_birth", null);
|
||||
if (legacyGender != null) {
|
||||
int gender = "male".equals(legacyGender) ? 1 : "female".equals(legacyGender) ? 0 : 2;
|
||||
@ -501,8 +592,8 @@ public class GBApplication extends Application {
|
||||
editor.putString(ActivityUser.PREF_USER_HEIGHT_CM, legacyHeight);
|
||||
editor.remove("mi_user_height_cm");
|
||||
}
|
||||
if (legacyWeigth != null) {
|
||||
editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeigth);
|
||||
if (legacyWeight != null) {
|
||||
editor.putString(ActivityUser.PREF_USER_WEIGHT_KG, legacyWeight);
|
||||
editor.remove("mi_user_weight_kg");
|
||||
}
|
||||
if (legacyYOB != null) {
|
||||
@ -591,4 +682,24 @@ public class GBApplication extends Application {
|
||||
public static Locale getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
try {
|
||||
return getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA).versionName;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
GB.log("Unable to determine Gadgetbridge's version", GB.WARN, e);
|
||||
return "0.0.0";
|
||||
}
|
||||
}
|
||||
|
||||
public String getNameAndVersion() {
|
||||
try {
|
||||
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
|
||||
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA);
|
||||
return String.format("%s %s", appInfo.name, packageInfo.versionName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
GB.log("Unable to determine Gadgetbridge's name/version", GB.WARN, e);
|
||||
return "Gadgetbridge";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Taavi Eomäe
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -24,7 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
|
||||
/**
|
||||
* Provides lowlevel access to the database.
|
||||
* Provides low-level access to the database.
|
||||
*/
|
||||
public class LockHandler implements DBHandler {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -30,6 +30,7 @@ import ch.qos.logback.core.FileAppender;
|
||||
import ch.qos.logback.core.encoder.Encoder;
|
||||
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
|
||||
import ch.qos.logback.core.util.StatusPrinter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public abstract class Logging {
|
||||
public static final String PROP_LOGFILES_DIR = "GB_LOGFILES_DIR";
|
||||
@ -148,7 +149,7 @@ public abstract class Logging {
|
||||
}
|
||||
StringBuilder builder = new StringBuilder(bytes.length * 5);
|
||||
for (byte b : bytes) {
|
||||
builder.append(String.format("0x%2x", b));
|
||||
builder.append(String.format("0x%02x", b));
|
||||
builder.append(" ");
|
||||
}
|
||||
return builder.toString().trim();
|
||||
@ -156,9 +157,7 @@ public abstract class Logging {
|
||||
|
||||
public static void logBytes(Logger logger, byte[] value) {
|
||||
if (value != null) {
|
||||
for (byte b : value) {
|
||||
logger.warn("DATA: " + String.format("0x%2x", b));
|
||||
}
|
||||
logger.warn("DATA: " + GB.hexdump(value, 0, value.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 0nse, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 0nse, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, walkjivefly
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -0,0 +1,53 @@
|
||||
/* Copyright (C) 2017-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.activities;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.ListView;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.AbstractItemAdapter;
|
||||
|
||||
public abstract class AbstractListActivity<T> extends AbstractGBActivity {
|
||||
private AbstractItemAdapter<T> itemAdapter;
|
||||
private ListView itemListView;
|
||||
|
||||
public void setItemAdapter(AbstractItemAdapter<T> itemAdapter) {
|
||||
this.itemAdapter = itemAdapter;
|
||||
itemListView.setAdapter(itemAdapter);
|
||||
}
|
||||
|
||||
protected void refresh() {
|
||||
this.itemAdapter.loadItems();
|
||||
}
|
||||
|
||||
public AbstractItemAdapter<T> getItemAdapter() {
|
||||
return itemAdapter;
|
||||
}
|
||||
|
||||
public ListView getItemListView() {
|
||||
return itemListView;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_list);
|
||||
itemListView = findViewById(R.id.itemListView);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Christian
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Christian
|
||||
Fischer, Daniele Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -0,0 +1,301 @@
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.support.v4.content.FileProvider;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.adapter.ActivitySummariesAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class ActivitySummariesActivity extends AbstractListActivity<BaseActivitySummary> {
|
||||
|
||||
private GBDevice mGBDevice;
|
||||
private SwipeRefreshLayout swipeLayout;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (Objects.requireNonNull(action)) {
|
||||
case GBDevice.ACTION_DEVICE_CHANGED:
|
||||
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
mGBDevice = device;
|
||||
if (device.isBusy()) {
|
||||
swipeLayout.setRefreshing(true);
|
||||
} else {
|
||||
boolean wasBusy = swipeLayout.isRefreshing();
|
||||
swipeLayout.setRefreshing(false);
|
||||
if (wasBusy) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
getMenuInflater().inflate(R.menu.activity_list_menu, menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
boolean processed = false;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.activity_action_manage_timestamp:
|
||||
resetFetchTimestampToChosenDate();
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Must provide a device when invoking this activity");
|
||||
}
|
||||
|
||||
IntentFilter filterLocal = new IntentFilter();
|
||||
filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setItemAdapter(new ActivitySummariesAdapter(this, mGBDevice));
|
||||
|
||||
getItemListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
Object item = parent.getItemAtPosition(position);
|
||||
if (item != null) {
|
||||
ActivitySummary summary = (ActivitySummary) item;
|
||||
|
||||
String gpxTrack = summary.getGpxTrack();
|
||||
if (gpxTrack != null) {
|
||||
showTrack(gpxTrack);
|
||||
} else {
|
||||
GB.toast("This activity does not contain GPX tracks.", Toast.LENGTH_LONG, GB.INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getItemListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
|
||||
getItemListView().setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode actionMode, int position, long id, boolean checked) {
|
||||
final int selectedItems = getItemListView().getCheckedItemCount();
|
||||
actionMode.setTitle(selectedItems + " selected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.activity_list_context_menu, menu);
|
||||
findViewById(R.id.fab).setVisibility(View.INVISIBLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||
boolean processed = false;
|
||||
SparseBooleanArray checked = getItemListView().getCheckedItemPositions();
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.activity_action_delete:
|
||||
List<BaseActivitySummary> toDelete = new ArrayList<>();
|
||||
for(int i = 0; i< checked.size(); i++) {
|
||||
if (checked.valueAt(i)) {
|
||||
toDelete.add(getItemAdapter().getItem(checked.keyAt(i)));
|
||||
}
|
||||
}
|
||||
deleteItems(toDelete);
|
||||
processed = true;
|
||||
break;
|
||||
case R.id.activity_action_export:
|
||||
List<String> paths = new ArrayList<>();
|
||||
|
||||
|
||||
for(int i = 0; i< checked.size(); i++) {
|
||||
if (checked.valueAt(i)) {
|
||||
|
||||
BaseActivitySummary item = getItemAdapter().getItem(checked.keyAt(i));
|
||||
if (item != null) {
|
||||
ActivitySummary summary = (ActivitySummary) item;
|
||||
|
||||
String gpxTrack = summary.getGpxTrack();
|
||||
if (gpxTrack != null) {
|
||||
paths.add(gpxTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shareMultiple(paths);
|
||||
processed = true;
|
||||
break;
|
||||
case R.id.activity_action_select_all:
|
||||
for ( int i=0; i < getItemListView().getCount(); i++) {
|
||||
getItemListView().setItemChecked(i, true);
|
||||
}
|
||||
return true; //don't finish actionmode in this case!
|
||||
default:
|
||||
break;
|
||||
}
|
||||
actionMode.finish();
|
||||
return processed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode actionMode) {
|
||||
findViewById(R.id.fab).setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
|
||||
swipeLayout = findViewById(R.id.list_activity_swipe_layout);
|
||||
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
fetchTrackData();
|
||||
}
|
||||
});
|
||||
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
fetchTrackData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void resetFetchTimestampToChosenDate() {
|
||||
final Calendar currentDate = Calendar.getInstance();
|
||||
new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
|
||||
@Override
|
||||
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
|
||||
Calendar date = Calendar.getInstance();
|
||||
date.set(year, monthOfYear, dayOfMonth);
|
||||
|
||||
Long timestamp = date.getTimeInMillis() - 1000;
|
||||
SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit();
|
||||
editor.remove(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis"); //FIXME: key reconstruction is BAD
|
||||
editor.putLong(mGBDevice.getAddress() + "_" + "lastSportsActivityTimeMillis", timestamp);
|
||||
editor.commit();
|
||||
}
|
||||
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void deleteItems(List<BaseActivitySummary> items) {
|
||||
for(BaseActivitySummary item : items) {
|
||||
item.delete();
|
||||
getItemAdapter().remove(item);
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void showTrack(String gpxTrack) {
|
||||
try {
|
||||
AndroidUtils.viewFile(gpxTrack, Intent.ACTION_VIEW, this);
|
||||
} catch (IOException e) {
|
||||
GB.toast(this, "Unable to display GPX track: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchTrackData() {
|
||||
if (mGBDevice.isInitialized() && !mGBDevice.isBusy()) {
|
||||
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_GPS_TRACKS);
|
||||
} else {
|
||||
swipeLayout.setRefreshing(false);
|
||||
if (!mGBDevice.isInitialized()) {
|
||||
GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shareMultiple(List<String> paths){
|
||||
|
||||
ArrayList<Uri> uris = new ArrayList<>();
|
||||
for(String path: paths){
|
||||
File file = new File(path);
|
||||
uris.add(FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".screenshot_provider", file));
|
||||
}
|
||||
|
||||
if(uris.size() > 0) {
|
||||
final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
|
||||
intent.setType("application/gpx+xml");
|
||||
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
|
||||
startActivity(Intent.createChooser(intent, "SHARE"));
|
||||
} else {
|
||||
GB.toast(this, "No selected activity contains a GPX track to share", Toast.LENGTH_SHORT, GB.ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -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() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -22,6 +22,8 @@ import android.support.v4.app.NavUtils;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.SearchView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@ -64,12 +66,25 @@ public class AppBlacklistActivity extends AbstractGBActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.app_blacklist_menu, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
return true;
|
||||
case R.id.blacklist_all_notif:
|
||||
appBlacklistAdapter.blacklistAllNotif();
|
||||
return true;
|
||||
case R.id.whitelist_all_notif:
|
||||
appBlacklistAdapter.whitelistAllNotif();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
|
||||
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Taavi Eomäe
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -24,7 +24,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Canvas;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@ -42,15 +41,13 @@ import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.cketti.library.changelog.ChangeLog;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
@ -72,9 +69,7 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
}
|
||||
|
||||
private DeviceManager deviceManager;
|
||||
private ImageView background;
|
||||
|
||||
private List<GBDevice> deviceList;
|
||||
private GBDeviceAdapterv2 mGBDeviceAdapter;
|
||||
private RecyclerView deviceListView;
|
||||
|
||||
@ -84,7 +79,7 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
switch (Objects.requireNonNull(action)) {
|
||||
case GBApplication.ACTION_LANGUAGE_CHANGE:
|
||||
setLanguage(GBApplication.getLanguage(), true);
|
||||
break;
|
||||
@ -104,10 +99,10 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_controlcenterv2);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -115,28 +110,29 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
|
||||
this, drawer, toolbar, R.string.controlcenter_navigation_drawer_open, R.string.controlcenter_navigation_drawer_close);
|
||||
drawer.setDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
|
||||
NavigationView navigationView = findViewById(R.id.nav_view);
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
|
||||
//end of material design boilerplate
|
||||
deviceManager = ((GBApplication) getApplication()).getDeviceManager();
|
||||
|
||||
deviceListView = (RecyclerView) findViewById(R.id.deviceListView);
|
||||
deviceListView = findViewById(R.id.deviceListView);
|
||||
deviceListView.setHasFixedSize(true);
|
||||
deviceListView.setLayoutManager(new LinearLayoutManager(this));
|
||||
background = (ImageView) findViewById(R.id.no_items_bg);
|
||||
|
||||
deviceList = deviceManager.getDevices();
|
||||
List<GBDevice> deviceList = deviceManager.getDevices();
|
||||
mGBDeviceAdapter = new GBDeviceAdapterv2(this, deviceList);
|
||||
|
||||
deviceListView.setAdapter(this.mGBDeviceAdapter);
|
||||
|
||||
/* uncomment to enable fixed-swipe to reveal more actions
|
||||
|
||||
ItemTouchHelper swipeToDismissTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.LEFT , ItemTouchHelper.RIGHT) {
|
||||
@Override
|
||||
@ -167,9 +163,8 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
}
|
||||
});
|
||||
|
||||
//uncomment to enable fixed-swipe to reveal more actions
|
||||
//swipeToDismissTouchHelper.attachToRecyclerView(deviceListView);
|
||||
|
||||
swipeToDismissTouchHelper.attachToRecyclerView(deviceListView);
|
||||
*/
|
||||
|
||||
registerForContextMenu(deviceListView);
|
||||
|
||||
@ -226,7 +221,7 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
if (drawer.isDrawerOpen(GravityCompat.START)) {
|
||||
drawer.closeDrawer(GravityCompat.START);
|
||||
} else {
|
||||
@ -237,7 +232,7 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
|
||||
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
|
||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||
drawer.closeDrawer(GravityCompat.START);
|
||||
|
||||
switch (item.getItemId()) {
|
||||
@ -253,6 +248,10 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
Intent dbIntent = new Intent(this, DbManagementActivity.class);
|
||||
startActivity(dbIntent);
|
||||
return true;
|
||||
case R.id.action_blacklist:
|
||||
Intent blIntent = new Intent(this, AppBlacklistActivity.class);
|
||||
startActivity(blIntent);
|
||||
return true;
|
||||
case R.id.action_quit:
|
||||
GBApplication.quit();
|
||||
return true;
|
||||
@ -283,13 +282,6 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
}
|
||||
|
||||
private void refreshPairedDevices() {
|
||||
List<GBDevice> deviceList = deviceManager.getDevices();
|
||||
if (deviceList.isEmpty()) {
|
||||
background.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
background.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
mGBDeviceAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@ -309,6 +301,8 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
wantedPermissions.add(Manifest.permission.READ_PHONE_STATE);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.RECEIVE_SMS);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.READ_SMS);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_DENIED)
|
||||
@ -317,6 +311,11 @@ public class ControlCenterv2 extends AppCompatActivity
|
||||
wantedPermissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.READ_CALENDAR);
|
||||
try {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.MEDIA_CONTENT_CONTROL) == PackageManager.PERMISSION_DENIED)
|
||||
wantedPermissions.add(Manifest.permission.MEDIA_CONTENT_CONTROL);
|
||||
} catch (Exception ignored){
|
||||
}
|
||||
|
||||
if (!wantedPermissions.isEmpty())
|
||||
ActivityCompat.requestPermissions(this, wantedPermissions.toArray(new String[wantedPermissions.size()]), 0);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2016-2018 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Frank Slezak, ivanovlev, Kasha, Lem Dulfo, Steffen Liebergeld
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -40,18 +40,21 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
|
||||
|
||||
|
||||
public class DebugActivity extends AbstractGBActivity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class);
|
||||
@ -61,23 +64,12 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
= "nodomain.freeyourgadget.gadgetbridge.DebugActivity.action.reply";
|
||||
|
||||
private Spinner sendTypeSpinner;
|
||||
private Button sendButton;
|
||||
private Button incomingCallButton;
|
||||
private Button outgoingCallButton;
|
||||
private Button startCallButton;
|
||||
private Button endCallButton;
|
||||
private Button testNotificationButton;
|
||||
private Button setMusicInfoButton;
|
||||
private Button setTimeButton;
|
||||
private Button rebootButton;
|
||||
private Button HeartRateButton;
|
||||
private Button testNewFunctionalityButton;
|
||||
|
||||
private EditText editContent;
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
switch (intent.getAction()) {
|
||||
switch (Objects.requireNonNull(intent.getAction())) {
|
||||
case ACTION_REPLY: {
|
||||
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
|
||||
CharSequence reply = remoteInput.getCharSequence(EXTRA_REPLY);
|
||||
@ -85,13 +77,11 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
GB.toast(context, "got wearable reply: " + reply, Toast.LENGTH_SHORT, GB.INFO);
|
||||
break;
|
||||
}
|
||||
case DeviceService.ACTION_HEARTRATE_MEASUREMENT: {
|
||||
int hrValue = intent.getIntExtra(DeviceService.EXTRA_HEART_RATE_VALUE, -1);
|
||||
GB.toast(DebugActivity.this, "Heart Rate measured: " + hrValue, Toast.LENGTH_LONG, GB.INFO);
|
||||
default:
|
||||
LOG.info("ignoring intent action " + intent.getAction());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@ -105,17 +95,17 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
|
||||
registerReceiver(mReceiver, filter); // for ACTION_REPLY
|
||||
|
||||
editContent = (EditText) findViewById(R.id.editContent);
|
||||
editContent = findViewById(R.id.editContent);
|
||||
|
||||
ArrayList<String> spinnerArray = new ArrayList<>();
|
||||
for (NotificationType notificationType : NotificationType.values()) {
|
||||
spinnerArray.add(notificationType.name());
|
||||
}
|
||||
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, spinnerArray);
|
||||
sendTypeSpinner = (Spinner) findViewById(R.id.sendTypeSpinner);
|
||||
sendTypeSpinner = findViewById(R.id.sendTypeSpinner);
|
||||
sendTypeSpinner.setAdapter(spinnerArrayAdapter);
|
||||
|
||||
sendButton = (Button) findViewById(R.id.sendButton);
|
||||
Button sendButton = findViewById(R.id.sendButton);
|
||||
sendButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -132,7 +122,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
incomingCallButton = (Button) findViewById(R.id.incomingCallButton);
|
||||
Button incomingCallButton = findViewById(R.id.incomingCallButton);
|
||||
incomingCallButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -142,7 +132,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
GBApplication.deviceService().onSetCallState(callSpec);
|
||||
}
|
||||
});
|
||||
outgoingCallButton = (Button) findViewById(R.id.outgoingCallButton);
|
||||
Button outgoingCallButton = findViewById(R.id.outgoingCallButton);
|
||||
outgoingCallButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -153,7 +143,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
startCallButton = (Button) findViewById(R.id.startCallButton);
|
||||
Button startCallButton = findViewById(R.id.startCallButton);
|
||||
startCallButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -162,7 +152,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
GBApplication.deviceService().onSetCallState(callSpec);
|
||||
}
|
||||
});
|
||||
endCallButton = (Button) findViewById(R.id.endCallButton);
|
||||
Button endCallButton = findViewById(R.id.endCallButton);
|
||||
endCallButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -172,15 +162,15 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
rebootButton = (Button) findViewById(R.id.rebootButton);
|
||||
Button rebootButton = findViewById(R.id.rebootButton);
|
||||
rebootButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
GBApplication.deviceService().onReboot();
|
||||
}
|
||||
});
|
||||
HeartRateButton = (Button) findViewById(R.id.HearRateButton);
|
||||
HeartRateButton.setOnClickListener(new View.OnClickListener() {
|
||||
Button heartRateButton = findViewById(R.id.HeartRateButton);
|
||||
heartRateButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
GB.toast("Measuring heart rate, please wait...", Toast.LENGTH_LONG, GB.INFO);
|
||||
@ -188,7 +178,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
setMusicInfoButton = (Button) findViewById(R.id.setMusicInfoButton);
|
||||
Button setMusicInfoButton = findViewById(R.id.setMusicInfoButton);
|
||||
setMusicInfoButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -214,7 +204,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
setTimeButton = (Button) findViewById(R.id.setTimeButton);
|
||||
Button setTimeButton = findViewById(R.id.setTimeButton);
|
||||
setTimeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -222,7 +212,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
testNotificationButton = (Button) findViewById(R.id.testNotificationButton);
|
||||
Button testNotificationButton = findViewById(R.id.testNotificationButton);
|
||||
testNotificationButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -230,7 +220,15 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
}
|
||||
});
|
||||
|
||||
testNewFunctionalityButton = (Button) findViewById(R.id.testNewFunctionality);
|
||||
Button fetchDebugLogsButton = findViewById(R.id.fetchDebugLogsButton);
|
||||
fetchDebugLogsButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_DEBUGLOGS);
|
||||
}
|
||||
});
|
||||
|
||||
Button testNewFunctionalityButton = findViewById(R.id.testNewFunctionality);
|
||||
testNewFunctionalityButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -266,7 +264,7 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender().addAction(action);
|
||||
|
||||
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this)
|
||||
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setContentTitle(getString(R.string.test_notification))
|
||||
.setContentText(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
|
||||
.setTicker(getString(R.string.this_is_a_test_notification_from_gadgetbridge))
|
||||
@ -275,8 +273,10 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
.setContentIntent(pendingIntent)
|
||||
.extend(wearableExtender);
|
||||
|
||||
if (nManager != null) {
|
||||
nManager.notify((int) System.currentTimeMillis(), ncomp.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
@ -295,7 +295,4 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
public interface DeviceSelectionCallback {
|
||||
void invoke(GBDevice device);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun, Lem Dulfo, Uwe Hermann
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun, Lem Dulfo, Taavi Eomäe, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -24,6 +24,7 @@ import android.app.AlertDialog;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.le.BluetoothLeScanner;
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.bluetooth.le.ScanRecord;
|
||||
@ -54,6 +55,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
@ -66,8 +68,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY;
|
||||
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY;
|
||||
|
||||
public class DiscoveryActivity extends AbstractGBActivity implements AdapterView.OnItemClickListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DiscoveryActivity.class);
|
||||
@ -80,7 +80,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
switch (intent.getAction()) {
|
||||
switch (Objects.requireNonNull(intent.getAction())) {
|
||||
case BluetoothAdapter.ACTION_DISCOVERY_STARTED:
|
||||
if (isScanning != Scanning.SCANNING_BTLE && isScanning != Scanning.SCANNING_NEW_BTLE) {
|
||||
discoveryStarted(Scanning.SCANNING_BT);
|
||||
@ -105,9 +105,8 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
});
|
||||
break;
|
||||
case BluetoothAdapter.ACTION_STATE_CHANGED:
|
||||
int oldState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF);
|
||||
int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
||||
bluetoothStateChanged(oldState, newState);
|
||||
bluetoothStateChanged(newState);
|
||||
break;
|
||||
case BluetoothDevice.ACTION_FOUND: {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
@ -119,7 +118,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, GBDevice.RSSI_UNKNOWN);
|
||||
Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
|
||||
ParcelUuid[] uuids2 = AndroidUtils.toParcelUUids(uuids);
|
||||
ParcelUuid[] uuids2 = AndroidUtils.toParcelUuids(uuids);
|
||||
handleDeviceFound(device, rssi, uuids2);
|
||||
break;
|
||||
}
|
||||
@ -232,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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,7 +262,7 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_discovery);
|
||||
startButton = (Button) findViewById(R.id.discovery_start);
|
||||
startButton = findViewById(R.id.discovery_start);
|
||||
startButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -273,11 +270,11 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
}
|
||||
});
|
||||
|
||||
progressView = (ProgressBar) findViewById(R.id.discovery_progressbar);
|
||||
progressView = findViewById(R.id.discovery_progressbar);
|
||||
progressView.setProgress(0);
|
||||
progressView.setIndeterminate(true);
|
||||
progressView.setVisibility(View.GONE);
|
||||
ListView deviceCandidatesView = (ListView) findViewById(R.id.discovery_deviceCandidatesView);
|
||||
ListView deviceCandidatesView = findViewById(R.id.discovery_deviceCandidatesView);
|
||||
|
||||
cadidateListAdapter = new DeviceCandidateAdapter(this, deviceCandidates);
|
||||
deviceCandidatesView.setAdapter(cadidateListAdapter);
|
||||
@ -434,19 +431,33 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
}
|
||||
|
||||
private void stopBTLEDiscovery() {
|
||||
if (adapter != null)
|
||||
adapter.stopLeScan(leScanCallback);
|
||||
}
|
||||
|
||||
private void stopBTDiscovery() {
|
||||
if (adapter != null)
|
||||
adapter.cancelDiscovery();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private void stopNewBTLEDiscovery() {
|
||||
adapter.getBluetoothLeScanner().stopScan(newLeScanCallback);
|
||||
if (adapter == null)
|
||||
return;
|
||||
|
||||
BluetoothLeScanner bluetoothLeScanner = adapter.getBluetoothLeScanner();
|
||||
if (bluetoothLeScanner == null) {
|
||||
LOG.warn("could not get BluetoothLeScanner()!");
|
||||
return;
|
||||
}
|
||||
if (newLeScanCallback == null) {
|
||||
LOG.warn("newLeScanCallback == null!");
|
||||
return;
|
||||
}
|
||||
bluetoothLeScanner.stopScan(newLeScanCallback);
|
||||
}
|
||||
|
||||
private void bluetoothStateChanged(int oldState, int newState) {
|
||||
private void bluetoothStateChanged(int newState) {
|
||||
discoveryFinished();
|
||||
if (newState == BluetoothAdapter.STATE_ON) {
|
||||
this.adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
@ -530,12 +541,12 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
private ScanSettings getScanSettings() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return new ScanSettings.Builder()
|
||||
.setScanMode(SCAN_MODE_LOW_LATENCY)
|
||||
.setMatchMode(MATCH_MODE_STICKY)
|
||||
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||
.setMatchMode(android.bluetooth.le.ScanSettings.MATCH_MODE_STICKY)
|
||||
.build();
|
||||
} else {
|
||||
return new ScanSettings.Builder()
|
||||
.setScanMode(SCAN_MODE_LOW_LATENCY)
|
||||
.setScanMode(android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -611,4 +622,14 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
stopBTDiscovery();
|
||||
stopBTLEDiscovery();
|
||||
if (GBApplication.isRunningLollipopOrLater()) {
|
||||
stopNewBTLEDiscovery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -34,19 +34,26 @@ import android.widget.Toast;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.GBChromeClient;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.GBWebClient;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.JSInterface;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_CONNECT;
|
||||
|
||||
public class ExternalPebbleJSActivity extends AbstractGBActivity {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExternalPebbleJSActivity.class);
|
||||
@ -64,24 +71,69 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle extras = getIntent().getExtras();
|
||||
|
||||
boolean showConfig = false;
|
||||
|
||||
UUID currentUUID = null;
|
||||
GBDevice currentDevice = null;
|
||||
|
||||
if (extras == null) {
|
||||
throw new IllegalArgumentException("Must provide device and uuid in extras when invoking this activity");
|
||||
confUri = getIntent().getData();
|
||||
if(confUri.getScheme().equals("gadgetbridge")) {
|
||||
try {
|
||||
currentUUID = UUID.fromString(confUri.getHost());
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.error("UUID in incoming configuration is not a valid UUID: " +confUri.toString());
|
||||
}
|
||||
|
||||
//first check if we are still connected to a pebble
|
||||
DeviceManager deviceManager = ((GBApplication) getApplication()).getDeviceManager();
|
||||
List<GBDevice> deviceList = deviceManager.getDevices();
|
||||
for (GBDevice device : deviceList) {
|
||||
if (device.getState() == GBDevice.State.INITIALIZED) {
|
||||
if (device.getType().equals(DeviceType.PEBBLE)) {
|
||||
currentDevice = device;
|
||||
break;
|
||||
} else {
|
||||
LOG.error("attempting to load pebble configuration but a different device type is connected!!!");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentDevice == null) {
|
||||
//then try to reconnect to last connected device
|
||||
String btDeviceAddress = GBApplication.getPrefs().getPreferences().getString("last_device_address", null);
|
||||
if (btDeviceAddress != null) {
|
||||
GBDevice candidate = DeviceHelper.getInstance().findAvailableDevice(btDeviceAddress, this);
|
||||
if(!candidate.isConnected() && candidate.getType() == DeviceType.PEBBLE){
|
||||
Intent intent = new Intent(this, DeviceCommunicationService.class)
|
||||
.setAction(ACTION_CONNECT)
|
||||
.putExtra(GBDevice.EXTRA_DEVICE, currentDevice);
|
||||
this.startService(intent);
|
||||
currentDevice = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showConfig = true; //we are getting incoming configuration data
|
||||
}
|
||||
} else {
|
||||
currentDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
currentUUID = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID);
|
||||
|
||||
if (extras.getBoolean(START_BG_WEBVIEW, false)) {
|
||||
startBackgroundWebViewAndFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
GBDevice currentDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
UUID currentUUID = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID);
|
||||
showConfig = extras.getBoolean(SHOW_CONFIG, false);
|
||||
}
|
||||
|
||||
if (GBApplication.getGBPrefs().isBackgroundJsEnabled()) {
|
||||
if (extras.getBoolean(SHOW_CONFIG, false)) {
|
||||
if (showConfig) {
|
||||
Objects.requireNonNull(currentDevice, "Must provide a device when invoking this activity");
|
||||
Objects.requireNonNull(currentUUID, "Must provide a uuid when invoking this activity");
|
||||
|
||||
WebViewSingleton.runJavascriptInterface(currentDevice, currentUUID);
|
||||
WebViewSingleton.getInstance().runJavascriptInterface(this, currentDevice, currentUUID);
|
||||
}
|
||||
|
||||
// FIXME: is this really supposed to be outside the check for SHOW_CONFIG?
|
||||
@ -104,7 +156,7 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
|
||||
|
||||
private void setupBGWebView() {
|
||||
setContentView(R.layout.activity_external_pebble_js);
|
||||
myWebView = WebViewSingleton.getWebView(this);
|
||||
myWebView = WebViewSingleton.getInstance().getWebView(this);
|
||||
if (myWebView.getParent() != null) {
|
||||
((ViewGroup) myWebView.getParent()).removeView(myWebView);
|
||||
}
|
||||
@ -161,6 +213,7 @@ public class ExternalPebbleJSActivity extends AbstractGBActivity {
|
||||
} catch (IllegalArgumentException e) {
|
||||
GB.toast("returned uri: " + confUri.toString(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
myWebView.stopLoading();
|
||||
myWebView.loadUrl("file:///android_asset/app_config/configure.html?" + queryString);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -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()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -27,4 +27,8 @@ public class HeartRateUtils {
|
||||
* Value is in minutes
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, Normano64
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, Felix Konstantin Maurer, Normano64
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -18,7 +18,6 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.activities;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -29,16 +28,13 @@ import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceCategory;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.widget.Toast;
|
||||
@ -47,7 +43,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -65,6 +60,10 @@ 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.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;
|
||||
@ -330,6 +329,110 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
});
|
||||
|
||||
pref = findPreference("auto_fetch_interval_limit");
|
||||
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object autoFetchInterval) {
|
||||
String summary = String.format(
|
||||
getApplicationContext().getString(R.string.pref_auto_fetch_limit_fetches_summary),
|
||||
Integer.valueOf((String) autoFetchInterval));
|
||||
preference.setSummary(summary);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
int autoFetchInterval = GBApplication.getPrefs().getInt("auto_fetch_interval_limit", 0);
|
||||
summary = String.format(
|
||||
getApplicationContext().getString(R.string.pref_auto_fetch_limit_fetches_summary),
|
||||
autoFetchInterval);
|
||||
pref.setSummary(summary);
|
||||
|
||||
|
||||
|
||||
final Preference displayPages = findPreference("bip_display_items");
|
||||
displayPages.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference setDateFormat = findPreference(PREF_MI2_DATEFORMAT);
|
||||
setDateFormat.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DATEFORMAT);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand2DisplayItems = findPreference(PREF_MI2_DISPLAY_ITEMS);
|
||||
miBand2DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand3ScreenUnlock = findPreference(PREF_MI3_BAND_SCREEN_UNLOCK);
|
||||
miBand3ScreenUnlock.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI3_BAND_SCREEN_UNLOCK);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final Preference miBand3DisplayItems = findPreference("miband3_display_items");
|
||||
miBand3DisplayItems.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newVal) {
|
||||
invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSendConfiguration(PREF_MI2_DISPLAY_ITEMS);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@ -377,7 +480,7 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
|
||||
/*
|
||||
Either returns the file path of the selected document, or the display name
|
||||
Either returns the file path of the selected document, or the display name, or an empty string
|
||||
*/
|
||||
private String getAutoExportLocationSummary() {
|
||||
String autoExportLocation = GBApplication.getPrefs().getString(GBPrefs.AUTO_EXPORT_LOCATION, null);
|
||||
@ -386,13 +489,9 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
Uri uri = Uri.parse(autoExportLocation);
|
||||
try {
|
||||
String filePath = AndroidUtils.getFilePath(getApplicationContext(), uri);
|
||||
if (filePath != null) {
|
||||
return filePath;
|
||||
}
|
||||
} catch (URISyntaxException e) {
|
||||
return "";
|
||||
}
|
||||
return AndroidUtils.getFilePath(getApplicationContext(), uri);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
Cursor cursor = getContentResolver().query(
|
||||
uri,
|
||||
new String[]{DocumentsContract.Document.COLUMN_DISPLAY_NAME},
|
||||
@ -401,6 +500,11 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME));
|
||||
}
|
||||
}
|
||||
catch (Exception fdfsdfds) {
|
||||
LOG.warn("fuck");
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -457,6 +561,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,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -428,7 +428,7 @@ public abstract class AbstractAppManagerFragment extends Fragment {
|
||||
startActivity(startIntent);
|
||||
return true;
|
||||
case R.id.appmanager_app_openinstore:
|
||||
String url = "https://apps.getpebble.com/en_US/search/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/1?query=" + selectedApp.getName() + "&dev_settings=true";
|
||||
String url = "https://pebble-appstore.romanport.com/" + ((selectedApp.getType() == GBDeviceApp.Type.WATCHFACE) ? "watchfaces" : "watchapps") + "/0/?query=" + selectedApp.getName();
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, walkjivefly
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -70,6 +70,8 @@ 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()
|
||||
@ -439,6 +441,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
List<Entry> notWornEntries = new ArrayList<>(numEntries);
|
||||
boolean hr = supportsHeartrate(gbDevice);
|
||||
List<Entry> heartrateEntries = hr ? new ArrayList<Entry>(numEntries) : null;
|
||||
List<Integer> colors = new ArrayList<>(numEntries); // this is kinda inefficient...
|
||||
int lastHrSampleIndex = -1;
|
||||
|
||||
for (int i = 0; i < numEntries; i++) {
|
||||
@ -509,7 +512,7 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
}
|
||||
activityEntries.add(createLineEntry(value, ts));
|
||||
}
|
||||
if (hr && isValidHeartRateValue(sample.getHeartRate())) {
|
||||
if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && HeartRateUtils.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));
|
||||
@ -574,10 +577,6 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
|
||||
return new DefaultChartsData(lineData, xValueFormatter);
|
||||
}
|
||||
|
||||
protected boolean isValidHeartRateValue(int value) {
|
||||
return value > HeartRateUtils.MIN_HEART_RATE_VALUE && value < HeartRateUtils.MAX_HEART_RATE_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this to supply the samples to be displayed.
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Alberto, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2015-2018 0nse, Alberto, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, 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;
|
||||
@ -85,16 +84,19 @@ class ActivityAnalysis {
|
||||
}
|
||||
|
||||
if (!stats.containsKey(steps)) {
|
||||
LOG.info("Adding: " + steps);
|
||||
// LOG.debug("Adding: " + steps);
|
||||
stats.put(steps, timeDifference);
|
||||
} else {
|
||||
long time = stats.get(steps);
|
||||
LOG.info("Updating: " + steps + " " + timeDifference + time);
|
||||
// LOG.debug("Updating: " + steps + " " + timeDifference + time);
|
||||
stats.put(steps, timeDifference + time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
amount.setStartDate(sample.getTimestamp());
|
||||
amount.setEndDate(sample.getTimestamp());
|
||||
|
||||
previousAmount = amount;
|
||||
previousSample = sample;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
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;
|
||||
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Vebryn
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -36,15 +36,12 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
@ -52,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdap
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
@ -59,16 +57,11 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||
|
||||
public class ChartsActivity extends AbstractGBFragmentActivity implements ChartsHost {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ChartsActivity.class);
|
||||
|
||||
private Button mPrevButton;
|
||||
private Button mNextButton;
|
||||
private TextView mDateControl;
|
||||
|
||||
private Date mStartDate;
|
||||
private Date mEndDate;
|
||||
private SwipeRefreshLayout swipeLayout;
|
||||
private NonSwipeableViewPager viewPager;
|
||||
|
||||
LimitedQueue mActivityAmountCache = new LimitedQueue(60);
|
||||
|
||||
@ -86,7 +79,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_charts_durationdialog);
|
||||
|
||||
durationLabel = (TextView) findViewById(R.id.charts_duration_label);
|
||||
durationLabel = findViewById(R.id.charts_duration_label);
|
||||
setDuration(mDuration);
|
||||
}
|
||||
|
||||
@ -103,7 +96,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
switch (action) {
|
||||
switch (Objects.requireNonNull(action)) {
|
||||
case GBDevice.ACTION_DEVICE_CHANGED:
|
||||
GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
||||
refreshBusyState(dev);
|
||||
@ -145,7 +138,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
throw new IllegalArgumentException("Must provide a device when invoking this activity");
|
||||
}
|
||||
|
||||
swipeLayout = (SwipeRefreshLayout) findViewById(R.id.activity_swipe_layout);
|
||||
swipeLayout = findViewById(R.id.activity_swipe_layout);
|
||||
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
@ -155,7 +148,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
enableSwipeRefresh(true);
|
||||
|
||||
// Set up the ViewPager with the sections adapter.
|
||||
viewPager = (NonSwipeableViewPager) findViewById(R.id.charts_pager);
|
||||
NonSwipeableViewPager viewPager = findViewById(R.id.charts_pager);
|
||||
viewPager.setAdapter(getPagerAdapter());
|
||||
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
@ -172,8 +165,8 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
}
|
||||
});
|
||||
|
||||
dateBar = (ViewGroup) findViewById(R.id.charts_date_bar);
|
||||
mDateControl = (TextView) findViewById(R.id.charts_text_date);
|
||||
dateBar = findViewById(R.id.charts_date_bar);
|
||||
mDateControl = findViewById(R.id.charts_text_date);
|
||||
mDateControl.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@ -182,22 +175,20 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
}
|
||||
});
|
||||
|
||||
mPrevButton = (Button) findViewById(R.id.charts_previous);
|
||||
Button mPrevButton = findViewById(R.id.charts_previous);
|
||||
mPrevButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handlePrevButtonClicked();
|
||||
}
|
||||
});
|
||||
mNextButton = (Button) findViewById(R.id.charts_next);
|
||||
Button mNextButton = findViewById(R.id.charts_next);
|
||||
mNextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleNextButtonClicked();
|
||||
}
|
||||
});
|
||||
|
||||
LinearLayout mainLayout = (LinearLayout) findViewById(R.id.charts_main_layout);
|
||||
}
|
||||
|
||||
private String formatDetailedDuration() {
|
||||
@ -284,7 +275,7 @@ public class ChartsActivity extends AbstractGBFragmentActivity implements Charts
|
||||
|
||||
private void fetchActivityData() {
|
||||
if (getDevice().isInitialized()) {
|
||||
GBApplication.deviceService().onFetchActivityData();
|
||||
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
|
||||
} else {
|
||||
swipeLayout.setRefreshing(false);
|
||||
GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -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 (isValidHeartRateValue(heartRate)) {
|
||||
if (HeartRateUtils.isValidHeartRateValue(heartRate)) {
|
||||
setCurrentHeartRate(heartRate, timestamp);
|
||||
}
|
||||
int steps = sample.getSteps();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti
|
||||
|
||||
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();
|
||||
@ -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) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti, Vebryn
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
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,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer,
|
||||
Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -0,0 +1,122 @@
|
||||
/* 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.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
/**
|
||||
* Adapter for displaying generic ItemWithDetails instances.
|
||||
*/
|
||||
public abstract class AbstractItemAdapter<T> extends ArrayAdapter<T> {
|
||||
|
||||
public static final int SIZE_SMALL = 1;
|
||||
public static final int SIZE_MEDIUM = 2;
|
||||
public static final int SIZE_LARGE = 3;
|
||||
private final Context context;
|
||||
private final List<T> items;
|
||||
private boolean horizontalAlignment;
|
||||
private int size = SIZE_MEDIUM;
|
||||
|
||||
public AbstractItemAdapter(Context context) {
|
||||
this (context, new ArrayList<T>());
|
||||
}
|
||||
|
||||
public AbstractItemAdapter(Context context, List<T> items) {
|
||||
super(context, 0, items);
|
||||
|
||||
this.context = context;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void setHorizontalAlignment(boolean horizontalAlignment) {
|
||||
this.horizontalAlignment = horizontalAlignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
T item = getItem(position);
|
||||
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
if (horizontalAlignment) {
|
||||
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
|
||||
} else {
|
||||
switch (size) {
|
||||
case SIZE_SMALL:
|
||||
view = inflater.inflate(R.layout.item_with_details_small, parent, false);
|
||||
break;
|
||||
default:
|
||||
view = inflater.inflate(R.layout.item_with_details, parent, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
|
||||
TextView nameView = (TextView) view.findViewById(R.id.item_name);
|
||||
TextView detailsView = (TextView) view.findViewById(R.id.item_details);
|
||||
|
||||
nameView.setText(getName(item));
|
||||
detailsView.setText(getDetails(item));
|
||||
iconView.setImageResource(getIcon(item));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
protected abstract String getName(T item);
|
||||
|
||||
protected abstract String getDetails(T item);
|
||||
|
||||
@DrawableRes
|
||||
protected abstract int getIcon(T item);
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public List<T> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void loadItems() {
|
||||
}
|
||||
|
||||
public void setItems(List<T> items, boolean notify) {
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
if (notify) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class ActivitySummariesAdapter extends AbstractItemAdapter<BaseActivitySummary> {
|
||||
private final GBDevice device;
|
||||
|
||||
public ActivitySummariesAdapter(Context context, GBDevice device) {
|
||||
super(context);
|
||||
this.device = device;
|
||||
loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadItems() {
|
||||
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||
BaseActivitySummaryDao summaryDao = handler.getDaoSession().getBaseActivitySummaryDao();
|
||||
Device dbDevice = DBHelper.findDevice(device, handler.getDaoSession());
|
||||
|
||||
QueryBuilder<BaseActivitySummary> qb = summaryDao.queryBuilder();
|
||||
qb.where(BaseActivitySummaryDao.Properties.DeviceId.eq(dbDevice.getId())).orderDesc(BaseActivitySummaryDao.Properties.StartTime);
|
||||
List<BaseActivitySummary> allSummaries = qb.build().list();
|
||||
setItems(allSummaries, true);
|
||||
} catch (Exception e) {
|
||||
GB.toast("Error loading activity summaries.", Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(BaseActivitySummary item) {
|
||||
String name = item.getName();
|
||||
if (name != null && name.length() > 0) {
|
||||
return name;
|
||||
}
|
||||
|
||||
Date startTime = item.getStartTime();
|
||||
if (startTime != null) {
|
||||
return DateTimeUtils.formatDateTime(startTime);
|
||||
}
|
||||
return "Unknown activity";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDetails(BaseActivitySummary item) {
|
||||
return ActivityKind.asString(item.getActivityKind(), getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIcon(BaseActivitySummary item) {
|
||||
return ActivityKind.getIconId(item.getActivityKind());
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
|
||||
/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -23,7 +23,7 @@ import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.ImageView;
|
||||
@ -32,12 +32,16 @@ import android.widget.TextView;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.packageNameToPebbleMsgSender;
|
||||
|
||||
public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapter.AppBLViewHolder> implements Filterable {
|
||||
|
||||
private List<ApplicationInfo> applicationInfoList;
|
||||
@ -62,7 +66,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
|
||||
if (name == null) {
|
||||
name = ai.packageName;
|
||||
}
|
||||
if (GBApplication.appIsBlacklisted(ai.packageName)) {
|
||||
if (GBApplication.appIsNotifBlacklisted(ai.packageName) || GBApplication.appIsPebbleBlacklisted(packageNameToPebbleMsgSender(ai.packageName))) {
|
||||
// sort blacklisted first by prefixing with a '!'
|
||||
name = "!" + name;
|
||||
}
|
||||
@ -94,23 +98,52 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
|
||||
holder.deviceAppNameLabel.setText(mNameMap.get(appInfo));
|
||||
holder.deviceImageView.setImageDrawable(appInfo.loadIcon(mPm));
|
||||
|
||||
holder.checkbox.setChecked(GBApplication.appIsBlacklisted(appInfo.packageName));
|
||||
holder.blacklist_checkbox.setChecked(GBApplication.appIsNotifBlacklisted(appInfo.packageName));
|
||||
holder.blacklist_pebble_checkbox.setChecked(GBApplication.appIsPebbleBlacklisted(packageNameToPebbleMsgSender(appInfo.packageName)));
|
||||
|
||||
holder.blacklist_pebble_checkbox.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
((CheckedTextView) view).toggle();
|
||||
if ( ((CheckedTextView)view).isChecked() ) {
|
||||
GBApplication.addAppToPebbleBlacklist(appInfo.packageName);
|
||||
} else {
|
||||
GBApplication.removeFromAppsPebbleBlacklist(appInfo.packageName);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
CheckBox checkBox = ((CheckBox) v.findViewById(R.id.item_checkbox));
|
||||
CheckedTextView checkBox = ((CheckedTextView) v.findViewById(R.id.item_checkbox));
|
||||
checkBox.toggle();
|
||||
if (checkBox.isChecked()) {
|
||||
GBApplication.addAppToBlacklist(appInfo.packageName);
|
||||
GBApplication.addAppToNotifBlacklist(appInfo.packageName);
|
||||
} else {
|
||||
GBApplication.removeFromAppsBlacklist(appInfo.packageName);
|
||||
GBApplication.removeFromAppsNotifBlacklist(appInfo.packageName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void blacklistAllNotif() {
|
||||
Set<String> apps_blacklist = new HashSet<>();
|
||||
List<ApplicationInfo> allApps = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
|
||||
for (ApplicationInfo ai : allApps) {
|
||||
apps_blacklist.add(ai.packageName);
|
||||
}
|
||||
GBApplication.setAppsNotifBlackList(apps_blacklist);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void whitelistAllNotif() {
|
||||
Set<String> apps_blacklist = new HashSet<>();
|
||||
GBApplication.setAppsNotifBlackList(apps_blacklist);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return applicationInfoList.size();
|
||||
@ -123,9 +156,10 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
|
||||
return applicationFilter;
|
||||
}
|
||||
|
||||
public class AppBLViewHolder extends RecyclerView.ViewHolder {
|
||||
class AppBLViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
final CheckBox checkbox;
|
||||
final CheckedTextView blacklist_checkbox;
|
||||
final CheckedTextView blacklist_pebble_checkbox;
|
||||
final ImageView deviceImageView;
|
||||
final TextView deviceAppVersionAuthorLabel;
|
||||
final TextView deviceAppNameLabel;
|
||||
@ -133,7 +167,8 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
|
||||
AppBLViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
checkbox = (CheckBox) itemView.findViewById(R.id.item_checkbox);
|
||||
blacklist_checkbox = (CheckedTextView) itemView.findViewById(R.id.item_checkbox);
|
||||
blacklist_pebble_checkbox = (CheckedTextView) itemView.findViewById(R.id.item_pebble_checkbox);
|
||||
deviceImageView = (ImageView) itemView.findViewById(R.id.item_image);
|
||||
deviceAppVersionAuthorLabel = (TextView) itemView.findViewById(R.id.item_details);
|
||||
deviceAppNameLabel = (TextView) itemView.findViewById(R.id.item_name);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Lem Dulfo
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -22,6 +22,7 @@ import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v7.widget.CardView;
|
||||
@ -43,14 +44,17 @@ import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ActivitySummariesActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureAlarms;
|
||||
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;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
@ -69,16 +73,16 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
this.deviceList = deviceList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public GBDeviceAdapterv2.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
public GBDeviceAdapterv2.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
this.parent = parent;
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.device_itemv2, parent, false);
|
||||
ViewHolder vh = new ViewHolder(view);
|
||||
return vh;
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, final int position) {
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
|
||||
final GBDevice device = deviceList.get(position);
|
||||
final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
|
||||
|
||||
@ -105,9 +109,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));
|
||||
|
||||
@ -143,7 +145,7 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showTransientSnackbar(R.string.busy_task_fetch_activity_data);
|
||||
GBApplication.deviceService().onFetchActivityData();
|
||||
GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -210,6 +212,20 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
}
|
||||
);
|
||||
|
||||
//show activity tracks
|
||||
holder.showActivityTracks.setVisibility(coordinator.supportsActivityTracks() ? View.VISIBLE : View.GONE);
|
||||
holder.showActivityTracks.setOnClickListener(new View.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent startIntent;
|
||||
startIntent = new Intent(context, ActivitySummariesActivity.class);
|
||||
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
|
||||
context.startActivity(startIntent);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ItemWithDetailsAdapter infoAdapter = new ItemWithDetailsAdapter(context, device.getDeviceInfos());
|
||||
infoAdapter.setHorizontalAlignment(true);
|
||||
holder.deviceInfoList.setAdapter(infoAdapter);
|
||||
@ -232,7 +248,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()
|
||||
|
||||
{
|
||||
@ -275,6 +291,16 @@ 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);
|
||||
}
|
||||
});
|
||||
|
||||
//remove device, hidden under details
|
||||
holder.removeDevice.setOnClickListener(new View.OnClickListener()
|
||||
|
||||
@ -338,6 +364,8 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
ImageView manageAppsView;
|
||||
ImageView setAlarmsView;
|
||||
ImageView showActivityGraphs;
|
||||
ImageView showActivityTracks;
|
||||
ImageView calibrateDevice;
|
||||
|
||||
ImageView deviceInfoView;
|
||||
//overflow
|
||||
@ -348,44 +376,45 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
container = (CardView) view.findViewById(R.id.card_view);
|
||||
container = view.findViewById(R.id.card_view);
|
||||
|
||||
deviceImageView = (ImageView) view.findViewById(R.id.device_image);
|
||||
deviceNameLabel = (TextView) view.findViewById(R.id.device_name);
|
||||
deviceStatusLabel = (TextView) view.findViewById(R.id.device_status);
|
||||
deviceImageView = view.findViewById(R.id.device_image);
|
||||
deviceNameLabel = view.findViewById(R.id.device_name);
|
||||
deviceStatusLabel = view.findViewById(R.id.device_status);
|
||||
|
||||
//actions
|
||||
batteryStatusBox = (LinearLayout) view.findViewById(R.id.device_battery_status_box);
|
||||
batteryStatusLabel = (TextView) view.findViewById(R.id.battery_status);
|
||||
batteryIcon = (ImageView) view.findViewById(R.id.device_battery_status);
|
||||
fetchActivityDataBox = (LinearLayout) view.findViewById(R.id.device_action_fetch_activity_box);
|
||||
fetchActivityData = (ImageView) view.findViewById(R.id.device_action_fetch_activity);
|
||||
busyIndicator = (ProgressBar) view.findViewById(R.id.device_busy_indicator);
|
||||
takeScreenshotView = (ImageView) view.findViewById(R.id.device_action_take_screenshot);
|
||||
manageAppsView = (ImageView) view.findViewById(R.id.device_action_manage_apps);
|
||||
setAlarmsView = (ImageView) view.findViewById(R.id.device_action_set_alarms);
|
||||
showActivityGraphs = (ImageView) view.findViewById(R.id.device_action_show_activity_graphs);
|
||||
deviceInfoView = (ImageView) view.findViewById(R.id.device_info_image);
|
||||
batteryStatusBox = view.findViewById(R.id.device_battery_status_box);
|
||||
batteryStatusLabel = view.findViewById(R.id.battery_status);
|
||||
batteryIcon = view.findViewById(R.id.device_battery_status);
|
||||
fetchActivityDataBox = view.findViewById(R.id.device_action_fetch_activity_box);
|
||||
fetchActivityData = view.findViewById(R.id.device_action_fetch_activity);
|
||||
busyIndicator = view.findViewById(R.id.device_busy_indicator);
|
||||
takeScreenshotView = view.findViewById(R.id.device_action_take_screenshot);
|
||||
manageAppsView = view.findViewById(R.id.device_action_manage_apps);
|
||||
setAlarmsView = view.findViewById(R.id.device_action_set_alarms);
|
||||
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 = (RelativeLayout) view.findViewById(R.id.device_item_infos_box);
|
||||
deviceInfoBox = view.findViewById(R.id.device_item_infos_box);
|
||||
//overflow
|
||||
deviceInfoList = (ListView) view.findViewById(R.id.device_item_infos);
|
||||
findDevice = (ImageView) view.findViewById(R.id.device_action_find);
|
||||
removeDevice = (ImageView) view.findViewById(R.id.device_action_remove);
|
||||
deviceInfoList = view.findViewById(R.id.device_item_infos);
|
||||
findDevice = view.findViewById(R.id.device_action_find);
|
||||
removeDevice = view.findViewById(R.id.device_action_remove);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void justifyListViewHeightBasedOnChildren(ListView listView) {
|
||||
private void justifyListViewHeightBasedOnChildren(ListView listView) {
|
||||
ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
|
||||
|
||||
if (adapter == null) {
|
||||
return;
|
||||
}
|
||||
ViewGroup vg = listView;
|
||||
int totalHeight = 0;
|
||||
for (int i = 0; i < adapter.getCount(); i++) {
|
||||
View listItem = adapter.getView(i, null, vg);
|
||||
View listItem = adapter.getView(i, null, listView);
|
||||
listItem.measure(0, 0);
|
||||
totalHeight += listItem.getMeasuredHeight();
|
||||
}
|
||||
@ -427,11 +456,11 @@ public class GBDeviceAdapterv2 extends RecyclerView.Adapter<GBDeviceAdapterv2.Vi
|
||||
private void showTransientSnackbar(int resource) {
|
||||
Snackbar snackbar = Snackbar.make(parent, resource, Snackbar.LENGTH_SHORT);
|
||||
|
||||
View snackbarView = snackbar.getView();
|
||||
//View snackbarView = snackbar.getView();
|
||||
|
||||
// change snackbar text color
|
||||
int snackbarTextId = android.support.design.R.id.snackbar_text;
|
||||
TextView textView = (TextView) snackbarView.findViewById(snackbarTextId);
|
||||
//int snackbarTextId = android.support.design.R.id.snackbar_text;
|
||||
//TextView textView = snackbarView.findViewById(snackbarTextId);
|
||||
//textView.setTextColor();
|
||||
//snackbarView.setBackgroundColor(Color.MAGENTA);
|
||||
snackbar.show();
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -17,77 +17,33 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
|
||||
|
||||
/**
|
||||
* Adapter for displaying generic ItemWithDetails instances.
|
||||
*/
|
||||
public class ItemWithDetailsAdapter extends ArrayAdapter<ItemWithDetails> {
|
||||
|
||||
public static final int SIZE_SMALL = 1;
|
||||
public static final int SIZE_MEDIUM = 2;
|
||||
public static final int SIZE_LARGE = 3;
|
||||
private final Context context;
|
||||
private boolean horizontalAlignment;
|
||||
private int size = SIZE_MEDIUM;
|
||||
public class ItemWithDetailsAdapter extends AbstractItemAdapter<ItemWithDetails> {
|
||||
|
||||
public ItemWithDetailsAdapter(Context context, List<ItemWithDetails> items) {
|
||||
super(context, 0, items);
|
||||
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void setHorizontalAlignment(boolean horizontalAlignment) {
|
||||
this.horizontalAlignment = horizontalAlignment;
|
||||
super(context, items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent) {
|
||||
ItemWithDetails item = getItem(position);
|
||||
|
||||
if (view == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
if (horizontalAlignment) {
|
||||
view = inflater.inflate(R.layout.item_with_details_horizontal, parent, false);
|
||||
} else {
|
||||
switch (size) {
|
||||
case SIZE_SMALL:
|
||||
view = inflater.inflate(R.layout.item_with_details_small, parent, false);
|
||||
break;
|
||||
default:
|
||||
view = inflater.inflate(R.layout.item_with_details, parent, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageView iconView = (ImageView) view.findViewById(R.id.item_image);
|
||||
TextView nameView = (TextView) view.findViewById(R.id.item_name);
|
||||
TextView detailsView = (TextView) view.findViewById(R.id.item_details);
|
||||
|
||||
nameView.setText(item.getName());
|
||||
detailsView.setText(item.getDetails());
|
||||
iconView.setImageResource(item.getIcon());
|
||||
|
||||
return view;
|
||||
protected String getName(ItemWithDetails item) {
|
||||
return item.getName();
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
@Override
|
||||
protected String getDetails(ItemWithDetails item) {
|
||||
return item.getDetails();
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
@Override
|
||||
protected int getIcon(ItemWithDetails item) {
|
||||
return item.getIcon();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Felix Konstantin Maurer, JohnnySun
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,3 +1,19 @@
|
||||
/* Copyright (C) 2018 Carsten Pfeiffer, Felix Konstantin Maurer
|
||||
|
||||
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.database;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
@ -65,8 +81,9 @@ public class PeriodicExporter extends BroadcastReceiver {
|
||||
return;
|
||||
}
|
||||
Uri dstUri = Uri.parse(dst);
|
||||
OutputStream out = context.getContentResolver().openOutputStream(dstUri);
|
||||
try (OutputStream out = context.getContentResolver().openOutputStream(dstUri)) {
|
||||
helper.exportDB(dbHandler, out);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
GB.updateExportFailedNotification(context.getString(R.string.notif_export_failed_title), context);
|
||||
LOG.info("Exception while exporting DB: ", ex);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2017 protomors
|
||||
/* Copyright (C) 2017-2018 protomors
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -18,6 +18,9 @@ package nodomain.freeyourgadget.gadgetbridge.deviceevents;
|
||||
|
||||
|
||||
public abstract class GBDeviceEvent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": ";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -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,4 @@
|
||||
/* Copyright (C) 2017 Andreas Shimokawa
|
||||
/* Copyright (C) 2017-2018 Andreas Shimokawa
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Carsten Pfeiffer, Daniele Gobbetti
|
||||
/* Copyright (C) 2015-2018 Carsten Pfeiffer, Daniele Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -124,4 +124,14 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
||||
public int getBondingStyle(GBDevice device) {
|
||||
return BONDING_STYLE_ASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracks() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, JohnnySun, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -139,6 +139,14 @@ public interface DeviceCoordinator {
|
||||
*/
|
||||
boolean supportsActivityTracking();
|
||||
|
||||
/**
|
||||
* Indicates whether the device supports recording dedicated activity tracks, like
|
||||
* walking, hiking, running, swimming, etc. and retrieving the recorded
|
||||
* data. This is different from the constant activity tracking since the tracks are
|
||||
* usually recorded with additional features, like e.g. GPS.
|
||||
*/
|
||||
boolean supportsActivityTracks();
|
||||
|
||||
/**
|
||||
* Returns true if activity data fetching is supported AND possible at this
|
||||
* very moment. This will consider the device state (being connected/disconnected/busy...)
|
||||
@ -234,4 +242,16 @@ 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();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer
|
||||
/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
@ -39,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
/**
|
||||
* Provides access to the list of devices managed by Gadgetbridge.
|
||||
@ -149,6 +151,8 @@ public class DeviceManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
GB.updateNotification(selectedDevice, context);
|
||||
|
||||
}
|
||||
|
||||
private void refreshPairedDevices() {
|
||||
@ -163,8 +167,11 @@ public class DeviceManager {
|
||||
Collections.sort(deviceList, new Comparator<GBDevice>() {
|
||||
@Override
|
||||
public int compare(GBDevice lhs, GBDevice rhs) {
|
||||
if (rhs.getStateOrdinal() - lhs.getStateOrdinal() == 0) {
|
||||
return Collator.getInstance().compare(lhs.getName(), rhs.getName());
|
||||
}
|
||||
return (rhs.getStateOrdinal() - lhs.getStateOrdinal());
|
||||
}
|
||||
});
|
||||
notifyDevicesChanged();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele
|
||||
Gobbetti, Julien Pivotto, Kasha, Steffen Liebergeld, Uwe Hermann
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
@ -67,7 +67,7 @@ public interface EventHandler {
|
||||
|
||||
void onAppReorder(UUID uuids[]);
|
||||
|
||||
void onFetchActivityData();
|
||||
void onFetchRecordedData(int dataTypes);
|
||||
|
||||
void onReboot();
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user