diff --git a/.travis.yml b/.travis.yml index be891d615..cf293dad1 100644 --- a/.travis.yml +++ b/.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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 15ef636ed..fcaa33393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,77 @@ ### Changelog +#### 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 + #### Version 0.24.3 * Charts: Try to fix another crash * Pebble: Fix weather for some watchfaces when using background JS @@ -52,7 +124,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 @@ -74,7 +146,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 @@ -121,7 +193,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 @@ -140,7 +212,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 @@ -211,7 +283,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 @@ -253,9 +325,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 @@ -275,7 +347,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 @@ -352,7 +424,7 @@ #### Version 0.13.7 * Pebble: Fix configuration of certain pebble apps (eg. QR Generator, Squared 4.0) -* Pebble: Add context menu option in app manager to search a watchapp in the pebble appstore +* Pebble: Add context menu option in app manager to search a watchapp in the pebble app store * Mi Band: allow to delete Mi Band address from development settings * Mi Band 2: Initial support for heart rate readings (Debug activity only) * Mi Band 2: Support disabled alarms @@ -523,7 +595,7 @@ * Fix enabling log file writing #261 #### Version 0.9.0 -* Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work) +* Pebble: Support for configuring watchfaces/apps locally (clay) or though web browser (some do not work) * Pebble: hide the alarm management activity as it's unsupported * Mi Band: Improve firmware detection and updates, including 1S support * Mi Band: Display HR FW for 1S @@ -671,7 +743,7 @@ * Pebble: Allow to treat K9 notifications as generic notifications (if notification mode is set to never) * Ignore QKSMS notifications to avoid double notification for incoming SMS * Improved UI of Firmware/App installer -* Device state again visible on lockscreen +* Device state again visible on lock screen * Date display and navigation now working properly for all charts #### Version 0.5.2 @@ -714,7 +786,7 @@ * Fixed crash when synchronizing activity data in the graphs activity and changing device orientation #### Version 0.4.4 -* Set Gadgetbridge notification visibility to public, to show the connection status on the lockscreen +* Set Gadgetbridge notification visibility to public, to show the connection status on the lock screen * Support for backup up and restoring of the activity database (via Debug activity) * Support for graceful upgrades and downgrades, keeping your activity database intact * Enhancement to activity graphs: new graphs for sleep data (only last night) accessible swiping right from the main graph @@ -804,7 +876,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) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 325a10829..d43487b68 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -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 * Daniele Gobbetti * João Paulo Barraca -* ivanovlev +* Jonas +* Yaron Shahrabani +* postsorino +* protomors +* Allan Nordhøy +* mueller-ma +* ivanovlev +* naofum +* youzhiran <2668760098@qq.com> +* Tijl Schepens +* TaaviE * Julien Pivotto +* Taavi Eomäe * Steffen Liebergeld * Lem Dulfo +* Hadrián Candela +* Felix Konstantin Maurer * Sergey Trofimov +* Robert Barat +* José Rebelo * JohnnySun * Uwe Hermann +* Edoardo Rosa * Alberto -* 0nse <0nse@users.noreply.github.com> +* Vladislav Serkov +* Vebryn +* Gilles Émilien MOREL * Gergely Peidl +* Bożydar +* 0nse <0nse@users.noreply.github.com> +* Максим Якимчук +* Rimas Raguliūnas +* masakoodaa +* Lukas Veneziano +* Kompact +* Jasper * Christian Fischer +* c4ndel4 * 6arms1leg +* Zhong Jianxin * walkjivefly +* Ted Stein +* NotAFIle * Normano64 +* NicoBuntu +* nautilusx +* Minori Hiraoka (미노리) +* Michal Novotny +* mesnevi +* LL +* Jesús +* exit-failure * Avamander +* AnthonyDiGirolamo +* Andreas Kromke * Ⲇⲁⲛⲓ Φi * Yar -* Yaron Shahrabani * xzovy * xphnx +* Vitaliy Shuruta +* Tomer Rosenfeld +* Tomas Radej +* tiparega <11555126+tiparega@users.noreply.github.com> * Tarik Sekmen * Szymon Tomasz Stefanek +* Sergio Lopez +* Sami Alaoui <4ndroidgeek@gmail.com> * Roman Plevka * rober +* redking +* Quallenauge +* Pavel Motyrev +* Pascal +* Olexandr Nesterenko * Nicolò Balzarotti * Natanael Arndt +* Moarc +* Michal Novak +* michaelneu +* McSym28 +* MaxL +* Martin +* Martin Piatka * Marc Schlaich +* Manuel Soler +* Luiz Felipe das Neves Lopes +* Leonardo Amaral +* lazarosfs +* ladbsoft <30509719+ladbsoft@users.noreply.github.com> +* Kristjan Räts * kevlarcade * Kevin Richter +* Kaz Wolfe * Kasha +* Joseph Kim +* Jan Lolek +* Jakub Jelínek * Ivan * Hasan Ammar * Gilles MOREL -* Gilles Émilien MOREL +* Gideão Gomes Ferreira +* Gabe Schrecker +* freezed-or-frozen +* Frank Slezak +* Davis Mosenkovs * Daniel Hauck +* criogenic * Chris Perelstein +* chabotsi * Carlos Ferreira +* bucala +* batataspt@gmail.com * atkyritsis +* AndrewH <36428679+andrewheadricke@users.noreply.github.com> * andre +* Allen B <28495335+Allen-B1@users.noreply.github.com> * Alexey Afanasev 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* diff --git a/FEATURES.md b/FEATURES.md index fbd3740ed..4ed6a00e5 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -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 | diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java index bc20ae128..31a07b46e 100644 --- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java @@ -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,10 +68,13 @@ public class GBDaoGenerator { addHPlusHealthActivityKindOverlay(schema, user, device); addHPlusHealthActivitySample(schema, user, device); addNo1F1ActivitySample(schema, user, device); + addXWatchActivitySample(schema, user, device); addZeTimeActivitySample(schema, user, device); addCalendarSyncState(schema, device); + addBipActivitySummary(schema, user, device); + new DaoGenerator().generateAll(schema, "app/src/main/java"); } @@ -270,6 +276,17 @@ 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(); @@ -309,6 +326,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())) { diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java.orig b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java.orig new file mode 100644 index 000000000..ea1897d5d --- /dev/null +++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java.orig @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2011 Markus Junginger, greenrobot (http://greenrobot.de) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nodomain.freeyourgadget.gadgetbridge.daogen; + +import java.util.Date; + +import de.greenrobot.daogenerator.DaoGenerator; +import de.greenrobot.daogenerator.Entity; +import de.greenrobot.daogenerator.Index; +import de.greenrobot.daogenerator.Property; +import de.greenrobot.daogenerator.Schema; + +/** + * Generates entities and DAOs for the example project DaoExample. + * Automatically run during build. + */ +public class GBDaoGenerator { + + private static final String VALID_FROM_UTC = "validFromUTC"; + private static final String VALID_TO_UTC = "validToUTC"; + 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"; + private static final String SAMPLE_RAW_KIND = "rawKind"; + private static final String SAMPLE_HEART_RATE = "heartRate"; + private static final String TIMESTAMP_FROM = "timestampFrom"; + private static final String TIMESTAMP_TO = "timestampTo"; + + + public static void main(String[] args) throws Exception { + Schema schema = new Schema(18, MAIN_PACKAGE + ".entities"); + + Entity userAttributes = addUserAttributes(schema); + Entity user = addUserInfo(schema, userAttributes); + + Entity deviceAttributes = addDeviceAttributes(schema); + Entity device = addDevice(schema, deviceAttributes); + + // yeah deep shit, has to be here (after device) for db upgrade and column order + // because addDevice adds a property to deviceAttributes also.... + deviceAttributes.addStringProperty("volatileIdentifier"); + + Entity tag = addTag(schema); + Entity userDefinedActivityOverlay = addActivityDescription(schema, tag, user); + + addMiBandActivitySample(schema, user, device); + addPebbleHealthActivitySample(schema, user, device); + addPebbleHealthActivityKindOverlay(schema, user, device); + addPebbleMisfitActivitySample(schema, user, device); + addPebbleMorpheuzActivitySample(schema, user, device); + addHPlusHealthActivityKindOverlay(schema, user, device); + addHPlusHealthActivitySample(schema, user, device); + addNo1F1ActivitySample(schema, user, device); +<<<<<<< HEAD + addZeTimeActivitySample(schema, user, device); +======= + addXWatchActivitySample(schema, user, device); +>>>>>>> master + + addCalendarSyncState(schema, device); + + addBipActivitySummary(schema, user, device); + + new DaoGenerator().generateAll(schema, "app/src/main/java"); + } + + private static Entity addTag(Schema schema) { + Entity tag = addEntity(schema, "Tag"); + tag.addIdProperty(); + tag.addStringProperty("name").notNull(); + tag.addStringProperty("description").javaDocGetterAndSetter("An optional description of this tag."); + tag.addLongProperty("userId").notNull(); + + return tag; + } + + private static Entity addActivityDescription(Schema schema, Entity tag, Entity user) { + Entity activityDesc = addEntity(schema, "ActivityDescription"); + activityDesc.setJavaDoc("A user may further specify his activity with a detailed description and the help of tags.\nOne or more tags can be added to a given activity range."); + activityDesc.addIdProperty(); + activityDesc.addIntProperty(TIMESTAMP_FROM).notNull(); + activityDesc.addIntProperty(TIMESTAMP_TO).notNull(); + activityDesc.addStringProperty("details").javaDocGetterAndSetter("An optional detailed description, specific to this very activity occurrence."); + + Property userId = activityDesc.addLongProperty("userId").notNull().getProperty(); + activityDesc.addToOne(user, userId); + + Entity activityDescTagLink = addEntity(schema, "ActivityDescTagLink"); + activityDescTagLink.addIdProperty(); + Property sourceId = activityDescTagLink.addLongProperty("activityDescriptionId").notNull().getProperty(); + Property targetId = activityDescTagLink.addLongProperty("tagId").notNull().getProperty(); + + activityDesc.addToMany(tag, activityDescTagLink, sourceId, targetId); + + return activityDesc; + } + + private static Entity addUserInfo(Schema schema, Entity userAttributes) { + Entity user = addEntity(schema, "User"); + user.addIdProperty(); + user.addStringProperty("name").notNull(); + user.addDateProperty("birthday").notNull(); + user.addIntProperty("gender").notNull(); + Property userId = userAttributes.addLongProperty("userId").notNull().getProperty(); + + // sorted by the from-date, newest first + Property userAttributesSortProperty = getPropertyByName(userAttributes, VALID_FROM_UTC); + user.addToMany(userAttributes, userId).orderDesc(userAttributesSortProperty); + + return user; + } + + private static Property getPropertyByName(Entity entity, String propertyName) { + for (Property prop : entity.getProperties()) { + if (propertyName.equals(prop.getPropertyName())) { + return prop; + } + } + throw new IllegalStateException("Could not find property " + propertyName + " in entity " + entity.getClassName()); + } + + private static Entity addUserAttributes(Schema schema) { + // additional properties of a user, which may change during the lifetime of a user + // this allows changing attributes while preserving user identity + Entity userAttributes = addEntity(schema, "UserAttributes"); + userAttributes.addIdProperty(); + userAttributes.addIntProperty("heightCM").notNull(); + userAttributes.addIntProperty("weightKG").notNull(); + userAttributes.addIntProperty("sleepGoalHPD").javaDocGetterAndSetter("Desired number of hours of sleep per day."); + userAttributes.addIntProperty("stepsGoalSPD").javaDocGetterAndSetter("Desired number of steps per day."); + addDateValidityTo(userAttributes); + + return userAttributes; + } + + private static void addDateValidityTo(Entity entity) { + entity.addDateProperty(VALID_FROM_UTC).codeBeforeGetter(OVERRIDE); + entity.addDateProperty(VALID_TO_UTC).codeBeforeGetter(OVERRIDE); + + entity.implementsInterface(VALID_BY_DATE); + } + + private static Entity addDevice(Schema schema, Entity deviceAttributes) { + Entity device = addEntity(schema, "Device"); + device.addIdProperty(); + device.addStringProperty("name").notNull(); + device.addStringProperty("manufacturer").notNull(); + device.addStringProperty("identifier").notNull().unique().javaDocGetterAndSetter("The fixed identifier, i.e. MAC address of the device."); + device.addIntProperty("type").notNull().javaDocGetterAndSetter("The DeviceType key, i.e. the GBDevice's type."); + device.addStringProperty("model").javaDocGetterAndSetter("An optional model, further specifying the kind of device-"); + Property deviceId = deviceAttributes.addLongProperty("deviceId").notNull().getProperty(); + // sorted by the from-date, newest first + Property deviceAttributesSortProperty = getPropertyByName(deviceAttributes, VALID_FROM_UTC); + device.addToMany(deviceAttributes, deviceId).orderDesc(deviceAttributesSortProperty); + + return device; + } + + private static Entity addDeviceAttributes(Schema schema) { + Entity deviceAttributes = addEntity(schema, "DeviceAttributes"); + deviceAttributes.addIdProperty(); + deviceAttributes.addStringProperty("firmwareVersion1").notNull(); + deviceAttributes.addStringProperty("firmwareVersion2"); + addDateValidityTo(deviceAttributes); + + return deviceAttributes; + } + + private static Entity addMiBandActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "MiBandActivitySample"); + activitySample.implementsSerializable(); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + return activitySample; + } + + private static void addHeartRateProperties(Entity activitySample) { + activitySample.addIntProperty(SAMPLE_HEART_RATE).notNull().codeBeforeGetterAndSetter(OVERRIDE); + } + + private static Entity addPebbleHealthActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "PebbleHealthActivitySample"); + addCommonActivitySampleProperties("AbstractPebbleHealthActivitySample", activitySample, user, device); + activitySample.addByteArrayProperty("rawPebbleHealthData").codeBeforeGetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + return activitySample; + } + + private static Entity addPebbleHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { + Entity activityOverlay = addEntity(schema, "PebbleHealthActivityOverlay"); + + activityOverlay.addIntProperty(TIMESTAMP_FROM).notNull().primaryKey(); + activityOverlay.addIntProperty(TIMESTAMP_TO).notNull().primaryKey(); + activityOverlay.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); + Property deviceId = activityOverlay.addLongProperty("deviceId").primaryKey().notNull().getProperty(); + activityOverlay.addToOne(device, deviceId); + + Property userId = activityOverlay.addLongProperty("userId").notNull().getProperty(); + activityOverlay.addToOne(user, userId); + activityOverlay.addByteArrayProperty("rawPebbleHealthData"); + + return activityOverlay; + } + + private static Entity addPebbleMisfitActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "PebbleMisfitSample"); + addCommonActivitySampleProperties("AbstractPebbleMisfitActivitySample", activitySample, user, device); + activitySample.addIntProperty("rawPebbleMisfitSample").notNull().codeBeforeGetter(OVERRIDE); + return activitySample; + } + + private static Entity addPebbleMorpheuzActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "PebbleMorpheuzSample"); + addCommonActivitySampleProperties("AbstractPebbleMorpheuzActivitySample", activitySample, user, device); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + return activitySample; + } + + private static Entity addHPlusHealthActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "HPlusHealthActivitySample"); + activitySample.implementsSerializable(); + addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device); + activitySample.addByteArrayProperty("rawHPlusHealthData"); + activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); + activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE); + activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE); + addHeartRateProperties(activitySample); + activitySample.addIntProperty("distance"); + activitySample.addIntProperty("calories"); + + return activitySample; + } + + private static Entity addHPlusHealthActivityKindOverlay(Schema schema, Entity user, Entity device) { + Entity activityOverlay = addEntity(schema, "HPlusHealthActivityOverlay"); + + activityOverlay.addIntProperty(TIMESTAMP_FROM).notNull().primaryKey(); + activityOverlay.addIntProperty(TIMESTAMP_TO).notNull().primaryKey(); + activityOverlay.addIntProperty(SAMPLE_RAW_KIND).notNull().primaryKey(); + Property deviceId = activityOverlay.addLongProperty("deviceId").primaryKey().notNull().getProperty(); + activityOverlay.addToOne(device, deviceId); + + Property userId = activityOverlay.addLongProperty("userId").notNull().getProperty(); + activityOverlay.addToOne(user, userId); + activityOverlay.addByteArrayProperty("rawHPlusHealthData"); + return activityOverlay; + } + + private static Entity addNo1F1ActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "No1F1ActivitySample"); + 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; + } + +<<<<<<< HEAD + private static Entity addZeTimeActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "ZeTimeActivitySample"); +======= + private static Entity addXWatchActivitySample(Schema schema, Entity user, Entity device) { + Entity activitySample = addEntity(schema, "XWatchActivitySample"); +>>>>>>> master + 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 void addCommonActivitySampleProperties(String superClass, Entity activitySample, Entity user, Entity device) { + activitySample.setSuperclass(superClass); + activitySample.addImport(MAIN_PACKAGE + ".devices.SampleProvider"); + activitySample.setJavaDoc( + "This class represents a sample specific to the device. Values like activity kind or\n" + + "intensity, are device specific. Normalized values can be retrieved through the\n" + + "corresponding {@link SampleProvider}."); + activitySample.addIntProperty("timestamp").notNull().codeBeforeGetterAndSetter(OVERRIDE).primaryKey(); + Property deviceId = activitySample.addLongProperty("deviceId").primaryKey().notNull().codeBeforeGetterAndSetter(OVERRIDE).getProperty(); + activitySample.addToOne(device, deviceId); + Property userId = activitySample.addLongProperty("userId").notNull().codeBeforeGetterAndSetter(OVERRIDE).getProperty(); + activitySample.addToOne(user, userId); + } + + private static void addCalendarSyncState(Schema schema, Entity device) { + Entity calendarSyncState = addEntity(schema, "CalendarSyncState"); + calendarSyncState.addIdProperty(); + Property deviceId = calendarSyncState.addLongProperty("deviceId").notNull().getProperty(); + Property calendarEntryId = calendarSyncState.addLongProperty("calendarEntryId").notNull().getProperty(); + Index indexUnique = new Index(); + indexUnique.addProperty(deviceId); + indexUnique.addProperty(calendarEntryId); + indexUnique.makeUnique(); + calendarSyncState.addIndex(indexUnique); + calendarSyncState.addToOne(device, deviceId); + 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())) { + return prop; + } + } + throw new IllegalArgumentException("Property " + propertyName + " not found in Entity " + entity.getClassName()); + } + + private static Entity addEntity(Schema schema, String className) { + Entity entity = schema.addEntity(className); + entity.addImport("de.greenrobot.dao.AbstractDao"); + return entity; + } +} diff --git a/README.md b/README.md index 32c8510ce..7648d0cfc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ 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) @@ -33,6 +33,7 @@ vendor's servers. * NO.1 F1 (WIP) * Liveview * Vibratissimo (experimental) +* XWatch (Affordable Chinese Casio-like smartwatches) ## Features @@ -94,7 +95,7 @@ For more information read [this wiki article](https://github.com/Freeyourgadget/ ## Contribute -Contributions are welcome, be it feedback, bugreports, documentation, translation, research or code. Feel free to work +Contributions are welcome, be it feedback, bug reports, documentation, translation, research or code. Feel free to work on any of the open [issues](https://github.com/Freeyourgadget/Gadgetbridge/issues?q=is%3Aopen+is%3Aissue); just leave a comment that you're working on one to avoid duplicated work. diff --git a/app/build.gradle b/app/build.gradle index b77775739..ae860a867 100644 --- a/app/build.gradle +++ b/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 +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.3" - versionCode 120 + // Note: always bump BOTH versionCode and versionName! + versionName "0.27.0" + versionCode 132 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 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a36e3301d..bf3ae12fb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + @@ -37,7 +38,8 @@ 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"> + + android:resource="@xml/shared_paths" /> diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java index 7ee81ed53..f7c0e442b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBApplication.java @@ -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,58 +373,119 @@ public class GBApplication extends Application { return NotificationManager.INTERRUPTION_FILTER_ALL; } - private static HashSet apps_blacklist = null; + private static HashSet 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 packageNames) { + public static void setAppsNotifBlackList(Set 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) 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) 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 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 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) 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 calendars_blacklist = null; public static boolean calendarIsBlacklisted(String calendarDisplayName) { @@ -406,7 +497,7 @@ public class GBApplication extends Application { public static void setCalendarsBlackList(Set 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"; + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBEnvironment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBEnvironment.java index 571a0bbc0..01638a87f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBEnvironment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBEnvironment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBException.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBException.java index 6518dac19..e415940e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBException.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GBException.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java index 86c7f1153..abf1934bf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LockHandler.java @@ -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 { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java index 30bf3af1f..b21e634ed 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/Logging.java @@ -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)); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LoggingExceptionHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LoggingExceptionHandler.java index 2f63ab96b..ffc8fbc5b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LoggingExceptionHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/LoggingExceptionHandler.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java index 23d466b34..b896a69d3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/SleepAlarmWidget.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 0nse, Carsten Pfeiffer +/* Copyright (C) 2016-2018 0nse, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractFragmentPagerAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractFragmentPagerAdapter.java index 163651949..81ea18512 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractFragmentPagerAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractFragmentPagerAdapter.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java index d8c50ded2..9bd477ca4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragment.java index b333d0977..971f1f2da 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java index 2390aedff..998be3e85 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractGBFragmentActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractListActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractListActivity.java new file mode 100644 index 000000000..b4c1b6199 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractListActivity.java @@ -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 . */ +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 extends AbstractGBActivity { + private AbstractItemAdapter itemAdapter; + private ListView itemListView; + + public void setItemAdapter(AbstractItemAdapter itemAdapter) { + this.itemAdapter = itemAdapter; + itemListView.setAdapter(itemAdapter); + } + + protected void refresh() { + this.itemAdapter.loadItems(); + } + + public AbstractItemAdapter 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); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java index 1cdb0a158..d8ad9fe7f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AbstractSettingsActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java new file mode 100644 index 000000000..9f73841c7 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ActivitySummariesActivity.java @@ -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 . */ +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 { + + 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 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 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 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 paths){ + + ArrayList 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); + } + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java index d0cfa085d..20d653c88 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AlarmDetails.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java index a2c39bbcf..cf7aa1ed5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AndroidPairingActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java index 20ea96cc9..3ce61409a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/AppBlacklistActivity.java @@ -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); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java index 88a6b2a20..42e512152 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/CalBlacklistActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 3528c82be..876a19edc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java index 171ee0159..6071254e0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ControlCenterv2.java @@ -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 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 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 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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java index 782f43a24..c54f91cff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DbManagementActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index 70a9672d4..3a1d8391b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -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,11 +77,9 @@ 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; - } } } }; @@ -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 spinnerArray = new ArrayList<>(); for (NotificationType notificationType : NotificationType.values()) { spinnerArray.add(notificationType.name()); } ArrayAdapter 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,7 +273,9 @@ public class DebugActivity extends AbstractGBActivity { .setContentIntent(pendingIntent) .extend(wearableExtender); - nManager.notify((int) System.currentTimeMillis(), ncomp.build()); + if (nManager != null) { + nManager.notify((int) System.currentTimeMillis(), ncomp.build()); + } } @Override @@ -295,7 +295,4 @@ public class DebugActivity extends AbstractGBActivity { unregisterReceiver(mReceiver); } - public interface DeviceSelectionCallback { - void invoke(GBDevice device); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java index b7af301cf..3ef33a824 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DiscoveryActivity.java @@ -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; } @@ -265,7 +264,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 +272,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); @@ -443,10 +442,15 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void stopNewBTLEDiscovery() { - adapter.getBluetoothLeScanner().stopScan(newLeScanCallback); + BluetoothLeScanner bluetoothLeScanner = adapter.getBluetoothLeScanner(); + if (bluetoothLeScanner == null) { + LOG.warn("could not get BluetoothLeScanner()!"); + 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 +534,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 +615,14 @@ public class DiscoveryActivity extends AbstractGBActivity implements AdapterView } } } + + @Override + protected void onPause() { + super.onPause(); + stopBTDiscovery(); + stopBTLEDiscovery(); + if (GB.supportsBluetoothLE()) { + stopNewBTLEDiscovery(); + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java index ade8f8589..e26e54c36 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ExternalPebbleJSActivity.java @@ -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()); + } - if (extras.getBoolean(START_BG_WEBVIEW, false)) { - startBackgroundWebViewAndFinish(); - return; - } + //first check if we are still connected to a pebble + DeviceManager deviceManager = ((GBApplication) getApplication()).getDeviceManager(); + List 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; + } + } + } - GBDevice currentDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE); - UUID currentUUID = (UUID) extras.getSerializable(DeviceService.EXTRA_APP_UUID); + 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; + } + 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); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java index 46b4b7190..eb1fb842b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FindPhoneActivity.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java index 28b57e464..5ab284a7d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/FwAppInstallerActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java index 13a5d640c..7be507add 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/GBActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java index 3c7f9b394..a2f3b0734 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/HeartRateUtils.java @@ -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; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/InstallActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/InstallActivity.java index 4af5405e6..c95946538 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/InstallActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/InstallActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index 8316564bc..f76f4731f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -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,7 @@ 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_DISPLAY_ITEMS; 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 +326,20 @@ public class SettingsActivity extends AbstractSettingsActivity { } }); + 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; + } + }); + // Get all receivers of Media Buttons Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); @@ -377,7 +387,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,20 +396,21 @@ public class SettingsActivity extends AbstractSettingsActivity { } Uri uri = Uri.parse(autoExportLocation); try { - String filePath = AndroidUtils.getFilePath(getApplicationContext(), uri); - if (filePath != null) { - return filePath; + return AndroidUtils.getFilePath(getApplicationContext(), uri); + } catch (IllegalArgumentException e) { + try { + Cursor cursor = getContentResolver().query( + uri, + new String[]{DocumentsContract.Document.COLUMN_DISPLAY_NAME}, + null, null, null, null + ); + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)); + } + } + catch (Exception fdfsdfds) { + LOG.warn("fuck"); } - } catch (URISyntaxException e) { - return ""; - } - Cursor cursor = getContentResolver().query( - uri, - new String[] { DocumentsContract.Document.COLUMN_DISPLAY_NAME }, - null, null, null, null - ); - if (cursor != null && cursor.moveToFirst()) { - return cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME)); } return ""; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java index 076d8d1c3..1901747b1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/VibrationActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java index 9ee0e3995..110c9895e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AbstractAppManagerFragment.java @@ -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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java index 80dd7678d..6d4925104 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java index d86a62e72..e77a5647c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentCache.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java index a890879cb..f1d05af8a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledApps.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java index 5cf407d13..4bee64475 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/appmanager/AppManagerFragmentInstalledWatchfaces.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java index 48cdb2793..e9fa8f020 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java @@ -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 notWornEntries = new ArrayList<>(numEntries); boolean hr = supportsHeartrate(gbDevice); List heartrateEntries = hr ? new ArrayList(numEntries) : null; + List colors = new ArrayList<>(numEntries); // this is kinda inefficient... int lastHrSampleIndex = -1; for (int i = 0; i < numEntries; i++) { @@ -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. * diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java index e9d63d800..4fb76c56e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java index db82772d8..493a59702 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityAnalysis.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java index aaeeb6b9a..d12f03f48 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java index d857800a4..778d83303 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java @@ -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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsData.java index 15b17038c..ff53f4ae1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsData.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java index 0f92cd2c5..a61b0262f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/CustomBarChart.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/CustomBarChart.java index 44ae446b0..2942da446 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/CustomBarChart.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/CustomBarChart.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java index c91023035..53c7563fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java @@ -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(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SingleEntryValueAnimator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SingleEntryValueAnimator.java index 053d3ba1a..4ca0f0dfc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SingleEntryValueAnimator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SingleEntryValueAnimator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index 419150adb..51672e5d8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepUtils.java index 81d684b6e..33ecef693 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepUtils.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java index bab6754fd..c255f3707 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java index 76e89f373..10631b031 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TimestampValueFormatter.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java index 89550ca7a..36e1f7a46 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TrailingActivitySample.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java index f0ea551fe..2ee3c0eb4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java index 86cc382c6..32be63c45 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractItemAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractItemAdapter.java new file mode 100644 index 000000000..09bd831e5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AbstractItemAdapter.java @@ -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 . */ +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 extends ArrayAdapter { + + public static final int SIZE_SMALL = 1; + public static final int SIZE_MEDIUM = 2; + public static final int SIZE_LARGE = 3; + private final Context context; + private final List items; + private boolean horizontalAlignment; + private int size = SIZE_MEDIUM; + + public AbstractItemAdapter(Context context) { + this (context, new ArrayList()); + } + + public AbstractItemAdapter(Context context, List 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 getItems() { + return items; + } + + public void loadItems() { + } + + public void setItems(List items, boolean notify) { + this.items.clear(); + this.items.addAll(items); + if (notify) { + notifyDataSetChanged(); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java new file mode 100644 index 000000000..544ff5f58 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/ActivitySummariesAdapter.java @@ -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 . */ +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 { + 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 qb = summaryDao.queryBuilder(); + qb.where(BaseActivitySummaryDao.Properties.DeviceId.eq(dbDevice.getId())).orderDesc(BaseActivitySummaryDao.Properties.StartTime); + List 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()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java index f7ae70bfd..1e562ad95 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/AppBlacklistAdapter.java @@ -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 implements Filterable { private List applicationInfoList; @@ -62,7 +66,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter apps_blacklist = new HashSet<>(); + List 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 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 { - - 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 { public ItemWithDetailsAdapter(Context context, List 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(); } + } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java index 0e3e94cb5..515493dbf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/contentprovider/PebbleContentProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBAccess.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBAccess.java index 86caa92df..79bc38313 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBAccess.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBAccess.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java index 3b25e33a2..80d99c507 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java index 07ba2a6da..e6d53720c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBHelper.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java index 0b109076d..7c4eb6c79 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBOpenHelper.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBUpdateScript.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBUpdateScript.java index d6e96adba..5449d15b6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBUpdateScript.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/DBUpdateScript.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java index f9665bf4b..5c5fccb36 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/PeriodicExporter.java @@ -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 . */ 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); - helper.exportDB(dbHandler, out); + 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); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java index 3e8272c3c..e0191ed2c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_14.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java index e0627d2ef..b2ff50ca5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_15.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java index 04c948539..707386b52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/GadgetbridgeUpdate_17.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 protomors +/* Copyright (C) 2017-2018 protomors This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java index 02ad310e2..8e2f6340d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/SchemaMigration.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java index d4d6518cc..f0ee21601 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppInfo.java index 1591975f5..fc3c17d00 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java index bd2caa3c6..1000861aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppMessage.java index 58028151c..d835ea1c0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppMessage.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventBatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventBatteryInfo.java index 1c6c89992..3f226e4f3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventBatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventBatteryInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventCallControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventCallControl.java index 2f776a9b9..a2240865a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventCallControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventCallControl.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java index 12722ac7d..7b45fb427 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventDisplayMessage.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventFindPhone.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventFindPhone.java index a708ec05b..318a73340 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventFindPhone.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventFindPhone.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventMusicControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventMusicControl.java index 04529fc80..9dd88bc15 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventMusicControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventMusicControl.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java index 4c2e4b990..1e7037b17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventNotificationControl.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventScreenshot.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventScreenshot.java index 17215e60e..1d11e9323 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventScreenshot.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventScreenshot.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSendBytes.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSendBytes.java index ea49c8bf7..4f4c4003f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSendBytes.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSendBytes.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSleepMonitorResult.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSleepMonitorResult.java index ea5863181..c6b24c5f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSleepMonitorResult.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventSleepMonitorResult.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventVersionInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventVersionInfo.java index 1ba7cb526..aa5a8f2fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventVersionInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventVersionInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java index a6b7be913..6ad638c69 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/pebble/GBDeviceEventDataLogging.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index 7c40f7304..6acbe6e4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -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,9 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { public int getBondingStyle(GBDevice device) { return BONDING_STYLE_ASK; } + + @Override + public boolean supportsActivityTracks() { + return false; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index 701b812be..84cc2db74 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index a1cdabf23..45d4150eb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -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...) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java index 445d5479a..77da965aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceManager.java @@ -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,7 +167,10 @@ public class DeviceManager { Collections.sort(deviceList, new Comparator() { @Override public int compare(GBDevice lhs, GBDevice rhs) { - return Collator.getInstance().compare(lhs.getName(), rhs.getName()); + if (rhs.getStateOrdinal() - lhs.getStateOrdinal() == 0) { + return Collator.getInstance().compare(lhs.getName(), rhs.getName()); + } + return (rhs.getStateOrdinal() - lhs.getStateOrdinal()); } }); notifyDevicesChanged(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index 8d6e86939..4acf58aea 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -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(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java index af533005f..2bbfa22b0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/InstallHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java index 3af113b31..543f22777 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/SampleProvider.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java index 3b9b7d9d4..e35417449 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/UnknownDeviceCoordinator.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/amazfitbip/BipActivitySummary.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/amazfitbip/BipActivitySummary.java new file mode 100644 index 000000000..d38332bea --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/amazfitbip/BipActivitySummary.java @@ -0,0 +1,222 @@ +/* Copyright (C) 2017-2018 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.amazfitbip; + +import java.util.Date; + +import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; + +public class BipActivitySummary extends BaseActivitySummary { + private int version; + private float distanceMeters; + private float ascentMeters; + private float descentMeters; + private float minAltitude; + private float maxAltitude; + private int minLatitude; + private int maxLatitude; + private int minLongitude; + private int maxLongitude; + private long steps; + private long activeTimeSeconds; + private float caloriesBurnt; + private float maxSpeed; + private float minPace; + private float maxPace; + private float totalStride; + private long timeAscent; + private long timeDescent; + private long timeFlat; + private int averageHR; + private int averagePace; + private int averageStride; + +// @Override +// public long getSteps() { +// return steps; +// } +// +// @Override +// public float getDistanceMeters() { +// return distanceMeters; +// } +// +// @Override +// public float getAscentMeters() { +// return ascentMeters; +// } +// +// @Override +// public float getDescentMeters() { +// return descentMeters; +// } +// +// @Override +// public float getMinAltitude() { +// return minAltitude; +// } +// +// @Override +// public float getMaxAltitude() { +// return maxAltitude; +// } +// +// @Override +// public float getCalories() { +// return caloriesBurnt; +// } +// +// @Override +// public float getMaxSpeed() { +// return maxSpeed; +// } +// +// @Override +// public float getMinSpeed() { +// return minPace; +// } +// +// @Override +// public float getAverageSpeed() { +// return averagePace; +// } + + public void setVersion(int version) { + this.version = version; + } + + public int getVersion() { + return version; + } + + public void setDistanceMeters(float distanceMeters) { + this.distanceMeters = distanceMeters; + } + + public void setAscentMeters(float ascentMeters) { + this.ascentMeters = ascentMeters; + } + + public void setDescentMeters(float descentMeters) { + this.descentMeters = descentMeters; + } + + public void setMinAltitude(float minAltitude) { + this.minAltitude = minAltitude; + } + + public void setMaxAltitude(float maxAltitude) { + this.maxAltitude = maxAltitude; + } + + public void setMinLatitude(int minLatitude) { + this.minLatitude = minLatitude; + } + + public void setMaxLatitude(int maxLatitude) { + this.maxLatitude = maxLatitude; + } + + public void setMinLongitude(int minLongitude) { + this.minLongitude = minLongitude; + } + + public void setMaxLongitude(int maxLongitude) { + this.maxLongitude = maxLongitude; + } + + public void setSteps(long steps) { + this.steps = steps; + } + + public void setActiveTimeSeconds(long activeTimeSeconds) { + this.activeTimeSeconds = activeTimeSeconds; + } + + public void setCaloriesBurnt(float caloriesBurnt) { + this.caloriesBurnt = caloriesBurnt; + } + + public void setMaxSpeed(float maxSpeed) { + this.maxSpeed = maxSpeed; + } + + public void setMinPace(float minPace) { + this.minPace = minPace; + } + + public void setMaxPace(float maxPace) { + this.maxPace = maxPace; + } + + public void setTotalStride(float totalStride) { + this.totalStride = totalStride; + } + + public float getTotalStride() { + return totalStride; + } + + public void setTimeAscent(long timeAscent) { + this.timeAscent = timeAscent; + } + + public long getTimeAscent() { + return timeAscent; + } + + public void setTimeDescent(long timeDescent) { + this.timeDescent = timeDescent; + } + + public long getTimeDescent() { + return timeDescent; + } + + public void setTimeFlat(long timeFlat) { + this.timeFlat = timeFlat; + } + + public long getTimeFlat() { + return timeFlat; + } + + public void setAverageHR(int averageHR) { + this.averageHR = averageHR; + } + + public int getAverageHR() { + return averageHR; + } + + public void setAveragePace(int averagePace) { + this.averagePace = averagePace; + } + + public int getAveragePace() { + return averagePace; + } + + public void setAverageStride(int averageStride) { + this.averageStride = averageStride; + } + + public int getAverageStride() { + return averageStride; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java index a630bb92e..1cfb0e20d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/EXRIZUK8Coordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca, Quallenauge +/* Copyright (C) 2017-2018 João Paulo Barraca, Quallenauge This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java index e8c7e6932..7989398dc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusConstants.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, João +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java index 0cce909b3..44499980b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, João +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index 4a605809a..dc816addc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2017-2018 Andreas Shimokawa, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java index 23041f6d0..d04c4ea50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/MakibesF68Coordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/Q8Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/Q8Coordinator.java new file mode 100644 index 000000000..7277d0e85 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/Q8Coordinator.java @@ -0,0 +1,55 @@ +/* Copyright (C) 2017-2018 João Paulo Barraca, tiparega + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.hplus; + +/* +* @author Alejandro Ladera Chamorro <11555126+tiparega@users.noreply.github.com> +*/ + + +import android.support.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +/** + * Pseudo Coordinator for the Q8, a sub type of the HPLUS devices + */ +public class Q8Coordinator extends HPlusCoordinator { + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if(name != null && name.startsWith("Q8")){ + return DeviceType.Q8; + } + + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.Q8; + } + + @Override + public String getManufacturer() { + return "Makibes"; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/UsedConfiguration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/ActivateDisplayOnLift.java similarity index 63% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/UsedConfiguration.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/ActivateDisplayOnLift.java index 01b174365..eb4e4c1d2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/database/UsedConfiguration.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/ActivateDisplayOnLift.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2017-2018 Andreas Shimokawa, José Rebelo This file is part of Gadgetbridge. @@ -14,19 +14,10 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.database; +package nodomain.freeyourgadget.gadgetbridge.devices.huami; -/** - * Contains the configuration used for particular activity samples. - */ -public class UsedConfiguration { - String fwVersion; - String userName; - short userWeight; - short userSize; - // ... - int usedFrom; // timestamp - int usedUntil; // timestamp - short sleepGoal; // minutes - short stepsGoal; +public enum ActivateDisplayOnLift { + ON, + OFF, + SCHEDULED } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java new file mode 100644 index 000000000..a4bc82811 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java @@ -0,0 +1,89 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huami; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public class HuamiConst { + // observed the following values so far: + // 00 01 02 09 0a 0b 0c 10 11 + + // 0 = same activity kind as before + // 1 = light activity walking? + // 3 = definitely non-wear + // 9 = probably light sleep, definitely some kind of sleep + // 10 = ignore, except for hr (if valid) + // 11 = probably deep sleep + // 12 = definitely wake up + // 17 = definitely not sleep related + + public static final int TYPE_UNSET = -1; + public static final int TYPE_NO_CHANGE = 0; + public static final int TYPE_ACTIVITY = 1; + public static final int TYPE_RUNNING = 2; + public static final int TYPE_NONWEAR = 3; + public static final int TYPE_RIDE_BIKE = 4; + public static final int TYPE_CHARGING = 6; + public static final int TYPE_LIGHT_SLEEP = 9; + public static final int TYPE_IGNORE = 10; + public static final int TYPE_DEEP_SLEEP = 11; + public static final int TYPE_WAKE_UP = 12; + + + public static final String MI_BAND2_NAME = "MI Band 2"; + public static final String MI_BAND2_NAME_HRX = "Mi Band HRX"; + public static final String MI_BAND3_NAME = "Mi Band 3"; + + public static int toActivityKind(int rawType) { + switch (rawType) { + case TYPE_DEEP_SLEEP: + return ActivityKind.TYPE_DEEP_SLEEP; + case TYPE_LIGHT_SLEEP: + return ActivityKind.TYPE_LIGHT_SLEEP; + case TYPE_ACTIVITY: + case TYPE_RUNNING: + case TYPE_WAKE_UP: + return ActivityKind.TYPE_ACTIVITY; + case TYPE_NONWEAR: + return ActivityKind.TYPE_NOT_WORN; + case TYPE_CHARGING: + return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption + case TYPE_RIDE_BIKE: + return ActivityKind.TYPE_CYCLING; + default: + case TYPE_UNSET: // fall through + return ActivityKind.TYPE_UNKNOWN; + } + } + + public static int toRawActivityType(int activityKind) { + switch (activityKind) { + case ActivityKind.TYPE_ACTIVITY: + return TYPE_ACTIVITY; + case ActivityKind.TYPE_DEEP_SLEEP: + return TYPE_DEEP_SLEEP; + case ActivityKind.TYPE_LIGHT_SLEEP: + return TYPE_LIGHT_SLEEP; + case ActivityKind.TYPE_NOT_WORN: + return TYPE_NONWEAR; + case ActivityKind.TYPE_UNKNOWN: // fall through + default: + return TYPE_UNSET; + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java index a920ff3d8..51eac0345 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo This file is part of Gadgetbridge. @@ -52,7 +52,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { @@ -133,9 +132,30 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return DateTimeDisplay.DATE_TIME; } - public static boolean getActivateDisplayOnLiftWrist() { + public static ActivateDisplayOnLift getActivateDisplayOnLiftWrist(Context context) { Prefs prefs = GBApplication.getPrefs(); - return prefs.getBoolean(MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT, true); + + String liftOff = context.getString(R.string.p_off); + String liftOn = context.getString(R.string.p_on); + String liftScheduled = context.getString(R.string.p_scheduled); + + String pref = prefs.getString(MiBandConst.PREF_ACTIVATE_DISPLAY_ON_LIFT, liftOff); + + if (liftOn.equals(pref)) { + return ActivateDisplayOnLift.ON; + } else if (liftScheduled.equals(pref)) { + return ActivateDisplayOnLift.SCHEDULED; + } + + return ActivateDisplayOnLift.OFF; + } + + public static Date getDisplayOnLiftStart() { + return getTimePreference( MiBandConst.PREF_DISPLAY_ON_LIFT_START, "00:00"); + } + + public static Date getDisplayOnLiftEnd() { + return getTimePreference( MiBandConst.PREF_DISPLAY_ON_LIFT_END, "00:00"); } public static Set getDisplayItems() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiFWHelper.java index a7a808350..48456e1dd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiFWHelper.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiWeatherConditions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiWeatherConditions.java index a616fe83b..f772f8adb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiWeatherConditions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiWeatherConditions.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java index 449054b37..af3f29253 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, João + Paulo Barraca This file is part of Gadgetbridge. @@ -64,6 +65,11 @@ public class AmazfitBipCoordinator extends HuamiCoordinator { return true; } + @Override + public boolean supportsActivityTracks() { + return true; + } + @Override public boolean supportsWeather() { return true; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWHelper.java index bac1b4aae..a14676f31 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWHelper.java @@ -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. @@ -23,7 +23,7 @@ import android.support.annotation.NonNull; import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipFirmwareInfo; public class AmazfitBipFWHelper extends HuamiFWHelper { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWInstallHandler.java index 77438bc9c..605491ff6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipFWInstallHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipService.java index 0033fad8d..36d0ce8ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipService.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -19,7 +19,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip; import java.util.UUID; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.DISPLAY_ITEM_BIT_CLOCK; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.ENDPOINT_DISPLAY; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.ENDPOINT_DISPLAY_ITEMS; public class AmazfitBipService { public static final UUID UUID_CHARACTERISTIC_WEATHER = UUID.fromString("0000000e-0000-3512-2118-0009af100700"); @@ -33,6 +35,13 @@ public class AmazfitBipService { public static final byte[] COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x01}; public static final byte[] COMMAND_SET_LANGUAGE_ENGLISH = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x02}; public static final byte[] COMMAND_SET_LANGUAGE_SPANISH = new byte[]{ENDPOINT_DISPLAY, 0x13, 0x00, 0x03}; + public static final byte[] COMMAND_SET_LANGUAGE_NEW_TEMPLATE = new byte[]{ENDPOINT_DISPLAY, 0x17, 0x00, 0, 0, 0, 0, 0}; + + + public static final byte COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES = 0x05; + public static final byte COMMAND_ACTIVITY_DATA_TYPE_SPORTS_DETAILS = 0x06; public static final byte[] COMMAND_ACK_FIND_PHONE_IN_PROGRESS = new byte[]{ENDPOINT_DISPLAY, 0x14, 0x00, 0x00}; + + public static final byte[] COMMAND_CHANGE_SCREENS = new byte[]{ENDPOINT_DISPLAY_ITEMS, DISPLAY_ITEM_BIT_CLOCK, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java index 4df1eb3b2..97d410a26 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2017-2018 Andreas Shimokawa, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWHelper.java index 4b7c38440..8b21fd2af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWHelper.java @@ -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,12 +18,10 @@ package nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor; import android.content.Context; import android.net.Uri; -import android.support.annotation.NonNull; import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorFirmwareInfo; public class AmazfitCorFWHelper extends HuamiFWHelper { @@ -32,12 +30,11 @@ public class AmazfitCorFWHelper extends HuamiFWHelper { super(uri, context); } - @NonNull @Override protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { firmwareInfo = new AmazfitCorFirmwareInfo(wholeFirmwareBytes); if (!firmwareInfo.isHeaderValid()) { - throw new IllegalArgumentException("Not a an Amazifit Bip firmware"); + throw new IllegalArgumentException("Not a an Amazfit Cor firmware"); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWInstallHandler.java index a13800f3d..452886c1e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitcor/AmazfitCorFWInstallHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java index e1d091102..9fab9909f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo This file is part of Gadgetbridge. @@ -25,9 +25,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; -import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; @@ -50,12 +50,10 @@ public class MiBand2Coordinator extends HuamiCoordinator { // and a heuristic for now try { BluetoothDevice device = candidate.getDevice(); -// if (isHealthWearable(device)) { String name = device.getName(); - if (name != null && name.equalsIgnoreCase(MiBandConst.MI_BAND2_NAME)) { + if (name != null && name.equalsIgnoreCase(HuamiConst.MI_BAND2_NAME)) { return DeviceType.MIBAND2; } -// } } catch (Exception ex) { LOG.error("unable to check device support", ex); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWHelper.java index d15ac3cf5..1ae8c13f6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWHelper.java @@ -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. @@ -23,7 +23,7 @@ import android.support.annotation.NonNull; import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2FirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2FirmwareInfo; public class MiBand2FWHelper extends HuamiFWHelper { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWInstallHandler.java index 05f25d8a2..a6027ebb6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2FWInstallHandler.java @@ -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. @@ -37,7 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Version; public class MiBand2FWInstallHandler extends AbstractMiBandFWInstallHandler { private static final Logger LOG = LoggerFactory.getLogger(MiBand2FWInstallHandler.class); - public MiBand2FWInstallHandler(Uri uri, Context context) { + MiBand2FWInstallHandler(Uri uri, Context context) { super(uri, context); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2HRXCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2HRXCoordinator.java index 8225c5b76..f45879b25 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2HRXCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2HRXCoordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2017-2018 Andreas Shimokawa, João Paulo Barraca This file is part of Gadgetbridge. @@ -25,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; @@ -44,7 +45,7 @@ public class MiBand2HRXCoordinator extends HuamiCoordinator { try { BluetoothDevice device = candidate.getDevice(); String name = device.getName(); - if (name != null && (name.equalsIgnoreCase("Mi Band HRX") || name.equalsIgnoreCase("Mi Band 2i"))) { + if (name != null && (name.equalsIgnoreCase(HuamiConst.MI_BAND2_NAME_HRX) || name.equalsIgnoreCase("Mi Band 2i"))) { return DeviceType.MIBAND2; } } catch (Exception ex) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java new file mode 100644 index 000000000..91bb24ce2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3Coordinator.java @@ -0,0 +1,73 @@ +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; +import android.support.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class MiBand3Coordinator extends HuamiCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(MiBand3Coordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.MIBAND3; + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && name.equalsIgnoreCase(HuamiConst.MI_BAND3_NAME)) { + return DeviceType.MIBAND3; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + MiBand3FWInstallHandler handler = new MiBand3FWInstallHandler(uri, context); + return handler.isValid() ? handler : null; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return true; + } + + @Override + public boolean supportsWeather() { + return true; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWHelper.java new file mode 100644 index 000000000..c0ecf9db4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWHelper.java @@ -0,0 +1,40 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3FirmwareInfo; + +public class MiBand3FWHelper extends HuamiFWHelper { + + public MiBand3FWHelper(Uri uri, Context context) throws IOException { + super(uri, context); + } + + @Override + protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { + firmwareInfo = new MiBand3FirmwareInfo(wholeFirmwareBytes); + if (!firmwareInfo.isHeaderValid()) { + throw new IllegalArgumentException("Not a Mi Band 3 firmware"); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWInstallHandler.java new file mode 100644 index 000000000..c957b1606 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband3/MiBand3FWInstallHandler.java @@ -0,0 +1,49 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +class MiBand3FWInstallHandler extends AbstractMiBandFWInstallHandler { + MiBand3FWInstallHandler(Uri uri, Context context) { + super(uri, context); + } + + @Override + protected String getFwUpgradeNotice() { + return mContext.getString(R.string.fw_upgrade_notice_miband3, helper.getHumanFirmwareVersion()); + } + + @Override + protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException { + return new MiBand3FWHelper(uri, context); + } + + @Override + protected boolean isSupportedDeviceType(GBDevice device) { + return device.getType() == DeviceType.MIBAND3; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java index 8476fba57..e84d60fb6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/JYouConstants.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Sami Alaoui +/* Copyright (C) 2017-2018 Sami Alaoui This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java index b35b1d404..4d3e98dbd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/jyou/TeclastH30Coordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, protomors, Sami Alaoui This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java index 6a597bef0..731f95008 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewConstants.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Daniele Gobbetti +/* Copyright (C) 2016-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java index 1e3c716e1..7b93b6913 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/liveview/LiveviewCoordinator.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java index 16722450f..a0f0d23c9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWHelper.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWInstallHandler.java index 27a0a860c..b6e6d4651 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandFWInstallHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java index 1b40aa439..88aebc0f2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/AbstractMiBandSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java index 43f098143..bf26cc9c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DateTimeDisplay.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DoNotDisturb.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DoNotDisturb.java index 912909ae5..04c689061 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DoNotDisturb.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/DoNotDisturb.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 José Rebelo +/* Copyright (C) 2017-2018 José Rebelo This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java index 29fb3eeae..16cb2e73c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2SampleProvider.java @@ -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. @@ -20,38 +20,15 @@ package nodomain.freeyourgadget.gadgetbridge.devices.miband; import java.util.List; import de.greenrobot.dao.query.QueryBuilder; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.*; public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { - - // observed the following values so far: - // 00 01 02 09 0a 0b 0c 10 11 - - // 0 = same activity kind as before - // 1 = light activity walking? - // 3 = definitely non-wear - // 9 = probably light sleep, definitely some kind of sleep - // 10 = ignore, except for hr (if valid) - // 11 = probably deep sleep - // 12 = definitely wake up - // 17 = definitely not sleep related - - public static final int TYPE_UNSET = -1; - public static final int TYPE_NO_CHANGE = 0; - public static final int TYPE_ACTIVITY = 1; - public static final int TYPE_RUNNING = 2; - public static final int TYPE_NONWEAR = 3; - public static final int TYPE_CHARGING = 6; - public static final int TYPE_LIGHT_SLEEP = 9; - public static final int TYPE_IGNORE = 10; - public static final int TYPE_DEEP_SLEEP = 11; - public static final int TYPE_WAKE_UP = 12; - public MiBand2SampleProvider(GBDevice device, DaoSession session) { super(device, session); } @@ -111,39 +88,11 @@ public class MiBand2SampleProvider extends AbstractMiBandSampleProvider { @Override public int normalizeType(int rawType) { - switch (rawType) { - case TYPE_DEEP_SLEEP: - return ActivityKind.TYPE_DEEP_SLEEP; - case TYPE_LIGHT_SLEEP: - return ActivityKind.TYPE_LIGHT_SLEEP; - case TYPE_ACTIVITY: - case TYPE_RUNNING: - case TYPE_WAKE_UP: - return ActivityKind.TYPE_ACTIVITY; - case TYPE_NONWEAR: - return ActivityKind.TYPE_NOT_WORN; - case TYPE_CHARGING: - return ActivityKind.TYPE_NOT_WORN; //I believe it's a safe assumption - default: - case TYPE_UNSET: // fall through - return ActivityKind.TYPE_UNKNOWN; - } + return HuamiConst.toActivityKind(rawType); } @Override public int toRawActivityKind(int activityKind) { - switch (activityKind) { - case ActivityKind.TYPE_ACTIVITY: - return TYPE_ACTIVITY; - case ActivityKind.TYPE_DEEP_SLEEP: - return TYPE_DEEP_SLEEP; - case ActivityKind.TYPE_LIGHT_SLEEP: - return TYPE_LIGHT_SLEEP; - case ActivityKind.TYPE_NOT_WORN: - return TYPE_NONWEAR; - case ActivityKind.TYPE_UNKNOWN: // fall through - default: - return TYPE_UNSET; - } + return HuamiConst.toRawActivityType(activityKind); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java index d18707577..b52a7b736 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBand2Service.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, JohnnySun, +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, JohnnySun, José Rebelo, Uwe Hermann This file is part of Gadgetbridge. @@ -136,13 +136,13 @@ public class MiBand2Service { public static final byte[] DATEFORMAT_TIME_24_HOURS = new byte[] {ENDPOINT_DISPLAY, 0x02, 0x0, 0x1 }; public static final byte[] COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x00}; + public static final byte[] COMMAND_SCHEDULE_DISPLAY_ON_LIFT_WRIST = new byte[]{ENDPOINT_DISPLAY, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; public static final byte[] COMMAND_ENABLE_GOAL_NOTIFICATION = new byte[]{ENDPOINT_DISPLAY, 0x06, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_GOAL_NOTIFICATION = new byte[]{ENDPOINT_DISPLAY, 0x06, 0x00, 0x00}; public static final byte[] COMMAND_ENABLE_ROTATE_WRIST_TO_SWITCH_INFO = new byte[]{ENDPOINT_DISPLAY, 0x0d, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_ROTATE_WRIST_TO_SWITCH_INFO = new byte[]{ENDPOINT_DISPLAY, 0x0d, 0x00, 0x00}; public static final byte[] COMMAND_ENABLE_DISPLAY_CALLER = new byte[]{ENDPOINT_DISPLAY, 0x10, 0x00, 0x00, 0x01}; public static final byte[] COMMAND_DISABLE_DISPLAY_CALLER = new byte[]{ENDPOINT_DISPLAY, 0x10, 0x00, 0x00, 0x00}; - public static final byte[] DISPLAY_XXX = new byte[] {ENDPOINT_DISPLAY, 0x03, 0x0, 0x0 }; public static final byte[] DISPLAY_YYY = new byte[] {ENDPOINT_DISPLAY, 0x10, 0x0, 0x1, 0x1 }; public static final byte[] COMMAND_DISTANCE_UNIT_METRIC = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x00 }; public static final byte[] COMMAND_DISTANCE_UNIT_IMPERIAL = new byte[] { ENDPOINT_DISPLAY, 0x03, 0x00, 0x01 }; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index 9807bd68e..b9d5ae507 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Christian +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Christian Fischer, Daniele Gobbetti, José Rebelo, Michal Novotny, Szymon Tomasz Stefanek This file is part of Gadgetbridge. @@ -49,7 +49,9 @@ public final class MiBandConst { public static final String PREF_MI2_DISPLAY_ITEM_CALORIES = "calories"; public static final String PREF_MI2_DISPLAY_ITEM_HEART_RATE = "heart_rate"; public static final String PREF_MI2_DISPLAY_ITEM_BATTERY = "battery"; - public static final String PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT = "mi2_activate_display_on_lift_wrist"; + public static final String PREF_ACTIVATE_DISPLAY_ON_LIFT = "activate_display_on_lift_wrist"; + public static final String PREF_DISPLAY_ON_LIFT_START = "display_on_lift_start"; + public static final String PREF_DISPLAY_ON_LIFT_END = "display_on_lift_end"; public static final String PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO = "mi2_rotate_wrist_to_switch_info"; public static final String PREF_MI2_ENABLE_TEXT_NOTIFICATIONS = "mi2_enable_text_notifications"; public static final String PREF_MI2_DO_NOT_DISTURB = "mi2_do_not_disturb"; @@ -71,8 +73,6 @@ public final class MiBandConst { public static final String ORIGIN_INCOMING_CALL = "incoming_call"; public static final String ORIGIN_ALARM_CLOCK = "alarm_clock"; public static final String MI_GENERAL_NAME_PREFIX = "MI"; - public static final String MI_BAND2_NAME = "MI Band 2"; - public static final String MI_BAND2_NAME_HRX = "Mi Band HRX"; public static final String MI_1 = "1"; public static final String MI_1A = "1A"; public static final String MI_1S = "1S"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java index 2cd04f320..f9a2063c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandCoordinator.java @@ -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, Szymon Tomasz Stefanek This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java index 0495ccab0..d73daf6fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandDateConverter.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Szymon Tomasz Stefanek This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java index 6a514f24e..46c2990ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWHelper.java @@ -1,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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java index 2d8d17273..766cfbd42 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandFWInstallHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java index 4e49ae911..e33d3c96b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPairingActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java index bc222ccd5..485dae7aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandPreferencesActivity.java @@ -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, José Rebelo, Szymon Tomasz Stefanek This file is part of Gadgetbridge. @@ -38,7 +38,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_ALARM_CLOCK; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.ORIGIN_INCOMING_CALL; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DISPLAY_ON_LIFT_END; +import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_DISPLAY_ON_LIFT_START; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DATEFORMAT; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DISPLAY_ITEMS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PREF_MI2_DO_NOT_DISTURB; @@ -135,21 +137,25 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { } }); - final Preference activateDisplayOnLift = findPreference(PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT); + final Preference activateDisplayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT); activateDisplayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { invokeLater(new Runnable() { @Override public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT); + GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); } }); return true; } }); + String displayOnLiftState = prefs.getString(MiBandConst.PREF_ACTIVATE_DISPLAY_ON_LIFT, PREF_MI2_DO_NOT_DISTURB_OFF); + boolean displayOnLiftScheduled = displayOnLiftState.equals(PREF_MI2_DO_NOT_DISTURB_SCHEDULED); + final Preference rotateWristCycleInfo = findPreference(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); + rotateWristCycleInfo.setEnabled(!PREF_MI2_DO_NOT_DISTURB_OFF.equals(displayOnLiftState)); rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { @@ -313,6 +319,57 @@ public class MiBandPreferencesActivity extends AbstractSettingsActivity { } }); + final Preference displayOnLiftStart = findPreference(PREF_DISPLAY_ON_LIFT_START); + displayOnLiftStart.setEnabled(displayOnLiftScheduled); + displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START); + } + }); + return true; + } + }); + + + final Preference displayOnLiftEnd = findPreference(PREF_DISPLAY_ON_LIFT_END); + displayOnLiftEnd.setEnabled(displayOnLiftScheduled); + displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END); + } + }); + return true; + } + }); + + + final Preference displayOnLift = findPreference(PREF_ACTIVATE_DISPLAY_ON_LIFT); + displayOnLift.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newVal) { + final boolean scheduled = PREF_MI2_DO_NOT_DISTURB_SCHEDULED.equals(newVal.toString()); + + displayOnLiftStart.setEnabled(scheduled); + displayOnLiftEnd.setEnabled(scheduled); + rotateWristCycleInfo.setEnabled(!PREF_MI2_DO_NOT_DISTURB_OFF.equals(newVal.toString())); + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); + } + }); + return true; + } + }); + final Preference fitnessGoal = findPreference(ActivityUser.PREF_USER_STEPS_GOAL); fitnessGoal.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java index 4842e5fa9..3a135a5c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java index c64d2c903..5de7d43a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandService.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Kasha This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java index b09bb2e73..38dbdcd90 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/UserInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Sergey Trofimov This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java index 7f7fdf588..f7a1804e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java index 05049a2b5..11058b144 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Constants.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 protomors +/* Copyright (C) 2017-2018 protomors This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java index fb157b677..373f59aac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1Coordinator.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, protomors This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java index 2b7b1e5db..4bd435a70 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/no1f1/No1F1SampleProvider.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 protomors +/* Copyright (C) 2017-2018 protomors This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java index c12996276..a5bd601c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWInstallHandler.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java index ee2458f77..5fd2fa670 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PBWReader.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleColor.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleColor.java index 4009ebbe4..c07f68860 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleColor.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleColor.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java index eefac58cf..d94a490f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleCoordinator.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index 75fff5131..b394f1421 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleIconID.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleIconID.java index 50a6ea2c7..06317582f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleIconID.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleIconID.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleInstallable.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleInstallable.java index abe2daa76..627ada12b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleInstallable.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleInstallable.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java index fe17e47b9..7dc106d30 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMisfitSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java index b8dd72cb8..7e92971a0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleMorpheuzSampleProvider.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java index 30d0b6d39..def98758e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebblePairingActivity.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/STM32CRC.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/STM32CRC.java index ba1fcea6d..b21bc20f9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/STM32CRC.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/STM32CRC.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java index b43c5fb37..55ca064bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vibratissimo/VibratissimoCoordinator.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchCoordinator.java new file mode 100644 index 000000000..1cfd7c2e3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchCoordinator.java @@ -0,0 +1,133 @@ +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, ladbsoft + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.xwatch; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class XWatchCoordinator extends AbstractDeviceCoordinator { + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + if (name != null && name.startsWith("XWatch")) { + return DeviceType.XWATCH; + } + return DeviceType.UNKNOWN; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.XWATCH; + } + + @Nullable + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return true; + } + + @Override + public boolean supportsActivityTracking() { + return true; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return new XWatchSampleProvider(device, session); + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsAlarmConfiguration() { + return true; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public String getManufacturer() { + return "Generic"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchSampleProvider.java new file mode 100644 index 000000000..2d37920b2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchSampleProvider.java @@ -0,0 +1,81 @@ +/* Copyright (C) 2017-2018 ladbsoft, protomors + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.xwatch; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.XWatchActivitySample; +import nodomain.freeyourgadget.gadgetbridge.entities.XWatchActivitySampleDao; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +//TODO: extend own class +public class XWatchSampleProvider extends AbstractSampleProvider { + public static final int TYPE_ACTIVITY = -1; + + public XWatchSampleProvider(GBDevice device, DaoSession session) { + super(device, session); + } + + @Override + public int normalizeType(int rawType) { + return ActivityKind.TYPE_ACTIVITY; + } + + @Override + public int toRawActivityKind(int activityKind) { + return TYPE_ACTIVITY; + } + + @Override + public float normalizeIntensity(int rawIntensity) { + return rawIntensity / 180.0f; + } + + @Override + public XWatchActivitySample createActivitySample() { + return new XWatchActivitySample(); + } + + @Override + public AbstractDao getSampleDao() { + return getSession().getXWatchActivitySampleDao(); + } + + @Nullable + @Override + protected Property getRawKindSampleProperty() { + return XWatchActivitySampleDao.Properties.RawKind; + } + + @NonNull + @Override + protected Property getTimestampSampleProperty() { + return XWatchActivitySampleDao.Properties.Timestamp; + } + + @NonNull + @Override + protected Property getDeviceIdentifierSampleProperty() { + return XWatchActivitySampleDao.Properties.DeviceId; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java new file mode 100644 index 000000000..aafcde504 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xwatch/XWatchService.java @@ -0,0 +1,50 @@ +/* Copyright (C) 2018 ladbsoft + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.xwatch; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class XWatchService { + public static final UUID UUID_NOTIFY = UUID.fromString("0000fff7-0000-1000-8000-00805f9b34fb"); + public static final UUID UUID_SERVICE = UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb"); + public static final UUID UUID_WRITE = UUID.fromString("0000fff6-0000-1000-8000-00805f9b34fb"); + + public static final byte COMMAND_CONNECTED = 0x01; + public static final byte COMMAND_ACTION_BUTTON = 0x4c; + public static final byte COMMAND_ACTIVITY_DATA = 0x43; + public static final byte COMMAND_ACTIVITY_TOTALS = 0x46; + + private static final Map XWATCH_DEBUG; + + static { + XWATCH_DEBUG = new HashMap<>(); + + XWATCH_DEBUG.put(UUID_NOTIFY, "Read data"); + XWATCH_DEBUG.put(UUID_WRITE, "Write data"); + XWATCH_DEBUG.put(UUID_SERVICE, "Get service"); + } + + public static String lookup(UUID uuid, String fallback) { + String name = XWATCH_DEBUG.get(uuid); + if (name == null) { + name = fallback; + } + return name; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java index 36acd5bb3..ff0492c6d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractActivitySample.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java index 4de5d39dd..b47a3eebb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleHealthActivitySample.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java index 0b169e2c7..5f15d5a24 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMisfitActivitySample.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java index 6b489264a..cba74f12b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/entities/AbstractPebbleMorpheuzActivitySample.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/ActivityTrackExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/ActivityTrackExporter.java new file mode 100644 index 000000000..7d7c61c1c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/ActivityTrackExporter.java @@ -0,0 +1,34 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.export; + +import android.support.annotation.NonNull; + +import java.io.File; +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; + +public interface ActivityTrackExporter { + @NonNull + String getDefaultFileName(@NonNull ActivityTrack track); + + void performExport(ActivityTrack track, File targetFile) throws IOException, GPXTrackEmptyException; + + class GPXTrackEmptyException extends Exception { + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java new file mode 100644 index 000000000..323575782 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/export/GPXExporter.java @@ -0,0 +1,218 @@ +/* Copyright (C) 2017-2018 AndrewH, Carsten Pfeiffer, Daniele Gobbetti + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.export; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Xml; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; +import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; + +public class GPXExporter implements ActivityTrackExporter { + private static final String NS_DEFAULT = ""; + private static final String NS_DEFAULT_URI = "http://www.topografix.com/GPX/1/1"; + private static final String NS_DEFAULT_PREFIX = ""; + private static final String NS_TRACKPOINT_EXTENSION = "gpxtpx"; + private static final String NS_TRACKPOINT_EXTENSION_URI = "http://www.garmin.com/xmlschemas/TrackPointExtension/v1"; + private static final String NS_XSI_URI = "http://www.w3.org/2001/XMLSchema-instance"; + + private String creator; + private boolean includeHeartRate = true; + private boolean includeHeartRateOfNearestSample = true; + + @NonNull + @Override + public String getDefaultFileName(@NonNull ActivityTrack track) { + return FileUtils.makeValidFileName(track.getName()); + } + + @Override + public void performExport(ActivityTrack track, File targetFile) throws IOException, GPXTrackEmptyException { + String encoding = StandardCharsets.UTF_8.name(); + XmlSerializer ser = Xml.newSerializer(); + try { + ser.setOutput(new FileOutputStream(targetFile), encoding); + ser.startDocument(encoding, Boolean.TRUE); + ser.setPrefix("xsi", NS_XSI_URI); + ser.setPrefix(NS_TRACKPOINT_EXTENSION, NS_TRACKPOINT_EXTENSION_URI); + ser.setPrefix(NS_DEFAULT_PREFIX, NS_DEFAULT); + + ser.startTag(NS_DEFAULT, "gpx"); + ser.attribute(NS_DEFAULT, "version", "1.1"); + ser.attribute(NS_DEFAULT, "creator", getCreator()); + ser.attribute(NS_XSI_URI, "schemaLocation", NS_DEFAULT_URI + " " + "http://www.topografix.com/GPX/1/1/gpx.xsd"); + + exportMetadata(ser, track); + exportTrack(ser, track); + + ser.endTag(NS_DEFAULT, "gpx"); + ser.endDocument(); + } finally { + ser.flush(); + } + } + + private void exportMetadata(XmlSerializer ser, ActivityTrack track) throws IOException { + ser.startTag(NS_DEFAULT, "metadata"); + ser.startTag(NS_DEFAULT, "name").text(track.getName()).endTag(NS_DEFAULT, "name"); + + ser.startTag(NS_DEFAULT, "author"); + ser.startTag(NS_DEFAULT, "name").text(track.getUser().getName()).endTag(NS_DEFAULT, "name"); + ser.endTag(NS_DEFAULT, "author"); + + ser.startTag(NS_DEFAULT, "time").text(formatTime(new Date())).endTag(NS_DEFAULT, "time"); + + ser.endTag(NS_DEFAULT, "metadata"); + } + + private String formatTime(Date date) { + return DateTimeUtils.formatIso8601(date); + } + + private void exportTrack(XmlSerializer ser, ActivityTrack track) throws IOException, GPXTrackEmptyException { + ser.startTag(NS_DEFAULT, "trk"); + ser.startTag(NS_DEFAULT, "trkseg"); + + List trackPoints = track.getTrackPoints(); + String source = getSource(track); + boolean atLeastOnePointExported = false; + for (ActivityPoint point : trackPoints) { + atLeastOnePointExported |= exportTrackPoint(ser, point, source, trackPoints); + } + + if(!atLeastOnePointExported) { + throw new GPXTrackEmptyException(); + } + + ser.endTag(NS_DEFAULT, "trkseg"); + ser.endTag(NS_DEFAULT, "trk"); + } + + private String getSource(ActivityTrack track) { + return track.getDevice().getName(); + } + + private boolean exportTrackPoint(XmlSerializer ser, ActivityPoint point, String source, List trackPoints) throws IOException { + GPSCoordinate location = point.getLocation(); + if (location == null) { + return false; // skip invalid points, that just contain hr data, for example + } + ser.startTag(NS_DEFAULT, "trkpt"); + ser.attribute(NS_DEFAULT, "lon", formatLocation(location.getLongitude())); + ser.attribute(NS_DEFAULT, "lat", formatLocation(location.getLatitude())); + ser.startTag(NS_DEFAULT, "ele").text(formatLocation(location.getAltitude())).endTag(NS_DEFAULT, "ele"); + ser.startTag(NS_DEFAULT, "time").text(formatTime(point.getTime())).endTag(NS_DEFAULT, "time"); + String description = point.getDescription(); + if (description != null) { + ser.startTag(NS_DEFAULT, "desc").text(description).endTag(NS_DEFAULT, "desc"); + } + //ser.startTag(NS_DEFAULT, "src").text(source).endTag(NS_DEFAULT, "src"); + + exportTrackpointExtensions(ser, point, trackPoints); + + ser.endTag(NS_DEFAULT, "trkpt"); + + return true; + } + + private void exportTrackpointExtensions(XmlSerializer ser, ActivityPoint point, List trackPoints) throws IOException { + if (!includeHeartRate) { + return; + } + + int hr = point.getHeartRate(); + if (!HeartRateUtils.isValidHeartRateValue(hr)) { + if (!includeHeartRateOfNearestSample) { + return; + } + + ActivityPoint closestPointItem = findClosestSensibleActivityPoint(point.getTime(), trackPoints); + if(closestPointItem == null) { + return; + } + + hr = closestPointItem.getHeartRate(); + if (!HeartRateUtils.isValidHeartRateValue(hr)) { + return; + } + } + + ser.startTag(NS_DEFAULT, "extensions"); + ser.setPrefix(NS_TRACKPOINT_EXTENSION, NS_TRACKPOINT_EXTENSION_URI); + ser.startTag(NS_TRACKPOINT_EXTENSION_URI, "TrackPointExtension"); + ser.startTag(NS_TRACKPOINT_EXTENSION_URI, "hr").text(String.valueOf(hr)).endTag(NS_TRACKPOINT_EXTENSION_URI, "hr"); + ser.endTag(NS_TRACKPOINT_EXTENSION_URI, "TrackPointExtension"); + ser.endTag(NS_DEFAULT, "extensions"); + } + + private @Nullable ActivityPoint findClosestSensibleActivityPoint(Date time, List trackPoints) { + ActivityPoint closestPointItem = null; + + long lowestDifference = 60 * 2 * 1000; // minimum distance is 2min + for (ActivityPoint pointItem : trackPoints) { + int hrItem = pointItem.getHeartRate(); + if (HeartRateUtils.isValidHeartRateValue(hrItem)) { + Date timeItem = pointItem.getTime(); + if (timeItem.after(time) || timeItem.equals(time)) { + break; // we assume that the given trackPoints are sorted in time ascending order (oldest first) + } + long difference = time.getTime() - timeItem.getTime(); + if (difference < lowestDifference) { + lowestDifference = difference; + closestPointItem = pointItem; + } + } + } + return closestPointItem; + } + + private String formatLocation(double value) { + return new BigDecimal(value).setScale(GPSCoordinate.GPS_DECIMAL_DEGREES_SCALE, RoundingMode.HALF_UP).toPlainString(); + } + + public String getCreator() { + return creator; // TODO: move to some kind of BrandingInfo class + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public void setIncludeHeartRate(boolean includeHeartRate) { + this.includeHeartRate = includeHeartRate; + } + + public boolean isIncludeHeartRate() { + return includeHeartRate; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java index 12466500c..c9a3c6831 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmClockReceiver.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java index b4e815822..02ff0b55d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AlarmReceiver.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java index 02ffcdbcf..5fdb1ce6f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/AutoStartReceiver.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2017-2018 Carsten Pfeiffer, Daniele Gobbetti, Felix + Konstantin Maurer This file is part of Gadgetbridge. @@ -31,7 +32,13 @@ public class AutoStartReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (GBApplication.getGBPrefs().getAutoStart() && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { Log.i(TAG, "Boot completed, starting Gadgetbridge"); - GBApplication.deviceService().start(); + if (GBApplication.getPrefs().getBoolean("general_autoconnectonbluetooth", false)) { + Log.i(TAG, "Autoconnect is enabled, attempting to connect"); + GBApplication.deviceService().connect(); + } else { + GBApplication.deviceService().start(); + } + PeriodicExporter.enablePeriodicExport(context); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java index 4d822973a..8bafd6176 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothConnectReceiver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java index 5291745ac..82fea4303 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothPairingRequestReceiver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2016-2018 Andreas Shimokawa, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java index 53e6fd739..730ddfa14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/BluetoothStateChangeReceiver.java @@ -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. @@ -36,7 +36,7 @@ public class BluetoothStateChangeReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_ON) { Intent refreshIntent = new Intent(DeviceManager.ACTION_REFRESH_DEVICELIST); @@ -50,6 +50,7 @@ public class BluetoothStateChangeReceiver extends BroadcastReceiver { LOG.info("Bluetooth turned on => connecting..."); GBApplication.deviceService().connect(); } else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) { + LOG.info("Bluetooth turned off => disconnecting..."); GBApplication.deviceService().disconnect(); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CMWeatherReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CMWeatherReceiver.java index d061372d0..87610fea9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CMWeatherReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CMWeatherReceiver.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017-2018 Andreas Shimokawa + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.app.AlarmManager; @@ -30,6 +46,7 @@ import static cyanogenmod.providers.WeatherContract.WeatherColumns.WeatherCode.N import static cyanogenmod.providers.WeatherContract.WeatherColumns.WeatherCode.SCATTERED_SNOW_SHOWERS; import static cyanogenmod.providers.WeatherContract.WeatherColumns.WeatherCode.SCATTERED_THUNDERSTORMS; import static cyanogenmod.providers.WeatherContract.WeatherColumns.WeatherCode.SHOWERS; +import static cyanogenmod.providers.WeatherContract.WeatherColumns.WindSpeedUnit.MPH; public class CMWeatherReceiver extends BroadcastReceiver implements CMWeatherManager.WeatherUpdateRequestListener, CMWeatherManager.LookupCityRequestListener { @@ -132,6 +149,13 @@ public class CMWeatherReceiver extends BroadcastReceiver implements CMWeatherMan weatherSpec.todayMaxTemp = (int) weatherInfo.getTodaysHigh() + 273; weatherSpec.todayMinTemp = (int) weatherInfo.getTodaysLow() + 273; } + if (weatherInfo.getWindSpeedUnit() == MPH) { + weatherSpec.windSpeed = (float) weatherInfo.getWindSpeed() * 1.609344f; + } else { + weatherSpec.windSpeed = (float) weatherInfo.getWindSpeed(); + } + weatherSpec.windDirection = (int) weatherInfo.getWindDirection(); + weatherSpec.currentConditionCode = Weather.mapToOpenWeatherMapCondition(CMtoYahooCondintion(weatherInfo.getConditionCode())); weatherSpec.currentCondition = Weather.getConditionString(weatherSpec.currentConditionCode); weatherSpec.currentHumidity = (int) weatherInfo.getHumidity(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java index 9506e9b90..86f2413e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/CalendarReceiver.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, - Daniel Hauck +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Daniel Hauck This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java index 99d4bd6db..88193d7ef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/MusicPlaybackReceiver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 andre, Andreas Shimokawa, Avamander, Carsten +/* Copyright (C) 2015-2018 andre, Andreas Shimokawa, Avamander, Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 7ecdeb32a..f39b64bf7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -1,6 +1,6 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Frank Slezak, Hasan Ammar, Julien Pivotto, Kevin Richter, Normano64, - Steffen Liebergeld + Steffen Liebergeld, Taavi Eomäe, Zhong Jianxin This file is part of Gadgetbridge. @@ -38,13 +38,13 @@ import android.os.PowerManager; import android.os.RemoteException; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; +import android.support.v4.app.NotificationCompat; import android.support.v4.app.RemoteInput; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; -import android.support.v7.app.NotificationCompat; import android.support.v7.graphics.Palette; import org.slf4j.Logger; @@ -67,6 +67,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; +import static android.support.v4.media.app.NotificationCompat.MediaStyle.getMediaSession; + public class NotificationListener extends NotificationListenerService { private static final Logger LOG = LoggerFactory.getLogger(NotificationListener.class); @@ -111,7 +113,7 @@ public class NotificationListener extends NotificationListenerService { } else { // ACTION_MUTE LOG.info("going to mute " + sbn.getPackageName()); - GBApplication.addAppToBlacklist(sbn.getPackageName()); + GBApplication.addAppToNotifBlacklist(sbn.getPackageName()); } } } @@ -203,7 +205,7 @@ public class NotificationListener extends NotificationListenerService { String source = sbn.getPackageName().toLowerCase(); Notification notification = sbn.getNotification(); NotificationSpec notificationSpec = new NotificationSpec(); - notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better + notificationSpec.id = (int) sbn.getPostTime(); //FIXME: a truly unique id would be better // determinate Source App Name ("Label") PackageManager pm = getPackageManager(); @@ -367,7 +369,7 @@ public class NotificationListener extends NotificationListenerService { Prefs prefs = GBApplication.getPrefs(); if (prefs.getBoolean("autoremove_notifications", false)) { LOG.info("notification removed, will ask device to delete it"); - GBApplication.deviceService().onDeleteNotification(sbn.getPackageName().hashCode() * 31 + sbn.getId()); + GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); } } @@ -424,7 +426,7 @@ public class NotificationListener extends NotificationListenerService { } } - if (GBApplication.appIsBlacklisted(source)) { + if (GBApplication.appIsNotifBlacklisted(source)) { LOG.info("Ignoring notification, application is blacklisted"); return true; } @@ -434,7 +436,7 @@ public class NotificationListener extends NotificationListenerService { private boolean shouldIgnoreNotification(Notification notification, String source) { - MediaSessionCompat.Token mediaSession = NotificationCompat.getMediaSession(notification); + MediaSessionCompat.Token mediaSession = getMediaSession(notification); //try to handle media session notifications if (mediaSession != null && handleMediaSessionNotification(mediaSession)) return true; @@ -444,7 +446,8 @@ public class NotificationListener extends NotificationListenerService { //some Apps always mark their notifcations as read-only if (NotificationCompat.getLocalOnly(notification) && type != NotificationType.WECHAT && - type != NotificationType.OUTLOOK) { + type != NotificationType.OUTLOOK && + type != NotificationType.SKYPE) { //see https://github.com/Freeyourgadget/Gadgetbridge/issues/1109 return true; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OmniJawsObserver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OmniJawsObserver.java index 20359ff7d..83b21a2e9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OmniJawsObserver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/OmniJawsObserver.java @@ -1,3 +1,19 @@ +/* Copyright (C) 2017-2018 Daniele Gobbetti + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.content.Context; @@ -44,7 +60,9 @@ public class OmniJawsObserver extends ContentObserver { "forecast_condition", "forecast_condition_code", "time_stamp", - "forecast_date" + "forecast_date", + "wind_speed", + "wind_direction" }; private final String[] SETTINGS_PROJECTION = new String[]{ @@ -100,9 +118,13 @@ public class OmniJawsObserver extends ContentObserver { weatherSpec.currentTemp = toKelvin(c.getFloat(3)); weatherSpec.currentHumidity = (int) c.getFloat(4); + + weatherSpec.windSpeed = toKmh(c.getFloat(11)); + weatherSpec.windDirection = c.getInt(12); + weatherSpec.timestamp = (int) (Long.valueOf(c.getString(9)) / 1000); + } else if (i == 1) { weatherSpec.todayMinTemp = toKelvin(c.getFloat(5)); weatherSpec.todayMaxTemp = toKelvin(c.getFloat(6)); - weatherSpec.timestamp = (int) (Long.valueOf(c.getString(9)) / 1000); } else { WeatherSpec.Forecast gbForecast = new WeatherSpec.Forecast(); @@ -160,4 +182,10 @@ public class OmniJawsObserver extends ContentObserver { return (int) ((temperature - 32) * 0.5555555555555556D + 273.15); } + private float toKmh(float speed) { + if (mMetric) { + return speed; + } + return (speed * 1.61f); + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java index 54741de2a..a2091a662 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PebbleReceiver.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. @@ -76,6 +77,11 @@ public class PebbleReceiver extends BroadcastReceiver { if (notificationSpec.title != null) { notificationSpec.type = NotificationType.UNKNOWN; String sender = intent.getStringExtra("sender"); + if (GBApplication.appIsPebbleBlacklisted(sender)) { + LOG.info("Ignoring Pebble message, application "+ sender +" is blacklisted"); + return; + } + if ("Conversations".equals(sender)) { notificationSpec.type = NotificationType.CONVERSATIONS; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java index 3082ca219..e5252eda5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/PhoneCallReceiver.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Normano64 +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Normano64 This file is part of Gadgetbridge. @@ -18,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents; import android.app.NotificationManager; import android.app.NotificationManager.Policy; +import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -35,19 +37,12 @@ public class PhoneCallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + TelephonyManager tm = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE); if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) { mSavedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"); } else { - String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE); String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); - int state = 0; - if (TelephonyManager.EXTRA_STATE_IDLE.equals(stateStr)) { - state = TelephonyManager.CALL_STATE_IDLE; - } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(stateStr)) { - state = TelephonyManager.CALL_STATE_OFFHOOK; - } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(stateStr)) { - state = TelephonyManager.CALL_STATE_RINGING; - } + int state = tm.getCallState(); onCallStateChanged(context, state, number); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java index 7056b20b9..be96e4381 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Normano64 +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, + Normano64, Zhong Jianxin This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java index 9486cc0a2..b202cbd2b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/TimeChangeReceiver.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java index 93e4dcb2c..5757a3515 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationConfig.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java index f016b61e5..437907681 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/WeatherNotificationReceiver.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java index 3810613c6..cdbee5377 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBAlarm.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java index 2444aabcc..296ae7bb2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDevice.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Taavi Eomäe, Uwe Hermann This file is part of Gadgetbridge. @@ -252,6 +252,10 @@ public class GBDevice implements Parcelable { return mState; } + public int getStateOrdinal() { + return mState.ordinal(); + } + public void setState(State state) { mState = state; if (state.ordinal() <= State.CONNECTED.ordinal()) { @@ -322,7 +326,7 @@ public class GBDevice implements Parcelable { public void setRssi(short rssi) { if (rssi < 0) { - LOG.warn("illegal rssi value " + rssi + ", setting to RSSI_UNKNOWN"); + LOG.warn("Illegal RSSI value " + rssi + ", setting to RSSI_UNKNOWN"); mRssi = RSSI_UNKNOWN; } else { mRssi = rssi; @@ -446,7 +450,7 @@ public class GBDevice implements Parcelable { result.add(new GenericItem(DEVINFO_FW_VER, mFirmwareVersion)); } if (mFirmwareVersion2 != null) { - // FIXME: thats ugly + // FIXME: This is ugly if (mDeviceType == DeviceType.AMAZFITBIP) { result.add(new GenericItem(DEVINFO_GPS_VER, mFirmwareVersion2)); } else { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java index fbb9a5c38..3c9fec7a5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceApp.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java index d25449129..525c33d1b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceCandidate.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Taavi Eomäe This file is part of Gadgetbridge. @@ -47,13 +47,13 @@ public class GBDeviceCandidate implements Parcelable { private final BluetoothDevice device; private final short rssi; - private final ParcelUuid[] serviceUuds; + private final ParcelUuid[] serviceUuids; private DeviceType deviceType = DeviceType.UNKNOWN; - public GBDeviceCandidate(BluetoothDevice device, short rssi, ParcelUuid[] serviceUuds) { + public GBDeviceCandidate(BluetoothDevice device, short rssi, ParcelUuid[] serviceUuids) { this.device = device; this.rssi = rssi; - this.serviceUuds = mergeServiceUuids(serviceUuds, device.getUuids()); + this.serviceUuids = mergeServiceUuids(serviceUuids, device.getUuids()); } private GBDeviceCandidate(Parcel in) { @@ -64,8 +64,8 @@ public class GBDeviceCandidate implements Parcelable { rssi = (short) in.readInt(); deviceType = DeviceType.valueOf(in.readString()); - ParcelUuid[] uuids = AndroidUtils.toParcelUUids(in.readParcelableArray(getClass().getClassLoader())); - serviceUuds = mergeServiceUuids(uuids, device.getUuids()); + ParcelUuid[] uuids = AndroidUtils.toParcelUuids(in.readParcelableArray(getClass().getClassLoader())); + serviceUuids = mergeServiceUuids(uuids, device.getUuids()); } @Override @@ -73,7 +73,7 @@ public class GBDeviceCandidate implements Parcelable { dest.writeParcelable(device, 0); dest.writeInt(rssi); dest.writeString(deviceType.name()); - dest.writeParcelableArray(serviceUuds, 0); + dest.writeParcelableArray(serviceUuids, 0); } public static final Creator CREATOR = new Creator() { @@ -104,10 +104,10 @@ public class GBDeviceCandidate implements Parcelable { return device != null ? device.getAddress() : GBApplication.getContext().getString(R.string._unknown_); } - private ParcelUuid[] mergeServiceUuids(ParcelUuid[] serviceUuds, ParcelUuid[] deviceUuids) { + private ParcelUuid[] mergeServiceUuids(ParcelUuid[] serviceUuids, ParcelUuid[] deviceUuids) { Set uuids = new HashSet<>(); - if (serviceUuds != null) { - uuids.addAll(Arrays.asList(serviceUuds)); + if (serviceUuids != null) { + uuids.addAll(Arrays.asList(serviceUuids)); } if (deviceUuids != null) { uuids.addAll(Arrays.asList(deviceUuids)); @@ -117,7 +117,7 @@ public class GBDeviceCandidate implements Parcelable { @NonNull public ParcelUuid[] getServiceUuids() { - return serviceUuds; + return serviceUuids; } public boolean supportsService(UUID aService) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index 1df97bb7a..71155069e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer, - Frank Slezak, ivanovlev, Julien Pivotto, Kasha, Steffen Liebergeld +/* Copyright (C) 2015-2018 Alberto, Andreas Shimokawa, Carsten Pfeiffer, + criogenic, Frank Slezak, ivanovlev, Julien Pivotto, Kasha, Steffen Liebergeld This file is part of Gadgetbridge. @@ -271,8 +271,9 @@ public class GBDeviceService implements DeviceService { } @Override - public void onFetchActivityData() { - Intent intent = createIntent().setAction(ACTION_FETCH_ACTIVITY_DATA); + public void onFetchRecordedData(int dataTypes) { + Intent intent = createIntent().setAction(ACTION_FETCH_RECORDED_DATA) + .putExtra(EXTRA_RECORDED_DATA_TYPES, dataTypes); invokeService(intent); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBSummaryOfDay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBSummaryOfDay.java index 7ccdb387c..9896848ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBSummaryOfDay.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBSummaryOfDay.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmount.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmount.java index 650fb793e..4c60a316f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmount.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmount.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmounts.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmounts.java index 672a01af3..1e047daab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmounts.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityAmounts.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java index 744a39f50..774bde62d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityKind.java @@ -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. @@ -17,8 +17,12 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.model; +import android.content.Context; +import android.support.annotation.DrawableRes; + import java.util.Arrays; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; public class ActivityKind { @@ -28,6 +32,11 @@ public class ActivityKind { public static final int TYPE_LIGHT_SLEEP = 2; public static final int TYPE_DEEP_SLEEP = 4; public static final int TYPE_NOT_WORN = 8; + public static final int TYPE_RUNNING = 16; + public static final int TYPE_WALKING = 32; + public static final int TYPE_SWIMMING = 64; + public static final int TYPE_CYCLING = 128; + public static final int TYPE_TREADMILL = 256; public static final int TYPE_SLEEP = TYPE_LIGHT_SLEEP | TYPE_DEEP_SLEEP; public static final int TYPE_ALL = TYPE_ACTIVITY | TYPE_SLEEP | TYPE_NOT_WORN; @@ -47,7 +56,75 @@ public class ActivityKind { if ((types & ActivityKind.TYPE_NOT_WORN) != 0) { result[i++] = provider.toRawActivityKind(TYPE_NOT_WORN); } + if ((types & ActivityKind.TYPE_RUNNING) != 0) { + result[i++] = provider.toRawActivityKind(TYPE_RUNNING); + } + if ((types & ActivityKind.TYPE_WALKING) != 0) { + result[i++] = provider.toRawActivityKind(TYPE_WALKING); + } + if ((types & ActivityKind.TYPE_SWIMMING) != 0) { + result[i++] = provider.toRawActivityKind(TYPE_SWIMMING); + } + if ((types & ActivityKind.TYPE_CYCLING) != 0) { + result[i++] = provider.toRawActivityKind(TYPE_CYCLING); + } + if ((types & ActivityKind.TYPE_TREADMILL) != 0) { + result[i++] = provider.toRawActivityKind(TYPE_TREADMILL); + } return Arrays.copyOf(result, i); } + public static String asString(int kind, Context context) { + switch (kind) { + case TYPE_NOT_MEASURED: + return context.getString(R.string.activity_type_not_measured); + case TYPE_ACTIVITY: + return context.getString(R.string.activity_type_activity); + case TYPE_LIGHT_SLEEP: + return context.getString(R.string.activity_type_light_sleep); + case TYPE_DEEP_SLEEP: + return context.getString(R.string.activity_type_deep_sleep); + case TYPE_NOT_WORN: + return context.getString(R.string.activity_type_not_worn); + case TYPE_RUNNING: + return context.getString(R.string.activity_type_running); + case TYPE_WALKING: + return context.getString(R.string.activity_type_walking); + case TYPE_SWIMMING: + return context.getString(R.string.activity_type_swimming); + case TYPE_CYCLING: + return context.getString(R.string.activity_type_biking); + case TYPE_TREADMILL: + return context.getString(R.string.activity_type_treadmill); + case TYPE_UNKNOWN: + default: + return context.getString(R.string.activity_type_unknown); + } + } + + @DrawableRes + public static int getIconId(int kind) { + switch (kind) { + case TYPE_NOT_MEASURED: + return R.drawable.ic_activity_not_measured; + case TYPE_LIGHT_SLEEP: + return R.drawable.ic_activity_light_sleep; + case TYPE_DEEP_SLEEP: + return R.drawable.ic_activity_deep_sleep; + case TYPE_RUNNING: + return R.drawable.ic_activity_running; + case TYPE_WALKING: + return R.drawable.ic_activity_walking; + case TYPE_CYCLING: + return R.drawable.ic_activity_biking; + case TYPE_TREADMILL: + return R.drawable.ic_activity_walking; + case TYPE_SWIMMING: // fall through + case TYPE_NOT_WORN: // fall through + case TYPE_ACTIVITY: // fall through + case TYPE_UNKNOWN: // fall through + default: + return R.drawable.ic_activity_unknown; + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityPoint.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityPoint.java new file mode 100644 index 000000000..a5813683c --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityPoint.java @@ -0,0 +1,111 @@ +/* Copyright (C) 2017-2018 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.model; + +import android.support.annotation.Nullable; + +import java.util.Date; + +// https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd +/* + + 29.2 + + + + 11 + 92 + 0 + + + +*/ +public class ActivityPoint { + private Date time; + private GPSCoordinate location; + private int heartRate; + private long speed4; + private long speed5; + private long speed6; + + // e.g. to describe a pause during the activity + private @Nullable String description; + + public ActivityPoint() { + } + + public ActivityPoint(Date time) { + this.time = time; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + @Nullable + public String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + public GPSCoordinate getLocation() { + return location; + } + + public void setLocation(GPSCoordinate location) { + this.location = location; + } + + public int getHeartRate() { + return heartRate; + } + + public void setHeartRate(int heartRate) { + this.heartRate = heartRate; + } + + public long getSpeed4() { + return speed4; + } + + public void setSpeed4(long speed4) { + this.speed4 = speed4; + } + + public long getSpeed5() { + return speed5; + } + + public void setSpeed5(long speed5) { + this.speed5 = speed5; + } + + public long getSpeed6() { + return speed6; + } + + public void setSpeed6(long speed6) { + this.speed6 = speed6; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java index 35ae57872..d862aa442 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySample.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummary.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummary.java new file mode 100644 index 000000000..1df0f5249 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivitySummary.java @@ -0,0 +1,49 @@ +/* Copyright (C) 2017-2018 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.model; + +import java.io.Serializable; +import java.util.Date; + +/** + * Summarized information about a temporal activity. + * + * // TODO: split into separate entities? + */ +public interface ActivitySummary extends Serializable { + String getName(); + Date getStartTime(); + Date getEndTime(); + + int getActivityKind(); + String getGpxTrack(); + + long getDeviceId(); + + long getUserId(); + // long getSteps(); +// float getDistanceMeters(); +// float getAscentMeters(); +// float getDescentMeters(); +// float getMinAltitude(); +// float getMaxAltitude(); +// float getCalories(); +// +// float getMaxSpeed(); +// float getMinSpeed(); +// float getAverageSpeed(); +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityTrack.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityTrack.java new file mode 100644 index 000000000..5326306be --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityTrack.java @@ -0,0 +1,78 @@ +/* Copyright (C) 2017-2018 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.model; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.User; + +public class ActivityTrack { + private Date baseTime; + private Device device; + private User user; + private String name; + + + public void setBaseTime(Date baseTime) { + this.baseTime = baseTime; + } + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public void setTrackPoints(List trackPoints) { + this.trackPoints = trackPoints; + } + + private List trackPoints = new ArrayList<>(); + + public void addTrackPoint(ActivityPoint point) { + trackPoints.add(point); + } + + public List getTrackPoints() { + return trackPoints; + } + + public Date getBaseTime() { + return baseTime; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java index 3a886d371..9f94f5ea4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ActivityUser.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, +/* Copyright (C) 2016-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Alarm.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Alarm.java index 260cde810..f977ca14a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Alarm.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Alarm.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java index 3b2f41044..39fb9fc70 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/AppNotificationType.java @@ -1,5 +1,6 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, AnthonyDiGirolamo, Daniele - Gobbetti, Frank Slezak, Kaz Wolfe, Kevin Richter +/* Copyright (C) 2016-2018 Andreas Shimokawa, AnthonyDiGirolamo, Daniele + Gobbetti, Frank Slezak, Kaz Wolfe, Kevin Richter, Lukas Veneziano, michaelneu, + NotAFIle, Tomas Radej This file is part of Gadgetbridge. @@ -76,6 +77,12 @@ public class AppNotificationType extends HashMap { // Threema put("ch.threema.app", NotificationType.THREEMA); + // Kontalk + put("org.kontalk", NotificationType.KONTALK); + + // Antox + put("chat.tox.antox", NotificationType.ANTOX); + // Twitter put("org.mariotaku.twidere", NotificationType.TWITTER); put("com.twitter.android", NotificationType.TWITTER); @@ -102,6 +109,9 @@ public class AppNotificationType extends HashMap { // Skype put("com.skype.raider", NotificationType.SKYPE); + // Skype for business + put("com.microsoft.office.lync15", NotificationType.SKYPE); + // Mailbox put("com.mailboxapp", NotificationType.MAILBOX); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BatteryState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BatteryState.java index 922dfb2f2..346612a43 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BatteryState.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/BatteryState.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Daniele Gobbetti +/* Copyright (C) 2015-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java index b67af12f3..d03859acb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEventSpec.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java index a3b3e2ea9..e882af658 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CalendarEvents.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Daniel Hauck This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java index df916457b..69f569d11 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CallSpec.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CannedMessagesSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CannedMessagesSpec.java index 30d3da65c..173d46101 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CannedMessagesSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/CannedMessagesSpec.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index 1e68558c5..19b29939c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Frank Slezak, +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Frank Slezak, ivanovlev, JohnnySun, Julien Pivotto, Kasha, Steffen Liebergeld This file is part of Gadgetbridge. @@ -49,7 +49,7 @@ public interface DeviceService extends EventHandler { String ACTION_INSTALL = PREFIX + ".action.install"; String ACTION_REBOOT = PREFIX + ".action.reboot"; String ACTION_HEARTRATE_TEST = PREFIX + ".action.heartrate_test"; - String ACTION_FETCH_ACTIVITY_DATA = PREFIX + ".action.fetch_activity_data"; + String ACTION_FETCH_RECORDED_DATA = PREFIX + ".action.fetch_activity_data"; String ACTION_DISCONNECT = PREFIX + ".action.disconnect"; String ACTION_FIND_DEVICE = PREFIX + ".action.find_device"; String ACTION_SET_CONSTANT_VIBRATION = PREFIX + ".action.set_constant_vibration"; @@ -103,8 +103,8 @@ public interface DeviceService extends EventHandler { String EXTRA_CONNECT_FIRST_TIME = "connect_first_time"; String EXTRA_BOOLEAN_ENABLE = "enable_realtime_steps"; String EXTRA_INTERVAL_SECONDS = "interval_seconds"; - String EXTRA_WEATHER = "weather"; + String EXTRA_RECORDED_DATA_TYPES = "data_types"; /** * Use EXTRA_REALTIME_SAMPLE instead diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index da0e69a8d..82e07a1af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -1,5 +1,6 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, Sami + Alaoui, tiparega This file is part of Gadgetbridge. @@ -35,14 +36,17 @@ public enum DeviceType { MIBAND2(11, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband2), AMAZFITBIP(12, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_amazfit_bip), AMAZFITCOR(13, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_amazfit_cor), + MIBAND3(14, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband3), VIBRATISSIMO(20, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo), LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview), HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus), MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_makibes_f68), EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_exrizu_k8), + Q8(43, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_q8), NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_no1_f1), TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_teclast_h30), - ZETIME(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_mykronoz_zetime), + XWATCH(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_xwatch), + ZETIME(80, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_mykronoz_zetime), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java.orig b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java.orig new file mode 100644 index 000000000..4acceeab6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java.orig @@ -0,0 +1,101 @@ +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, Sami + Alaoui, tiparega + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.model; + +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; + +import nodomain.freeyourgadget.gadgetbridge.R; + +/** + * For every supported device, a device type constant must exist. + * + * Note: they key of every constant is stored in the DB, so it is fixed forever, + * and may not be changed. + */ +public enum DeviceType { + UNKNOWN(-1, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_unknown), + PEBBLE(1, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_pebble), + MIBAND(10, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband), + MIBAND2(11, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband2), + AMAZFITBIP(12, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_amazfit_bip), + AMAZFITCOR(13, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_amazfit_cor), + MIBAND3(14, R.drawable.ic_device_miband, R.drawable.ic_device_miband_disabled, R.string.devicetype_miband3), + VIBRATISSIMO(20, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo), + LIVEVIEW(30, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview), + HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus), + MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_makibes_f68), + EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_exrizu_k8), + Q8(43, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_q8), + NO1F1(50, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_no1_f1), + TECLASTH30(60, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_teclast_h30), +<<<<<<< HEAD + ZETIME(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_mykronoz_zetime), +======= + XWATCH(70, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_xwatch), +>>>>>>> master + TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); + + private final int key; + @DrawableRes + private final int defaultIcon; + @DrawableRes + private final int disabledIcon; + @StringRes + private final int name; + + DeviceType(int key, int defaultIcon, int disabledIcon, int name) { + this.key = key; + this.defaultIcon = defaultIcon; + this.disabledIcon = disabledIcon; + this.name = name; + } + + public int getKey() { + return key; + } + + public boolean isSupported() { + return this != UNKNOWN; + } + + public static DeviceType fromKey(int key) { + for (DeviceType type : values()) { + if (type.key == key) { + return type; + } + } + return DeviceType.UNKNOWN; + } + + @StringRes + public int getName() { + return name; + } + + @DrawableRes + public int getIcon() { + return defaultIcon; + } + + @DrawableRes + public int getDisabledIcon() { + return disabledIcon; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GPSCoordinate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GPSCoordinate.java new file mode 100644 index 000000000..3a867a75a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GPSCoordinate.java @@ -0,0 +1,81 @@ +/* Copyright (C) 2017-2018 Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.model; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public final class GPSCoordinate { + private final double latitude; + private final double longitude; + private final double altitude; + + public static final int GPS_DECIMAL_DEGREES_SCALE = 6; // precise to 111.132mm at equator: https://en.wikipedia.org/wiki/Decimal_degrees + + public GPSCoordinate(double longitude, double latitude, double altitude) { + this.longitude = longitude; + this.latitude = latitude; + this.altitude = altitude; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public double getAltitude() { + return altitude; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GPSCoordinate that = (GPSCoordinate) o; + + if (Double.compare(that.getLatitude(), getLatitude()) != 0) return false; + if (Double.compare(that.getLongitude(), getLongitude()) != 0) return false; + return Double.compare(that.getAltitude(), getAltitude()) == 0; + + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(getLatitude()); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getLongitude()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(getAltitude()); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + private String formatLocation(double value) { + return new BigDecimal(value).setScale(8, RoundingMode.HALF_UP).toPlainString(); + } + + @Override + public String toString() { + return "lon: " + formatLocation(longitude) + ", lat: " + formatLocation(latitude) + ", alt: " + formatLocation(altitude) + "m"; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java index 375bc51c3..bc050b5b6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/GenericItem.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java index 34b743adb..aeb08786b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ItemWithDetails.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java index e90d02b34..aeca5a0c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Measurement.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java index 0e97db841..c0a7d3285 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicSpec.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java index 466e61986..286fdf4cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/MusicStateSpec.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Avamander, Carsten Pfeiffer, +/* Copyright (C) 2016-2018 Andreas Shimokawa, Avamander, Carsten Pfeiffer, Daniele Gobbetti, Steffen Liebergeld This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java index b2d624774..7d0ff891f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Frank Slezak +/* Copyright (C) 2015-2018 Andreas Shimokawa, Frank Slezak This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java index 64fcd3e16..c35f1bb93 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationType.java @@ -1,5 +1,6 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, AnthonyDiGirolamo, Carsten - Pfeiffer, Frank Slezak, Julien Pivotto, Kaz Wolfe, Kevin Richter +/* Copyright (C) 2015-2018 Andreas Shimokawa, AnthonyDiGirolamo, Carsten + Pfeiffer, Daniele Gobbetti, Frank Slezak, Julien Pivotto, Kaz Wolfe, Kevin + Richter, Lukas Veneziano This file is part of Gadgetbridge. @@ -59,6 +60,8 @@ public enum NotificationType { SNAPCHAT(PebbleIconID.NOTIFICATION_SNAPCHAT, PebbleColor.Icterine), TELEGRAM(PebbleIconID.NOTIFICATION_TELEGRAM, PebbleColor.VividCerulean), THREEMA(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.JaegerGreen), + KONTALK(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.JaegerGreen), + ANTOX(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.JaegerGreen), TRANSIT(PebbleIconID.LOCATION, PebbleColor.JaegerGreen), TWITTER(PebbleIconID.NOTIFICATION_TWITTER, PebbleColor.BlueMoon), VIBER(PebbleIconID.NOTIFICATION_VIBER, PebbleColor.VividViolet), @@ -104,6 +107,8 @@ public enum NotificationType { case SIGNAL: case TELEGRAM: case THREEMA: + case KONTALK: + case ANTOX: case WHATSAPP: case GOOGLE_MESSENGER: case GOOGLE_HANGOUTS: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RecordedDataTypes.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RecordedDataTypes.java new file mode 100644 index 000000000..4c6cf4ebe --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/RecordedDataTypes.java @@ -0,0 +1,28 @@ +/* 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 . */ + +package nodomain.freeyourgadget.gadgetbridge.model; + +public class RecordedDataTypes { + public static int TYPE_ACTIVITY = 0x00000001; + public static int TYPE_WORKOUTS = 0x00000002; + public static int TYPE_GPS_TRACKS = 0x00000004; + public static int TYPE_TEMPERATURE = 0x00000008; + public static int TYPE_DEBUGLOGS = 0x00000010; + + public static int TYPE_ALL = (int)0xffffffff; +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SummaryOfDay.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SummaryOfDay.java index 9e7be7db3..39ad55478 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SummaryOfDay.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/SummaryOfDay.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/TimeStamped.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/TimeStamped.java index 0fe0002c5..6ffeb865b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/TimeStamped.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/TimeStamped.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ValidByDate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ValidByDate.java index bb800c343..482974914 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ValidByDate.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/ValidByDate.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java index a029c53ed..347bef6e8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/Weather.java @@ -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. @@ -45,6 +45,7 @@ public class Weather { JSONArray weather = new JSONArray(); JSONObject condition = new JSONObject(); JSONObject main = new JSONObject(); + JSONObject wind = new JSONObject(); try { condition.put("id", weatherSpec.currentConditionCode); @@ -53,14 +54,19 @@ public class Weather { condition.put("icon", Weather.mapToOpenWeatherMapIcon(weatherSpec.currentConditionCode)); weather.put(condition); + main.put("temp", weatherSpec.currentTemp); main.put("humidity", weatherSpec.currentHumidity); main.put("temp_min", weatherSpec.todayMinTemp); main.put("temp_max", weatherSpec.todayMaxTemp); - main.put("name", weatherSpec.location); + + wind.put("speed", (weatherSpec.windSpeed / 3.6f)); //meter per second + wind.put("deg", weatherSpec.windDirection); reconstructedOWMWeather.put("weather", weather); reconstructedOWMWeather.put("main", main); + reconstructedOWMWeather.put("name", weatherSpec.location); + reconstructedOWMWeather.put("wind", wind); } catch (JSONException e) { LOG.error("Error while reconstructing OWM weather reply"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java index 5f84eb423..d7603d086 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/WeatherSpec.java @@ -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. @@ -42,6 +43,9 @@ public class WeatherSpec implements Parcelable { public int currentHumidity; public int todayMaxTemp; public int todayMinTemp; + public float windSpeed; //km per hour + public int windDirection; //deg + public ArrayList forecasts = new ArrayList<>(); public WeatherSpec() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index a17d8fc25..b44657646 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Taavi Eomäe This file is part of Gadgetbridge. @@ -66,6 +66,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBMusicControlRece import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; +import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID; + // TODO: support option for a single reminder notification when notifications could not be delivered? // conditions: app was running and received notifications, but device was not connected. // maybe need to check for "unread notifications" on device for that. @@ -225,13 +227,13 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { private void handleGBDeviceEvent(GBDeviceEventSleepMonitorResult sleepMonitorResult) { Context context = getContext(); LOG.info("Got event for SLEEP_MONIOR_RES"); - Intent sleepMontiorIntent = new Intent(ChartsHost.REFRESH); - sleepMontiorIntent.putExtra("smartalarm_from", sleepMonitorResult.smartalarm_from); - sleepMontiorIntent.putExtra("smartalarm_to", sleepMonitorResult.smartalarm_to); - sleepMontiorIntent.putExtra("recording_base_timestamp", sleepMonitorResult.recording_base_timestamp); - sleepMontiorIntent.putExtra("alarm_gone_off", sleepMonitorResult.alarm_gone_off); + Intent sleepMonitorIntent = new Intent(ChartsHost.REFRESH); + sleepMonitorIntent.putExtra("smartalarm_from", sleepMonitorResult.smartalarm_from); + sleepMonitorIntent.putExtra("smartalarm_to", sleepMonitorResult.smartalarm_to); + sleepMonitorIntent.putExtra("recording_base_timestamp", sleepMonitorResult.recording_base_timestamp); + sleepMonitorIntent.putExtra("alarm_gone_off", sleepMonitorResult.alarm_gone_off); - LocalBroadcastManager.getInstance(context).sendBroadcast(sleepMontiorIntent); + LocalBroadcastManager.getInstance(context).sendBroadcast(sleepMonitorIntent); } private void handleGBDeviceEvent(GBDeviceEventScreenshot screenshot) { @@ -257,7 +259,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { NotificationCompat.Action action = new NotificationCompat.Action.Builder(android.R.drawable.ic_menu_share, "share", pendingShareIntent).build(); - Notification notif = new NotificationCompat.Builder(context) + Notification notif = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle("Screenshot taken") .setTicker("Screenshot taken") .setContentText(filename) @@ -298,10 +300,10 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { deviceEvent.phoneNumber = (String) GBApplication.getIDSenderLookup().lookup(deviceEvent.handle); } if (deviceEvent.phoneNumber != null) { - LOG.info("got notfication reply for SMS from " + deviceEvent.phoneNumber + " : " + deviceEvent.reply); + LOG.info("Got notification reply for SMS from " + deviceEvent.phoneNumber + " : " + deviceEvent.reply); SmsManager.getDefault().sendTextMessage(deviceEvent.phoneNumber, null, deviceEvent.reply, null, null); } else { - LOG.info("got notfication reply for notification id " + deviceEvent.handle + " : " + deviceEvent.reply); + LOG.info("Got notification reply for notification id " + deviceEvent.handle + " : " + deviceEvent.reply); action = NotificationListener.ACTION_REPLY; } break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 0215568d0..e2bdc2afc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -1,6 +1,7 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Avamander, Carsten Pfeiffer, +/* Copyright (C) 2015-2018 Andreas Shimokawa, Avamander, Carsten Pfeiffer, Daniele Gobbetti, Daniel Hauck, Frank Slezak, ivanovlev, João Paulo Barraca, - Julien Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld, Uwe Hermann + Julien Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld, Taavi Eomäe, + Uwe Hermann This file is part of Gadgetbridge. @@ -69,6 +70,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.receivers.GBAutoFetchReceiver; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; @@ -86,7 +88,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_DI import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_HEARTRATE_SLEEP_SUPPORT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_HEARTRATE_MEASUREMENT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_ENABLE_REALTIME_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_ACTIVITY_DATA; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FETCH_RECORDED_DATA; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_FIND_DEVICE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_HEARTRATE_TEST; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.ACTION_INSTALL; @@ -150,6 +152,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOT import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SUBJECT; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TITLE; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_TYPE; +import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_RECORDED_DATA_TYPES; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_URI; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_VIBRATION_INTENSITY; import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEATHER; @@ -173,6 +176,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private BluetoothConnectReceiver mBlueToothConnectReceiver = null; private BluetoothPairingRequestReceiver mBlueToothPairingRequestReceiver = null; private AlarmClockReceiver mAlarmClockReceiver = null; + private GBAutoFetchReceiver mGBAutoFetchReceiver = null; private AlarmReceiver mAlarmReceiver = null; private CalendarReceiver mCalendarReceiver = null; @@ -216,7 +220,6 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mGBDevice = device; boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized()); setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), DeviceHelper.getInstance().getCoordinator(device)); - GB.updateNotification(mGBDevice, context); } else { LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + device); } @@ -355,7 +358,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere || (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null)) { // NOTE: maybe not where it belongs if (prefs.getBoolean("pebble_force_untested", false)) { - // I would rather like to save that as an array in ShadredPreferences + // I would rather like to save that as an array in SharedPreferences // this would work but I dont know how to do the same in the Settings Activity's xml ArrayList replies = new ArrayList<>(); for (int i = 1; i <= 16; i++) { @@ -401,17 +404,19 @@ public class DeviceCommunicationService extends Service implements SharedPrefere mDeviceSupport.onHeartRateTest(); break; } - case ACTION_FETCH_ACTIVITY_DATA: { - mDeviceSupport.onFetchActivityData(); + case ACTION_FETCH_RECORDED_DATA: { + int dataTypes = intent.getIntExtra(EXTRA_RECORDED_DATA_TYPES, 0); + mDeviceSupport.onFetchRecordedData(dataTypes); break; } case ACTION_DISCONNECT: { mDeviceSupport.dispose(); - if (mGBDevice != null && mGBDevice.getState() == GBDevice.State.WAITING_FOR_RECONNECT) { - setReceiversEnableState(false, false, null); + if (mGBDevice != null) { mGBDevice.setState(GBDevice.State.NOT_CONNECTED); mGBDevice.sendDeviceUpdateIntent(this); } + setReceiversEnableState(false, false, null); + mGBDevice = null; mDeviceSupport = null; break; } @@ -674,6 +679,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere //Nothing wrong, it just means we're not running on omnirom. } } + if (GBApplication.getPrefs().getBoolean("auto_fetch_enabled", false) && + coordinator != null && coordinator.supportsActivityDataFetching()) { + mGBAutoFetchReceiver = new GBAutoFetchReceiver(); + registerReceiver(mGBAutoFetchReceiver, new IntentFilter("android.intent.action.USER_PRESENT")); + } } else { if (mPhoneCallReceiver != null) { unregisterReceiver(mPhoneCallReceiver); @@ -715,6 +725,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere if (mOmniJawsObserver != null) { getContentResolver().unregisterContentObserver(mOmniJawsObserver); } + if (mGBAutoFetchReceiver != null) { + unregisterReceiver(mGBAutoFetchReceiver); + mGBAutoFetchReceiver = null; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java index 087019ccc..9f23f9093 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupport.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 3ace63cb4..33e9f15d5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -1,6 +1,6 @@ -/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Daniele Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui, - Sergey Trofimov +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, + Daniele Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, + Sami Alaoui, Sergey Trofimov, tiparega This file is part of Gadgetbridge. @@ -30,15 +30,17 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch.XWatchSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.zetime.ZeTimeDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -115,6 +117,9 @@ public class DeviceSupportFactory { case MIBAND2: deviceSupport = new ServiceDeviceSupport(new MiBand2Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case MIBAND3: + deviceSupport = new ServiceDeviceSupport(new MiBand3Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case AMAZFITBIP: deviceSupport = new ServiceDeviceSupport(new AmazfitBipSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; @@ -136,13 +141,18 @@ public class DeviceSupportFactory { case EXRIZUK8: deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.EXRIZUK8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case Q8: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.Q8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case NO1F1: deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; case TECLASTH30: deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; - case ZETIME: + case XWATCH: + deviceSupport = new ServiceDeviceSupport(new XWatchSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + case ZETIME: deviceSupport = new ServiceDeviceSupport(new ZeTimeDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java.orig b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java.orig new file mode 100644 index 000000000..cccd267a4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java.orig @@ -0,0 +1,186 @@ +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, + Daniele Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, + Sami Alaoui, Sergey Trofimov, tiparega + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.widget.Toast; + +import java.lang.reflect.Constructor; +import java.util.EnumSet; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.liveview.LiveviewSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.jyou.TeclastH30Support; +<<<<<<< HEAD +import nodomain.freeyourgadget.gadgetbridge.service.devices.zetime.ZeTimeDeviceSupport; +======= +import nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch.XWatchSupport; +>>>>>>> master +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class DeviceSupportFactory { + private final BluetoothAdapter mBtAdapter; + private final Context mContext; + + public DeviceSupportFactory(Context context) { + mContext = context; + mBtAdapter = BluetoothAdapter.getDefaultAdapter(); + } + + public synchronized DeviceSupport createDeviceSupport(GBDevice device) throws GBException { + DeviceSupport deviceSupport = null; + String deviceAddress = device.getAddress(); + int indexFirstColon = deviceAddress.indexOf(":"); + if (indexFirstColon > 0) { + if (indexFirstColon == deviceAddress.lastIndexOf(":")) { // only one colon + deviceSupport = createTCPDeviceSupport(device); + } else { + // multiple colons -- bt? + deviceSupport = createBTDeviceSupport(device); + } + } else { + // no colon at all, maybe a class name? + deviceSupport = createClassNameDeviceSupport(device); + } + + if (deviceSupport != null) { + return deviceSupport; + } + + // no device found, check transport availability and warn + checkBtAvailability(); + return null; + } + + private DeviceSupport createClassNameDeviceSupport(GBDevice device) throws GBException { + String className = device.getAddress(); + try { + Class deviceSupportClass = Class.forName(className); + Constructor constructor = deviceSupportClass.getConstructor(); + DeviceSupport support = (DeviceSupport) constructor.newInstance(); + // has to create the device itself + support.setContext(device, null, mContext); + return support; + } catch (ClassNotFoundException e) { + return null; // not a class, or not known at least + } catch (Exception e) { + throw new GBException("Error creating DeviceSupport instance for " + className, e); + } + } + + private void checkBtAvailability() { + if (mBtAdapter == null) { + GB.toast(mContext.getString(R.string.bluetooth_is_not_supported_), Toast.LENGTH_SHORT, GB.WARN); + } else if (!mBtAdapter.isEnabled()) { + GB.toast(mContext.getString(R.string.bluetooth_is_disabled_), Toast.LENGTH_SHORT, GB.WARN); + } + } + + private DeviceSupport createBTDeviceSupport(GBDevice gbDevice) throws GBException { + if (mBtAdapter != null && mBtAdapter.isEnabled()) { + DeviceSupport deviceSupport = null; + + try { + switch (gbDevice.getType()) { + case PEBBLE: + deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case MIBAND: + deviceSupport = new ServiceDeviceSupport(new MiBandSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case MIBAND2: + deviceSupport = new ServiceDeviceSupport(new MiBand2Support(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case MIBAND3: + deviceSupport = new ServiceDeviceSupport(new MiBand3Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case AMAZFITBIP: + deviceSupport = new ServiceDeviceSupport(new AmazfitBipSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case AMAZFITCOR: + deviceSupport = new ServiceDeviceSupport(new AmazfitCorSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case VIBRATISSIMO: + deviceSupport = new ServiceDeviceSupport(new VibratissimoSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case LIVEVIEW: + deviceSupport = new ServiceDeviceSupport(new LiveviewSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case HPLUS: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.HPLUS), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case MAKIBESF68: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.MAKIBESF68), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case EXRIZUK8: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.EXRIZUK8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case Q8: + deviceSupport = new ServiceDeviceSupport(new HPlusSupport(DeviceType.Q8), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case NO1F1: + deviceSupport = new ServiceDeviceSupport(new No1F1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case TECLASTH30: + deviceSupport = new ServiceDeviceSupport(new TeclastH30Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; +<<<<<<< HEAD + case ZETIME: + deviceSupport = new ServiceDeviceSupport(new ZeTimeDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; +======= + case XWATCH: + deviceSupport = new ServiceDeviceSupport(new XWatchSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); +>>>>>>> master + } + if (deviceSupport != null) { + deviceSupport.setContext(gbDevice, mBtAdapter, mContext); + return deviceSupport; + } + } catch (Exception e) { + throw new GBException(mContext.getString(R.string.cannot_connect_bt_address_invalid_), e); + } + } + return null; + } + + private DeviceSupport createTCPDeviceSupport(GBDevice gbDevice) throws GBException { + try { + DeviceSupport deviceSupport = new ServiceDeviceSupport(new PebbleSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + deviceSupport.setContext(gbDevice, mBtAdapter, mContext); + return deviceSupport; + } catch (Exception e) { + throw new GBException("cannot connect to " + gbDevice, e); // FIXME: localize + } + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/NotificationCollectorMonitorService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/NotificationCollectorMonitorService.java index a88e340f6..f0fd333f0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/NotificationCollectorMonitorService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/NotificationCollectorMonitorService.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index de27c6037..4c77c5a01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -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 This file is part of Gadgetbridge. @@ -248,11 +248,11 @@ public class ServiceDeviceSupport implements DeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { if (checkBusy("fetch activity data")) { return; } - delegate.onFetchActivityData(); + delegate.onFetchRecordedData(dataTypes); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java index 08bf2fe7e..5724bdfba 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java index 7b1707b10..e7f157dfe 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEDeviceSupport.java @@ -1,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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java index 3b9e847ab..5dc29645f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractBTLEOperation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. @@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.Op public abstract class AbstractBTLEOperation implements GattCallback, BTLEOperation { private final T mSupport; protected OperationStatus operationStatus = OperationStatus.INITIAL; + private String name; protected AbstractBTLEOperation(T support) { mSupport = support; @@ -115,6 +116,22 @@ public abstract class AbstractBTLEOperation return mSupport.getDevice(); } + protected void setName(String name) { + this.name = name; + } + + @Override + public String getName() { + if (name != null) { + return name; + } + String busyTask = getDevice().getBusyTask(); + if (busyTask != null) { + return busyTask; + } + return getClass().getSimpleName(); + } + protected BluetoothGattCharacteristic getCharacteristic(UUID uuid) { return mSupport.getCharacteristic(uuid); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java index ec76decb1..f09314a88 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/AbstractGattCallback.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java index 12806b2e6..55ecb9dcc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BLETypeConversions.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Lukas Veneziano This file is part of Gadgetbridge. @@ -29,6 +30,9 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat * Provides methods to convert standard BLE units to byte sequences and vice versa. */ public class BLETypeConversions { + public static final int TZ_FLAG_NONE = 0; + public static final int TZ_FLAG_INCLUDE_DST_IN_TZ = 1; + /** * Converts a timestamp to the byte sequence to be sent to the current time characteristic * @@ -163,10 +167,33 @@ public class BLETypeConversions { return createCalendar(); } + public static long toUnsigned(int unsignedInt) { + return ((long) unsignedInt) & 0xffffffffL; + } + public static int toUnsigned(short value) { + return value & 0xffff; + } + + public static int toUnsigned(byte value) { + return value & 0xff; + } + + public static int toUint16(byte value) { + return toUnsigned(value); + } + public static int toUint16(byte... bytes) { return (bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8); } + public static int toInt16(byte... bytes) { + return (short) (bytes[0] & 0xff | ((bytes[1] & 0xff) << 8)); + } + + public static int toUint32(byte... bytes) { + return (bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8) | ((bytes[2] & 0xff) << 16) | ((bytes[3] & 0xff) << 24); + } + public static byte[] fromUint16(int value) { return new byte[] { (byte) (value & 0xff), @@ -229,7 +256,20 @@ public class BLETypeConversions { * @return sint8 value from -48..+56 */ public static byte mapTimeZone(TimeZone timeZone) { - int utcOffsetInHours = (timeZone.getRawOffset() / (1000 * 60 * 60)); + return mapTimeZone(timeZone, TZ_FLAG_NONE); + } + + /** + * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.time_zone.xml + * @param timeZone + * @return sint8 value from -48..+56 + */ + public static byte mapTimeZone(TimeZone timeZone, int timezoneFlags) { + int offsetMillis = timeZone.getRawOffset(); + if (false && timezoneFlags == TZ_FLAG_INCLUDE_DST_IN_TZ) { + offsetMillis += timeZone.getDSTSavings(); + } + int utcOffsetInHours = (offsetMillis / (1000 * 60 * 60)); return (byte) (utcOffsetInHours * 4); } @@ -289,6 +329,8 @@ public class BLETypeConversions { case SNAPCHAT: case TELEGRAM: case THREEMA: + case KONTALK: + case ANTOX: case TWITTER: case WHATSAPP: case VIBER: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BTLEOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BTLEOperation.java index b6efc50c3..779e9f5a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BTLEOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BTLEOperation.java @@ -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. @@ -20,4 +20,9 @@ import java.io.IOException; public interface BTLEOperation { void perform() throws IOException; + + /** + * Returns a human readable name of this operation, to be used e.g. in log output. + */ + String getName(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java index c27fb3e73..7a1728e13 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BleNamesResolver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, João +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, João Paulo Barraca, JohnnySun This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEAction.java index 1c502e61f..6ca33af52 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEAction.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java index 1feff2946..1f2cb4d34 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/BtLEQueue.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Sergey Trofimov, Uwe Hermann This file is part of Gadgetbridge. @@ -38,6 +38,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; +import nodomain.freeyourgadget.gadgetbridge.Logging; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; @@ -467,10 +468,7 @@ public final class BtLEQueue { public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { if (LOG.isDebugEnabled()) { - String content = ""; - for (byte b : characteristic.getValue()) { - content += String.format(" 0x%1x", b); - } + String content = Logging.formatBytes(characteristic.getValue()); LOG.debug("characteristic changed: " + characteristic.getUuid() + " value: " + content); } if (!checkCorrectGattInstance(gatt, "characteristic changed")) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java index 07d05e3cf..6e882ff77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattCharacteristic.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattDescriptor.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattDescriptor.java index 0306d5b55..4f17540c4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattDescriptor.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattDescriptor.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Daniele Gobbetti +/* Copyright (C) 2015-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattService.java index ba2d456bb..60d468e22 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/GattService.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java index edd747cb4..b1c22dc0d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/Transaction.java @@ -1,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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java index 2d8715930..cef217495 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/TransactionBuilder.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/AbortTransactionAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/AbortTransactionAction.java index 7d8469299..f83daabaf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/AbortTransactionAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/AbortTransactionAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java index 71b914000..319cfb1ac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/CheckInitializedAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java index 2efa240c4..71c3c3c9a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ConditionalWriteAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java index e31cdb3ed..4ddc45468 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/NotifyAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/PlainAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/PlainAction.java index ba7943d33..ab09bf9bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/PlainAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/PlainAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ReadAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ReadAction.java index f562bfb0c..c0984d1f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ReadAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/ReadAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceBusyAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceBusyAction.java index 220ad1d5c..733e97009 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceBusyAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceBusyAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java index 70e667839..f6d1937ed 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetDeviceStateAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java index 8812ade67..cbb476c76 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/SetProgressAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WaitAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WaitAction.java index 891bf2394..edfe25158 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WaitAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WaitAction.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java index c152a3d9d..1d21315d8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/actions/WriteAction.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java index 8a88b4647..4f2aa823d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/AbstractBleProfile.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java index f5fd359c0..c5bb0531d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/ValueDecoder.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertCategory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertCategory.java index 4556e4b3a..53ac4f095 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertCategory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertCategory.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertLevel.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertLevel.java index f2658fe63..f59885afd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertLevel.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertLevel.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationControl.java index ebb111ae1..051901717 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationControl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationControl.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java index 1032fb242..104f18c2b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertNotificationProfile.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertStatus.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertStatus.java index 218cc7c4c..84c04efac 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertStatus.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/AlertStatus.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/Command.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/Command.java index e7d4021db..1fdb01779 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/Command.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/Command.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Carsten Pfeiffer +/* Copyright (C) 2017-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java index f61ebc063..003dc2285 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/NewAlert.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java index b3b6100ac..f15ba4cb1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/OverflowStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Carsten Pfeiffer +/* Copyright (C) 2017-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/SupportedNewAlertCategory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/SupportedNewAlertCategory.java index d8449298e..ccecc68b3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/SupportedNewAlertCategory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/alertnotification/SupportedNewAlertCategory.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2016-2018 Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java index 67a1305d9..8f17dbc28 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java index 09b5cb884..2649e2acb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/battery/BatteryInfoProfile.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java index 0f996c717..c3712c3f0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java index 92514d71a..1ff7a4fc7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/deviceinfo/DeviceInfoProfile.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/BodySensorLocation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/BodySensorLocation.java index a779c8fa8..5963667f6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/BodySensorLocation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/BodySensorLocation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, Uwe Hermann +/* Copyright (C) 2016-2018 Carsten Pfeiffer, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java index 43b437759..841c09603 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btle/profiles/heartrate/HeartRateProfile.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java index 0d01c1aba..2ac36e5b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/common/SimpleNotification.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java index 4d9b80f04..afa36e7a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecord.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java index 28d034d5d..d5122d1e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySlot.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca +/* Copyright (C) 2017-2018 Andreas Shimokawa, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java index dfc0cc971..d4c2ae374 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordDaySummary.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java index 4f8e4f9b1..44d675ef5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordRealtime.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java index c453a2cea..238e4e50a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusDataRecordSleep.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java index 14a0e388c..24dc952bc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusHandlerThread.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 João Paulo Barraca +/* Copyright (C) 2017-2018 João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java index ebbc481cd..183370895 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/hplus/HPlusSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer, +/* Copyright (C) 2016-2018 Alberto, Andreas Shimokawa, Carsten Pfeiffer, ivanovlev, João Paulo Barraca, Pavel Motyrev, Quallenauge This file is part of Gadgetbridge. @@ -539,7 +539,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { if (syncHelper == null){ syncHelper = new HPlusHandlerThread(gbDevice, getContext(), this); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/BatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java similarity index 93% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/BatteryInfo.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java index a33988bb0..1b0d2083f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/BatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiBatteryInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami; import java.util.GregorianCalendar; @@ -45,14 +45,14 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.AbstractInfo; // 04 = 4 // num charges?? // 64 = 100 // how much was charged -public class BatteryInfo extends AbstractInfo { +public class HuamiBatteryInfo extends AbstractInfo { public static final byte DEVICE_BATTERY_NORMAL = 0; public static final byte DEVICE_BATTERY_CHARGING = 1; // public static final byte DEVICE_BATTERY_LOW = 1; // public static final byte DEVICE_BATTERY_CHARGING_FULL = 3; // public static final byte DEVICE_BATTERY_CHARGE_OFF = 4; - public BatteryInfo(byte[] data) { + public HuamiBatteryInfo(byte[] data) { super(data); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java index 7a643eacf..9f76582a9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java index 605da25f5..b1ddc33ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareType.java index fddb3b477..c6253d535 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiFirmwareType.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiIcon.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiIcon.java index de6ddfe6b..9803e7791 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiIcon.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiIcon.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa, Daniele Gobbetti, Lukas + Veneziano This file is part of Gadgetbridge. @@ -67,6 +68,8 @@ public class HuamiIcon { case CONVERSATIONS: case RIOT: case HIPCHAT: + case KONTALK: + case ANTOX: return WECHAT; case GENERIC_EMAIL: case GMAIL: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java new file mode 100644 index 000000000..eaec8cc8e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/ActivityDetailsParser.java @@ -0,0 +1,232 @@ +/* Copyright (C) 2017-2018 Andreas Shimokawa, AndrewH, Carsten Pfeiffer + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; +import nodomain.freeyourgadget.gadgetbridge.model.GPSCoordinate; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; + +public class ActivityDetailsParser { + private static final Logger LOG = LoggerFactory.getLogger(ActivityDetailsParser.class); + + private static final byte TYPE_GPS = 0; + private static final byte TYPE_HR = 1; + private static final byte TYPE_UNKNOWN2 = 2; + private static final byte TYPE_PAUSE = 3; + private static final byte TYPE_SPEED4 = 4; + private static final byte TYPE_SPEED5 = 5; + private static final byte TYPE_GPS_SPEED6 = 6; + + public static final BigDecimal HUAMI_TO_DECIMAL_DEGREES_DIVISOR = new BigDecimal(3000000.0); + private final BaseActivitySummary summary; + private final ActivityTrack activityTrack; +// private final int version; + private final Date baseDate; + private long baseLongitude; + private long baseLatitude; + private int baseAltitude; + private ActivityPoint lastActivityPoint; + + public boolean getSkipCounterByte() { + return skipCounterByte; + } + + public void setSkipCounterByte(boolean skipCounterByte) { + this.skipCounterByte = skipCounterByte; + } + + private boolean skipCounterByte; + + public ActivityDetailsParser(BaseActivitySummary summary) { + this.summary = summary; +// this.version = version; +// this.baseDate = baseDate; +// + this.baseLongitude = summary.getBaseLongitude(); + this.baseLatitude = summary.getBaseLatitude(); + this.baseAltitude = summary.getBaseAltitude(); + this.baseDate = summary.getStartTime(); + + this.activityTrack = new ActivityTrack(); + activityTrack.setUser(summary.getUser()); + activityTrack.setDevice(summary.getDevice()); + activityTrack.setName(summary.getName() + "-" + summary.getId()); + } + + public ActivityTrack parse(byte[] bytes) throws GBException { + int i = 0; + try { + long totalTimeOffset = 0; + int lastTimeOffset = 0; + while (i < bytes.length) { + if (skipCounterByte && (i % 17) == 0) { + i++; + } + + byte type = bytes[i++]; + int timeOffset = BLETypeConversions.toUnsigned(bytes[i++]); + // handle timeOffset overflows (1 byte, always increasing, relative to base) + if (lastTimeOffset <= timeOffset) { + timeOffset = timeOffset - lastTimeOffset; + lastTimeOffset += timeOffset; + } else { + lastTimeOffset = timeOffset; + } + totalTimeOffset += timeOffset; + + switch (type) { + case TYPE_GPS: + i += consumeGPSAndUpdateBaseLocation(bytes, i, totalTimeOffset); + break; + case TYPE_HR: + i += consumeHeartRate(bytes, i, totalTimeOffset); + break; + case TYPE_UNKNOWN2: + i += consumeUnknown2(bytes, i); + break; + case TYPE_PAUSE: + i += consumePause(bytes, i); + break; + case TYPE_SPEED4: + i += consumeSpeed4(bytes, i); + break; + case TYPE_SPEED5: + i += consumeSpeed5(bytes, i); + break; + case TYPE_GPS_SPEED6: + i += consumeSpeed6(bytes, i); + break; + } + } + } catch (IndexOutOfBoundsException ex) { + throw new GBException("Error parsing activity details: " + ex.getMessage(), ex); + } + + return activityTrack; + } + + private int consumeGPSAndUpdateBaseLocation(byte[] bytes, int offset, long timeOffset) { + int i = 0; + int longitudeDelta = BLETypeConversions.toInt16(bytes[offset + i++], bytes[offset + i++]); + int latitudeDelta = BLETypeConversions.toInt16(bytes[offset + i++], bytes[offset + i++]); + int altitudeDelta = BLETypeConversions.toInt16(bytes[offset + i++], bytes[offset + i++]); + + baseLongitude += longitudeDelta; + baseLatitude += latitudeDelta; + baseAltitude += altitudeDelta; + + GPSCoordinate coordinate = new GPSCoordinate( + convertHuamiValueToDecimalDegrees(baseLongitude), + convertHuamiValueToDecimalDegrees(baseLatitude), + baseAltitude); + + ActivityPoint ap = getActivityPointFor(timeOffset); + ap.setLocation(coordinate); + add(ap); + + return i; + } + + private double convertHuamiValueToDecimalDegrees(long huamiValue) { + BigDecimal result = new BigDecimal(huamiValue).divide(HUAMI_TO_DECIMAL_DEGREES_DIVISOR, GPSCoordinate.GPS_DECIMAL_DEGREES_SCALE, RoundingMode.HALF_UP); + return result.doubleValue(); + } + + private int consumeHeartRate(byte[] bytes, int offset, long timeOffsetSeconds) { + int v1 = BLETypeConversions.toUint16(bytes[offset]); + int v2 = BLETypeConversions.toUint16(bytes[offset + 1]); + int v3 = BLETypeConversions.toUint16(bytes[offset + 2]); + int v4 = BLETypeConversions.toUint16(bytes[offset + 3]); + int v5 = BLETypeConversions.toUint16(bytes[offset + 4]); + int v6 = BLETypeConversions.toUint16(bytes[offset + 5]); + + if (v2 == 0 && v3 == 0 && v4 == 0 && v5 == 0 && v6 == 0) { + // new version +// LOG.info("detected heart rate in 'new' version, where version is: " + summary.getVersion()); + LOG.info("detected heart rate in 'new' version format"); + ActivityPoint ap = getActivityPointFor(timeOffsetSeconds); + ap.setHeartRate(v1); + add(ap); + } else { + ActivityPoint ap = getActivityPointFor(v1); + ap.setHeartRate(v2); + add(ap); + + ap = getActivityPointFor(v3); + ap.setHeartRate(v4); + add(ap); + + ap = getActivityPointFor(v5); + ap.setHeartRate(v6); + add(ap); + } + return 6; + } + + private ActivityPoint getActivityPointFor(long timeOffsetSeconds) { + Date time = makeAbsolute(timeOffsetSeconds); + if (lastActivityPoint != null) { + if (lastActivityPoint.getTime().equals(time)) { + return lastActivityPoint; + } + } + return new ActivityPoint(time); + } + + private Date makeAbsolute(long timeOffsetSeconds) { + return new Date(baseDate.getTime() + timeOffsetSeconds * 1000); + } + + private void add(ActivityPoint ap) { + if (ap != lastActivityPoint) { + lastActivityPoint = ap; + activityTrack.addTrackPoint(ap); + } else { + LOG.info("skipping point!"); + } + } + + private int consumeUnknown2(byte[] bytes, int offset) { + return 6; // just guessing... + } + + private int consumePause(byte[] bytes, int i) { + return 6; // just guessing... + } + + private int consumeSpeed4(byte[] bytes, int offset) { + return 6; + } + + private int consumeSpeed5(byte[] bytes, int offset) { + return 6; + } + + private int consumeSpeed6(byte[] bytes, int offset) { + return 6; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java similarity index 73% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java index 5fcf1f964..2c218bc58 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; import java.util.HashMap; import java.util.Map; @@ -24,6 +24,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Version; public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { // gps detection is totally bogus, just the first 16 bytes @@ -45,21 +46,11 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { (byte) 0x8c, 0x36, 0x2e, (byte) 0x8c, (byte) 0x9c, 0x08, 0x54, (byte) 0xa6 }; - // guessed - at least it is the same across versions from 0.0.7.x to 0.0.9.x - // and different from other devices + // this is the same as Cor private static final byte[] FW_HEADER = new byte[]{ - 0x68, 0x46, 0x70, 0x47, 0x68, 0x46, 0x70, 0x47, - 0x68, 0x46, 0x70, 0x47, 0x68, 0x46, 0x70, 0x47 + 0x00, (byte) 0x98, 0x00, 0x20, (byte) 0xA5, 0x04, 0x00, 0x20, (byte) 0xAD, 0x04, 0x00, 0x20, (byte) 0xC5, 0x04, 0x00, 0x20 }; - // guessed - this is true for 0.1.0.11 - private static final byte[] FW_HEADER_NEW = new byte[]{ - 0x60, (byte) 0xeb, 0x03, 0x0c, 0x70, 0x46, 0x31, 0x46, - 0x3a, 0x46, 0x63, 0x46, (byte) 0xbd, (byte) 0xe8, (byte) 0xf0, (byte) 0x81 - }; - - private static final int FW_HEADER_OFFSET = 0x9330; - private static final byte[] GPS_ALMANAC_HEADER = new byte[]{ // probably wrong (byte) 0xa0, (byte) 0x80, 0x08, 0x00, (byte) 0x8b, 0x07 }; @@ -91,6 +82,17 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(47685, "0.1.0.43"); crcToVersion.put(2839, "0.1.0.44"); crcToVersion.put(30229, "0.1.0.45"); + crcToVersion.put(24302, "0.1.0.70"); + crcToVersion.put(1333, "0.1.0.80"); + crcToVersion.put(12017, "0.1.0.86"); + crcToVersion.put(8276, "0.1.1.14"); + crcToVersion.put(5914, "0.1.1.17"); + crcToVersion.put(6228, "0.1.1.29"); + crcToVersion.put(44223, "0.1.1.31"); + crcToVersion.put(39726, "0.1.1.36"); + crcToVersion.put(11062, "0.1.1.39"); + crcToVersion.put(56670, "0.1.1.41"); + crcToVersion.put(58736, "0.1.1.45"); // resources crcToVersion.put(12586, "0.0.8.74"); @@ -100,15 +102,28 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(22051, "0.0.9.40"); crcToVersion.put(46233, "0.0.9.49-0.1.0.11"); crcToVersion.put(12098, "0.1.0.17"); - crcToVersion.put(28696, "0.1.0.26-0.1.0.27"); + crcToVersion.put(28696, "0.1.0.26-27"); crcToVersion.put(5650, "0.1.0.33"); - crcToVersion.put(16117, "0.1.0.39-0.1.0.45"); + crcToVersion.put(16117, "0.1.0.39-45"); + crcToVersion.put(22506, "0.1.0.66-70"); + crcToVersion.put(42264, "0.1.0.77-80"); + crcToVersion.put(55934, "0.1.0.86-89"); + crcToVersion.put(26587, "0.1.1.14-25"); + crcToVersion.put(7446, "0.1.1.29"); + crcToVersion.put(47887, "0.1.1.31-36"); + crcToVersion.put(14334, "0.1.1.39"); + crcToVersion.put(21109, "0.1.1.41"); + crcToVersion.put(23073, "0.1.1.45"); // gps crcToVersion.put(61520, "9367,8f79a91,0,0,"); crcToVersion.put(8784, "9565,dfbd8fa,0,0,"); crcToVersion.put(16716, "9565,dfbd8faf42,0"); crcToVersion.put(54154, "9567,8b05506,0,0,"); + + // font + crcToVersion.put(61054, "8"); + crcToVersion.put(62291, "9 (Latin)"); } public AmazfitBipFirmwareInfo(byte[] bytes) { @@ -118,7 +133,7 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { @Override protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { if (ArrayUtils.startsWith(bytes, RES_HEADER) || ArrayUtils.startsWith(bytes, NEWRES_HEADER)) { - if (bytes.length > 500000) { // dont know how to distinguish from Cor .res + if ((bytes.length <= 100000) || (bytes.length > 700000)) { // dont know how to distinguish from Cor/Mi Band 3 .res return HuamiFirmwareType.INVALID; } return HuamiFirmwareType.RES; @@ -132,9 +147,15 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo { if (ArrayUtils.startsWith(bytes, GPS_CEP_HEADER)) { return HuamiFirmwareType.GPS_CEP; } - if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET) || ArrayUtils.equals(bytes, FW_HEADER_NEW, FW_HEADER_OFFSET)) { - // TODO: this is certainly not a correct validation, but it works for now - return HuamiFirmwareType.FIRMWARE; + if (ArrayUtils.startsWith(bytes, FW_HEADER)) { + String foundVersion = searchFirmwareVersion(bytes); + if (foundVersion != null) { + Version version = new Version(foundVersion); + if ((version.compareTo(new Version("0.0.8.00")) >= 0) && (version.compareTo(new Version("1.0.0.00")) < 0)) { + return HuamiFirmwareType.FIRMWARE; + } + } + return HuamiFirmwareType.INVALID; } if (ArrayUtils.startsWith(bytes, WATCHFACE_HEADER)) { return HuamiFirmwareType.WATCHFACE; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java similarity index 59% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java index 2181b12c6..2ff2f09f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java @@ -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. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; @@ -28,6 +28,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Locale; +import java.util.Set; import java.util.SimpleTimeZone; import java.util.UUID; @@ -38,23 +39,27 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipF import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; +import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes; import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ConditionalWriteAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertCategory; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertNotificationProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.operations.AmazfitBipFetchLogsOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations.AmazfitBipFetchLogsOperation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiIcon; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchActivityOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchSportsSummaryOperation; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import nodomain.freeyourgadget.gadgetbridge.util.Version; -import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service.ENDPOINT_DISPLAY_ITEMS; - public class AmazfitBipSupport extends MiBand2Support { private static final Logger LOG = LoggerFactory.getLogger(AmazfitBipSupport.class); @@ -126,23 +131,84 @@ public class AmazfitBipSupport extends MiBand2Support { @Override protected AmazfitBipSupport setDisplayItems(TransactionBuilder builder) { - /* - LOG.info("Enabling all display items"); + if (gbDevice.getType() != DeviceType.AMAZFITBIP) { + return this; // Disable for Cor for now + } + if (gbDevice.getFirmwareVersion() == null) { + LOG.warn("Device not initialized yet, won't set menu items"); + return this; + } - // This will brick the watch, don't enable it! - byte[] data = new byte[]{ENDPOINT_DISPLAY_ITEMS, (byte) 0xff, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + Version version = new Version(gbDevice.getFirmwareVersion()); + if (version.compareTo(new Version("0.1.1.14")) < 0) { + LOG.warn("Won't set menu items since firmware is too low to be safe"); + return this; + } + + Prefs prefs = GBApplication.getPrefs(); + Set pages = prefs.getStringSet("bip_display_items", null); + LOG.info("Setting display items to " + (pages == null ? "none" : pages)); + byte[] command = AmazfitBipService.COMMAND_CHANGE_SCREENS.clone(); + + boolean shortcut_weather = false; + boolean shortcut_alipay = false; + + if (pages != null) { + if (pages.contains("status")) { + command[1] |= 0x02; + } + if (pages.contains("activity")) { + command[1] |= 0x04; + } + if (pages.contains("weather")) { + command[1] |= 0x08; + } + if (pages.contains("alarm")) { + command[1] |= 0x10; + } + if (pages.contains("timer")) { + command[1] |= 0x20; + } + if (pages.contains("compass")) { + command[1] |= 0x40; + } + if (pages.contains("settings")) { + command[1] |= 0x80; + } + if (pages.contains("alipay")) { + command[2] |= 0x01; + } + if (pages.contains("shortcut_weather")) { + shortcut_weather = true; + } + if (pages.contains("shortcut_alipay")) { + shortcut_alipay = true; + } + } + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), command); + setShortcuts(builder, shortcut_weather, shortcut_alipay); - builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), data); - */ return this; } + private void setShortcuts(TransactionBuilder builder, boolean weather, boolean alipay) { + LOG.info("Setting shortcuts: weather=" + weather + " alipay=" + alipay); + + // Basically a hack to put weather first always, if alipay is the only enabled one + // there are actually two alipays set but the second one disabled.... :P + byte[] command = new byte[]{0x10, + (byte) ((alipay || weather) ? 0x80 : 0x00), (byte) (weather ? 0x02 : 0x01), + (byte) ((alipay && weather) ? 0x81 : 0x01), 0x01, + }; + + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), command); + } + @Override public void onSendWeather(WeatherSpec weatherSpec) { if (gbDevice.getFirmwareVersion() == null) { LOG.warn("Device not initialized yet, so not sending weather info"); return; - } boolean supportsConditionString = false; @@ -181,28 +247,30 @@ public class AmazfitBipSupport extends MiBand2Support { LOG.error("Error sending current weather", ex); } - try { - TransactionBuilder builder; - builder = performInitialized("Sending air quality index"); - int length = 8; - String aqiString = "(n/a)"; - if (supportsConditionString) { - length += aqiString.getBytes().length + 1; + if (gbDevice.getType() == DeviceType.AMAZFITBIP) { + try { + TransactionBuilder builder; + builder = performInitialized("Sending air quality index"); + int length = 8; + String aqiString = "(n/a)"; + if (supportsConditionString) { + length += aqiString.getBytes().length + 1; + } + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.put((byte) 4); + buf.putInt(weatherSpec.timestamp); + buf.put((byte) (tz_offset_hours * 4)); + buf.putShort((short) 0); + if (supportsConditionString) { + buf.put(aqiString.getBytes()); + buf.put((byte) 0); + } + builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array()); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Error sending air quality"); } - ByteBuffer buf = ByteBuffer.allocate(length); - buf.order(ByteOrder.LITTLE_ENDIAN); - buf.put((byte) 4); - buf.putInt(weatherSpec.timestamp); - buf.put((byte) (tz_offset_hours * 4)); - buf.putShort((short) 0); - if (supportsConditionString) { - buf.put(aqiString.getBytes()); - buf.put((byte) 0); - } - builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array()); - builder.queue(getQueue()); - } catch (IOException ex) { - LOG.error("Error sending air quality"); } try { @@ -258,14 +326,43 @@ public class AmazfitBipSupport extends MiBand2Support { } catch (Exception ex) { LOG.error("Error sending weather forecast", ex); } + + if (gbDevice.getType() == DeviceType.AMAZFITCOR) { + try { + TransactionBuilder builder; + builder = performInitialized("Sending forecast location"); + + int length = 2 + weatherSpec.location.getBytes().length; + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.put((byte) 8); + buf.put(weatherSpec.location.getBytes()); + buf.put((byte) 0); + + builder.write(getCharacteristic(AmazfitBipService.UUID_CHARACTERISTIC_WEATHER), buf.array()); + builder.queue(getQueue()); + } catch (Exception ex) { + LOG.error("Error sending current forecast location", ex); + } + } } @Override - public void onTestNewFunction() { + public void onFetchRecordedData(int dataTypes) { try { - new AmazfitBipFetchLogsOperation(this).perform(); + // FIXME: currently only one data type supported, these are meant to be flags + if (dataTypes == RecordedDataTypes.TYPE_ACTIVITY) { + new FetchActivityOperation(this).perform(); + } else if (dataTypes == RecordedDataTypes.TYPE_GPS_TRACKS) { + new FetchSportsSummaryOperation(this).perform(); + } else if (dataTypes == RecordedDataTypes.TYPE_DEBUGLOGS) { + new AmazfitBipFetchLogsOperation(this).perform(); + } + else { + LOG.warn("fetching multiple data types at once is not supported yet"); + } } catch (IOException ex) { - LOG.error("Unable to fetch logs", ex); + LOG.error("Unable to fetch recorded data types" + dataTypes, ex); } } @@ -303,43 +400,68 @@ public class AmazfitBipSupport extends MiBand2Support { } private AmazfitBipSupport setLanguage(TransactionBuilder builder) { + String language = Locale.getDefault().getLanguage(); String country = Locale.getDefault().getCountry(); LOG.info("Setting watch language, phone language = " + language + " country = " + country); - byte[] command; + final byte[] command_new; + final byte[] command_old; + String localeString; + switch (GBApplication.getPrefs().getInt("amazfitbip_language", -1)) { case 0: - command = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; + localeString = "zh_CN"; break; case 1: - command = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; + localeString = "zh_TW"; break; case 2: - command = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; + localeString = "en_US"; break; case 3: - Version version = new Version(gbDevice.getFirmwareVersion()); - if (version.compareTo(new Version("0.1.0.66")) >= 0) { - command = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; - } else { - command = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; - } + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; + localeString = "es_ES"; break; default: - if (language.equals("zh")) { - if (country.equals("TW") || country.equals("HK") || country.equals("MO")) { // Taiwan, Hong Kong, Macao - command = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; - } else { - command = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; - } - } else { - command = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; + switch (language) { + case "zh": + if (country.equals("TW") || country.equals("HK") || country.equals("MO")) { // Taiwan, Hong Kong, Macao + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_TRADITIONAL_CHINESE; + localeString = "zh_TW"; + } else { + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SIMPLIFIED_CHINESE; + localeString = "zh_CN"; + } + break; + case "es": + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_SPANISH; + localeString = "es_ES"; + break; + default: + command_old = AmazfitBipService.COMMAND_SET_LANGUAGE_ENGLISH; + localeString = "en_US"; + break; } } + command_new = AmazfitBipService.COMMAND_SET_LANGUAGE_NEW_TEMPLATE; + System.arraycopy(localeString.getBytes(), 0, command_new, 3, localeString.getBytes().length); + + builder.add(new ConditionalWriteAction(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION)) { + @Override + protected byte[] checkCondition() { + if (gbDevice.getType() == DeviceType.AMAZFITBIP && new Version(gbDevice.getFirmwareVersion()).compareTo(new Version("0.1.0.77")) >= 0) { + return command_new; + } else { + return command_old; + } + } + }); - builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), command); return this; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipTextNotificationStrategy.java similarity index 89% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipTextNotificationStrategy.java index 866f2f30b..60547c9ce 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/AmazfitBipTextNotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipTextNotificationStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; import android.support.annotation.NonNull; @@ -26,9 +26,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.NewAlert; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.OverflowStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2TextNotificationStrategy; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; -import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.Mi2TextNotificationStrategy; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; // This class in no longer in use except for incoming calls diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java new file mode 100644 index 000000000..d82d50d36 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/BipActivityType.java @@ -0,0 +1,69 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip; + +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; + +public enum BipActivityType { + Outdoor(1), + Treadmill(2), + Walking(3), + Cycling(4); + + private final int code; + + BipActivityType(final int code) { + this.code = code; + } + + public int toActivityKind() { + switch (this) { + case Outdoor: + return ActivityKind.TYPE_RUNNING; + case Treadmill: + return ActivityKind.TYPE_TREADMILL; + case Cycling: + return ActivityKind.TYPE_CYCLING; + case Walking: + return ActivityKind.TYPE_WALKING; + } + throw new RuntimeException("Not mapped activity kind for: " + this); + } + + public static BipActivityType fromCode(int bipCode) { + for (BipActivityType type : values()) { + if (type.code == bipCode) { + return type; + } + } + throw new RuntimeException("No matching BipActivityType for code: " + bipCode); + } + + public static BipActivityType fromActivityKind(int activityKind) { + switch (activityKind) { + case ActivityKind.TYPE_RUNNING: + return Outdoor; + case ActivityKind.TYPE_TREADMILL: + return Treadmill; + case ActivityKind.TYPE_CYCLING: + return Cycling; + case ActivityKind.TYPE_WALKING: + return Walking; + } + throw new RuntimeException("No matching activity activityKind: " + activityKind); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipFetchLogsOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java similarity index 83% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipFetchLogsOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java index c1026a8f1..0aa62184f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/amazfitbip/operations/AmazfitBipFetchLogsOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/operations/AmazfitBipFetchLogsOperation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.operations; import android.support.annotation.NonNull; import android.widget.Toast; @@ -26,7 +26,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -38,8 +37,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.AbstractFetchOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.AbstractFetchOperation; import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -50,6 +49,7 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { public AmazfitBipFetchLogsOperation(AmazfitBipSupport support) { super(support); + setName("fetch logs"); } @Override @@ -61,7 +61,7 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { return; } - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-hhmmss", Locale.US); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US); String filename = "amazfitbip_" + dateFormat.format(new Date()) + ".log"; File outputFile = new File(dir, filename ); @@ -89,8 +89,8 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { } @Override - protected void handleActivityFetchFinish() { - LOG.info("Fetching log data has finished"); + protected void handleActivityFetchFinish(boolean success) { + LOG.info(getName() +" data has finished"); try { logOutputStream.close(); logOutputStream = null; @@ -98,7 +98,7 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { LOG.warn("could not close output stream", e); return; } - super.handleActivityFetchFinish(); + super.handleActivityFetchFinish(success); } @Override @@ -113,18 +113,18 @@ public class AmazfitBipFetchLogsOperation extends AbstractFetchOperation { lastPacketCounter++; bufferActivityData(value); } else { - GB.toast("Error fetching activity data, invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); - handleActivityFetchFinish(); + GB.toast("Error " + getName() + " invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(false); } } @Override protected void bufferActivityData(@NonNull byte[] value) { try { - logOutputStream.write(Arrays.copyOfRange(value, 1, value.length)); + logOutputStream.write(value, 1, value.length - 1); } catch (IOException e) { LOG.warn("could not write to output stream", e); - handleActivityFetchFinish(); + handleActivityFetchFinish(false); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java index 3d5122861..b5502f8e1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorFirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa, Daniele Gobbetti This file is part of Gadgetbridge. @@ -24,19 +24,14 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; +import nodomain.freeyourgadget.gadgetbridge.util.Version; public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { - // guessed - at least it is the same accross current versions and different from other devices + // this is the same as Bip private static final byte[] FW_HEADER = new byte[]{ - (byte) 0x06, (byte) 0x48, (byte) 0x00, (byte) 0x47, (byte) 0xfe, (byte) 0xe7, (byte) 0xfe, (byte) 0xe7, - (byte) 0xfe, (byte) 0xe7, (byte) 0xfe, (byte) 0xe7, (byte) 0xfe, (byte) 0xe7, (byte) 0xfe, (byte) 0xe7 + 0x00, (byte) 0x98, 0x00, 0x20, (byte) 0xA5, 0x04, 0x00, 0x20, (byte) 0xAD, 0x04, 0x00, 0x20, (byte) 0xC5, 0x04, 0x00, 0x20 }; - //FIXME: this is a moving target :/ - private static final int FW_HEADER_OFFSET = 0x9330; - private static final int FW_HEADER_OFFSET_2 = 0x9340; - private static final int FW_HEADER_OFFSET_3 = 0x9288; - private static final int COMPRESSED_RES_HEADER_OFFSET = 0x9; private static Map crcToVersion = new HashMap<>(); @@ -45,11 +40,16 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { // firmware crcToVersion.put(39948, "1.0.5.60"); crcToVersion.put(62147, "1.0.5.78"); + crcToVersion.put(54213, "1.0.6.76"); + crcToVersion.put(9458, "1.0.7.52"); + crcToVersion.put(51575, "1.0.7.88"); // resources crcToVersion.put(46341, "RES 1.0.5.60"); crcToVersion.put(21770, "RES 1.0.5.78"); - + crcToVersion.put(64977, "RES 1.0.6.76"); + crcToVersion.put(60501, "RES 1.0.7.52-71"); + crcToVersion.put(31263, "RES 1.0.7.77-91"); } public AmazfitCorFirmwareInfo(byte[] bytes) { @@ -63,14 +63,24 @@ public class AmazfitCorFirmwareInfo extends HuamiFirmwareInfo { return HuamiFirmwareType.INVALID; } return HuamiFirmwareType.RES; - } else if (ArrayUtils.equals(bytes, RES_HEADER, COMPRESSED_RES_HEADER_OFFSET) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET)) { + } + if (ArrayUtils.equals(bytes, RES_HEADER, COMPRESSED_RES_HEADER_OFFSET) || ArrayUtils.equals(bytes, NEWRES_HEADER, COMPRESSED_RES_HEADER_OFFSET)) { return HuamiFirmwareType.RES_COMPRESSED; - } else if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET) || ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET_2) || ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET_3)) { - // TODO: this is certainly not a correct validation, but it works for now - return HuamiFirmwareType.FIRMWARE; - } else if (ArrayUtils.startsWith(bytes, WATCHFACE_HEADER)) { + } + if (ArrayUtils.startsWith(bytes, FW_HEADER)) { + String foundVersion = searchFirmwareVersion(bytes); + if (foundVersion != null) { + Version version = new Version(foundVersion); + if ((version.compareTo(new Version("1.0.5.00")) >= 0) && (version.compareTo(new Version("2.0.0.00")) < 0)) { + return HuamiFirmwareType.FIRMWARE; + } + } + return HuamiFirmwareType.INVALID; + } + if (ArrayUtils.startsWith(bytes, WATCHFACE_HEADER)) { return HuamiFirmwareType.WATCHFACE; } + return HuamiFirmwareType.INVALID; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java index 8481ff835..60164c544 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitcor/AmazfitCorSupport.java @@ -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. @@ -23,7 +23,7 @@ import java.io.IOException; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorFWHelper; -import nodomain.freeyourgadget.gadgetbridge.service.devices.amazfitbip.AmazfitBipSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; public class AmazfitCorSupport extends AmazfitBipSupport { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/AbstractMiBand2Operation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/AbstractMiBand2Operation.java similarity index 91% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/AbstractMiBand2Operation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/AbstractMiBand2Operation.java index 67e8ec0a0..4cc5b2768 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/AbstractMiBand2Operation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/AbstractMiBand2Operation.java @@ -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. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.operations.AbstractMiBandOperation; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2FirmwareInfo.java similarity index 83% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2FirmwareInfo.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2FirmwareInfo.java index 0c1670eb3..8cccc9ceb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2FirmwareInfo.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Davis + Mosenkovs This file is part of Gadgetbridge. @@ -14,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2; import java.util.HashMap; import java.util.Map; @@ -47,6 +48,9 @@ public class Mi2FirmwareInfo extends HuamiFirmwareInfo { private static final int FW_HEADER_OFFSET = 0x150; + private static final byte FW_MAGIC = (byte) 0xf8; + private static final int FW_MAGIC_OFFSET = 0x17d; + private static Map crcToVersion = new HashMap<>(); static { @@ -55,15 +59,18 @@ public class Mi2FirmwareInfo extends HuamiFirmwareInfo { crcToVersion.put(49197, "1.0.0.53"); crcToVersion.put(32450, "1.0.1.28"); crcToVersion.put(51770, "1.0.1.34"); - crcToVersion.put(3929, "1.0.1.39"); + crcToVersion.put(3929, "1.0.1.39"); crcToVersion.put(47364, "1.0.1.54"); crcToVersion.put(44776, "1.0.1.59"); crcToVersion.put(27318, "1.0.1.67"); crcToVersion.put(54702, "1.0.1.69"); - + crcToVersion.put(31698, "1.0.1.81"); + crcToVersion.put(53474, "1.0.1.81 (tph)"); + crcToVersion.put(46048, "1.0.1.81 (tph as7000)"); + crcToVersion.put(19930, "1.0.1.81 (tph india)"); // fonts crcToVersion.put(45624, "Font"); - crcToVersion.put(6377, "Font (En)"); + crcToVersion.put(6377, "Font (En)"); } public Mi2FirmwareInfo(byte[] bytes) { @@ -74,7 +81,8 @@ public class Mi2FirmwareInfo extends HuamiFirmwareInfo { if (ArrayUtils.startsWith(bytes, HuamiFirmwareInfo.FT_HEADER)) { return HuamiFirmwareType.FONT; } - if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET)) { + if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET) + && (bytes[FW_MAGIC_OFFSET] == FW_MAGIC)) { // TODO: this is certainly not a correct validation, but it works for now return HuamiFirmwareType.FIRMWARE; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2NotificationStrategy.java similarity index 71% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2NotificationStrategy.java index 4ec7471f1..880ad8eaa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2NotificationStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Martin Piatka This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2; import android.bluetooth.BluetoothGattCharacteristic; import android.support.annotation.Nullable; @@ -37,24 +37,22 @@ public class Mi2NotificationStrategy extends V2NotificationStrategy 0) { + short vibration = (short) vibrationProfile.getOnOffSequence()[0]; + short pause = (short) vibrationProfile.getOnOffSequence()[1]; + waitDuration = (vibration + pause) * repeat; + builder.write(alert, new byte[]{-1, (byte) (vibration & 255), (byte) (vibration >> 8 & 255), (byte) (pause & 255), (byte) (pause >> 8 & 255), repeat}); + } - if (++j < onOffSequence.length) { - int off = Math.max(onOffSequence[j], 25); // wait at least 25ms - builder.wait(off); - } + waitDuration = Math.max(waitDuration, 4000); + builder.wait(waitDuration); - if (extraAction != null) { - builder.add(extraAction); - } - } + if (extraAction != null) { + builder.add(extraAction); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2TextNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2TextNotificationStrategy.java similarity index 97% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2TextNotificationStrategy.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2TextNotificationStrategy.java index 17e774776..b69ea329b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/Mi2TextNotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/Mi2TextNotificationStrategy.java @@ -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. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2; import android.bluetooth.BluetoothGattCharacteristic; import android.support.annotation.NonNull; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/MiBand2Support.java similarity index 95% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/MiBand2Support.java index 6f1ed3ed6..ab1d4dbe6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/MiBand2Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/MiBand2Support.java @@ -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, JohnnySun, José Rebelo, Julien Pivotto, Kasha, Michal Novotny, Sergey Trofimov, Steffen Liebergeld @@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; @@ -56,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallContro import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.ActivateDisplayOnLift; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; @@ -68,6 +69,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySample; @@ -99,13 +101,15 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotificat import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile; import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.heartrate.HeartRateProfile; import nodomain.freeyourgadget.gadgetbridge.service.devices.common.SimpleNotification; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.NotificationStrategy; import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.RealtimeSamplesSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions.StopNotificationAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.FetchActivityOperation; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.InitOperation; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations.UpdateFirmwareOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.actions.StopNotificationAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchActivityOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.FetchSportsSummaryOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.InitOperation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations.UpdateFirmwareOperation; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; @@ -213,6 +217,14 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { return builder; } + /** + * Returns the given date/time (calendar) as a byte sequence, suitable for sending to the + * Mi Band 2 (or derivative). The band appears to not handle DST offsets, so we simply add this + * to the timezone. + * @param calendar + * @param precision + * @return + */ public byte[] getTimeBytes(Calendar calendar, TimeUnit precision) { byte[] bytes; if (precision == TimeUnit.MINUTES) { @@ -222,7 +234,8 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } else { throw new IllegalArgumentException("Unsupported precision, only MINUTES and SECONDS are supported till now"); } - byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(calendar.getTimeZone()) }; // 0 = adjust reason bitflags? or DST offset?? , timezone + byte[] tail = new byte[] { 0, BLETypeConversions.mapTimeZone(calendar.getTimeZone(), BLETypeConversions.TZ_FLAG_INCLUDE_DST_IN_TZ) }; + // 0 = adjust reason bitflags? or DST offset?? , timezone // byte[] tail = new byte[] { 0x2 }; // reason byte[] all = BLETypeConversions.join(bytes, tail); return all; @@ -791,7 +804,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { try { new FetchActivityOperation(this).perform(); } catch (IOException ex) { @@ -1188,7 +1201,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { sample.setHeartRate(getHeartrateBpm()); sample.setSteps(getSteps()); sample.setRawIntensity(ActivitySample.NOT_MEASURED); - sample.setRawKind(MiBand2SampleProvider.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? + sample.setRawKind(HuamiConst.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? provider.addGBActivitySample(sample); @@ -1279,7 +1292,7 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { private void handleBatteryInfo(byte[] value, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { - BatteryInfo info = new BatteryInfo(value); + HuamiBatteryInfo info = new HuamiBatteryInfo(value); batteryCmd.level = ((short) info.getLevelInPercent()); batteryCmd.state = info.getState(); batteryCmd.lastChargeTime = info.getLastChargeTime(); @@ -1330,7 +1343,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { case MiBandConst.PREF_MI2_GOAL_NOTIFICATION: setGoalNotification(builder); break; - case MiBandConst.PREF_MI2_ACTIVATE_DISPLAY_ON_LIFT: + case MiBandConst.PREF_ACTIVATE_DISPLAY_ON_LIFT: + case MiBandConst.PREF_DISPLAY_ON_LIFT_START: + case MiBandConst.PREF_DISPLAY_ON_LIFT_END: setActivateDisplayOnLiftWrist(builder); break; case MiBandConst.PREF_MI2_DISPLAY_ITEMS: @@ -1369,10 +1384,9 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { try { - TransactionBuilder builder = performInitialized("test realtime steps"); - builder.read(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_7_REALTIME_STEPS)); - builder.queue(getQueue()); - } catch (IOException e) { + new FetchSportsSummaryOperation(this).perform(); + } catch (IOException ex) { + LOG.error("Unable to fetch MI activity data", ex); } } @@ -1418,12 +1432,32 @@ public class MiBand2Support extends AbstractBTLEDeviceSupport { } private MiBand2Support setActivateDisplayOnLiftWrist(TransactionBuilder builder) { - boolean enable = HuamiCoordinator.getActivateDisplayOnLiftWrist(); - LOG.info("Setting activate display on lift wrist to " + enable); - if (enable) { - builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST); - } else { - builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST); + ActivateDisplayOnLift displayOnLift = HuamiCoordinator.getActivateDisplayOnLiftWrist(getContext()); + LOG.info("Setting activate display on lift wrist to " + displayOnLift); + + switch (displayOnLift) { + case ON: + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_ENABLE_DISPLAY_ON_LIFT_WRIST); + break; + case OFF: + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), MiBand2Service.COMMAND_DISABLE_DISPLAY_ON_LIFT_WRIST); + break; + case SCHEDULED: + byte[] cmd = MiBand2Service.COMMAND_SCHEDULE_DISPLAY_ON_LIFT_WRIST.clone(); + + Calendar calendar = GregorianCalendar.getInstance(); + + Date start = HuamiCoordinator.getDisplayOnLiftStart(); + calendar.setTime(start); + cmd[4] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + cmd[5] = (byte) calendar.get(Calendar.MINUTE); + + Date end = HuamiCoordinator.getDisplayOnLiftEnd(); + calendar.setTime(end); + cmd[6] = (byte) calendar.get(Calendar.HOUR_OF_DAY); + cmd[7] = (byte) calendar.get(Calendar.MINUTE); + + builder.write(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_3_CONFIGURATION), cmd); } return this; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/actions/StopNotificationAction.java similarity index 91% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/actions/StopNotificationAction.java index 8b33a4186..97e41aaef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/actions/StopNotificationAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/actions/StopNotificationAction.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Carsten Pfeiffer +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.actions; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.actions; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/AbstractFetchOperation.java similarity index 81% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/AbstractFetchOperation.java index ffa73fab8..1d0b5e72c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/AbstractFetchOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/AbstractFetchOperation.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. @@ -14,14 +15,13 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.content.SharedPreferences; import android.support.annotation.CallSuper; import android.support.annotation.NonNull; -import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,8 +40,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.AbstractMiBand2Operation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -57,6 +57,7 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { protected BluetoothGattCharacteristic characteristicActivityData; protected BluetoothGattCharacteristic characteristicFetch; protected Calendar startTimestamp; + protected int expectedDataLength; public AbstractFetchOperation(MiBand2Support support) { super(support); @@ -66,7 +67,8 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { protected void enableNeededNotifications(TransactionBuilder builder, boolean enable) { if (!enable) { // dynamically enabled, but always disabled on finish - builder.notify(getCharacteristic(MiBand2Service.UUID_CHARACTERISTIC_5_ACTIVITY_DATA), enable); + builder.notify(characteristicFetch, enable); + builder.notify(characteristicActivityData, enable); } } @@ -78,7 +80,7 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { protected void startFetching() throws IOException { lastPacketCounter = -1; - TransactionBuilder builder = performInitialized("fetching activity data"); + TransactionBuilder builder = performInitialized(getName()); getSupport().setLowLatency(builder); if (fetchCount == 0) { builder.add(new SetDeviceBusyAction(getDevice(), getContext().getString(R.string.busy_task_fetch_activity_data), getContext())); @@ -115,7 +117,8 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { } @CallSuper - protected void handleActivityFetchFinish() { + protected void handleActivityFetchFinish(boolean success) { + GB.updateTransferNotification(null,"",false,100,getContext()); operationFinished(); unsetBusy(); } @@ -139,28 +142,30 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { // first two bytes are whether our request was accepted if (ArrayUtils.equals(value, MiBand2Service.RESPONSE_ACTIVITY_DATA_START_DATE_SUCCESS, 0)) { // the third byte (0x01 on success) = ? - // the 4th - 7th bytes probably somehow represent the number of bytes/packets to expect + // the 4th - 7th bytes epresent the number of bytes/packets to expect, excluding the counter bytes + expectedDataLength = BLETypeConversions.toUint32(Arrays.copyOfRange(value, 3, 7)); // last 8 bytes are the start date Calendar startTimestamp = getSupport().fromTimeBytes(Arrays.copyOfRange(value, 7, value.length)); setStartTimestamp(startTimestamp); - GB.toast(getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, - DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); + GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), + getContext().getString(R.string.FetchActivityOperation_about_to_transfer_since, + DateFormat.getDateTimeInstance().format(startTimestamp.getTime())), true, 0, getContext());; } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); - handleActivityFetchFinish(); + handleActivityFetchFinish(false); } } else if (value.length == 3) { if (Arrays.equals(MiBand2Service.RESPONSE_FINISH_SUCCESS, value)) { - handleActivityFetchFinish(); + handleActivityFetchFinish(true); } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); - handleActivityFetchFinish(); + handleActivityFetchFinish(false); } } else { LOG.warn("Unexpected activity metadata: " + Logging.formatBytes(value)); - handleActivityFetchFinish(); + handleActivityFetchFinish(false); } } @@ -168,6 +173,10 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { this.startTimestamp = startTimestamp; } + protected Calendar getLastStartTimestamp() { + return startTimestamp; + } + protected void saveLastSyncTimestamp(@NonNull GregorianCalendar timestamp) { SharedPreferences.Editor editor = GBApplication.getPrefs().getPreferences().edit(); editor.putLong(getLastSyncTimeKey(), timestamp.getTimeInMillis()); @@ -183,7 +192,7 @@ public abstract class AbstractFetchOperation extends AbstractMiBand2Operation { return calendar; } GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.add(Calendar.DAY_OF_MONTH, -10); + calendar.add(Calendar.DAY_OF_MONTH, - 100); return calendar; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchActivityOperation.java similarity index 89% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchActivityOperation.java index d87d10235..c68b178da 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchActivityOperation.java @@ -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. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; import android.text.format.DateUtils; import android.widget.Toast; @@ -42,7 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -57,6 +57,7 @@ public class FetchActivityOperation extends AbstractFetchOperation { public FetchActivityOperation(MiBand2Support support) { super(support); + setName("fetching activity data"); } @Override @@ -74,24 +75,24 @@ public class FetchActivityOperation extends AbstractFetchOperation { builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_DATA}); } - protected void handleActivityFetchFinish() { - LOG.info("Fetching activity data has finished round " + fetchCount); + protected void handleActivityFetchFinish(boolean success) { + LOG.info(getName() + " has finished round " + fetchCount); GregorianCalendar lastSyncTimestamp = saveSamples(); if (lastSyncTimestamp != null && needsAnotherFetch(lastSyncTimestamp)) { try { startFetching(); return; } catch (IOException ex) { - LOG.error("Error starting another round of fetching activity data", ex); + LOG.error("Error starting another round of " + getName(), ex); } } - super.handleActivityFetchFinish(); + super.handleActivityFetchFinish(success); } private boolean needsAnotherFetch(GregorianCalendar lastSyncTimestamp) { if (fetchCount > 5) { - LOG.warn("Already jave 5 fetch rounds, not doing another one."); + LOG.warn("Already have 5 fetch rounds, not doing another one."); return false; } @@ -166,12 +167,13 @@ public class FetchActivityOperation extends AbstractFetchOperation { lastPacketCounter++; bufferActivityData(value); } else { - GB.toast("Error fetching activity data, invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); - handleActivityFetchFinish(); + GB.toast("Error " + getName() + ", invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(false); return; } } else { - GB.toast("Error fetching activity data, unexpected package length: " + value.length, Toast.LENGTH_LONG, GB.ERROR); + GB.toast("Error " + getName() + ", unexpected package length: " + value.length, Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(false); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsDetailsOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsDetailsOperation.java new file mode 100644 index 000000000..721e4867a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsDetailsOperation.java @@ -0,0 +1,189 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; + +import android.support.annotation.NonNull; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.GregorianCalendar; +import java.util.concurrent.TimeUnit; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary; +import nodomain.freeyourgadget.gadgetbridge.export.ActivityTrackExporter; +import nodomain.freeyourgadget.gadgetbridge.export.GPXExporter; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityTrack; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.ActivityDetailsParser; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; +import nodomain.freeyourgadget.gadgetbridge.util.FileUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +/** + * An operation that fetches activity data. For every fetch, a new operation must + * be created, i.e. an operation may not be reused for multiple fetches. + */ +public class FetchSportsDetailsOperation extends AbstractFetchOperation { + private static final Logger LOG = LoggerFactory.getLogger(FetchSportsDetailsOperation.class); + private final BaseActivitySummary summary; + private final String lastSyncTimeKey; + + private ByteArrayOutputStream buffer; + + public FetchSportsDetailsOperation(@NonNull BaseActivitySummary summary, @NonNull MiBand2Support support, @NonNull String lastSyncTimeKey) { + super(support); + setName("fetching sport details"); + this.summary = summary; + this.lastSyncTimeKey = lastSyncTimeKey; + } + + @Override + protected void startFetching(TransactionBuilder builder) { + LOG.info("start " + getName()); + buffer = new ByteArrayOutputStream(1024); + GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); + + builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { + MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, + AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_DETAILS}, + getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); + builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply + builder.notify(characteristicActivityData, true); + builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_DATA }); + } + + @Override + protected void handleActivityFetchFinish(boolean success) { + LOG.info(getName() + " has finished round " + fetchCount); +// GregorianCalendar lastSyncTimestamp = saveSamples(); +// if (lastSyncTimestamp != null && needsAnotherFetch(lastSyncTimestamp)) { +// try { +// startFetching(); +// return; +// } catch (IOException ex) { +// LOG.error("Error starting another round of fetching activity data", ex); +// } +// } + + + if (success) { + ActivityDetailsParser parser = new ActivityDetailsParser(summary); + parser.setSkipCounterByte(false); // is already stripped + try { + ActivityTrack track = parser.parse(buffer.toByteArray()); + ActivityTrackExporter exporter = createExporter(); + String fileName = FileUtils.makeValidFileName("gadgetbridge-track-" + DateTimeUtils.formatIso8601(summary.getStartTime()) + ".gpx"); + File targetFile = new File(FileUtils.getExternalFilesDir(), fileName); + + try { + exporter.performExport(track, targetFile); + + try (DBHandler dbHandler = GBApplication.acquireDB()) { + summary.setGpxTrack(targetFile.getAbsolutePath()); + dbHandler.getDaoSession().getBaseActivitySummaryDao().update(summary); + } + } catch (ActivityTrackExporter.GPXTrackEmptyException ex) { + GB.toast(getContext(), "This activity does not contain GPX tracks.", Toast.LENGTH_LONG, GB.ERROR, ex); + } + + GregorianCalendar endTime = BLETypeConversions.createCalendar(); + endTime.setTime(summary.getEndTime()); + saveLastSyncTimestamp(endTime); + } catch (Exception ex) { + GB.toast(getContext(), "Error getting activity details: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + + super.handleActivityFetchFinish(success); + } + + protected ActivityTrackExporter createExporter() { + GPXExporter exporter = new GPXExporter(); + exporter.setCreator(GBApplication.app().getNameAndVersion()); + return exporter; + } + + /** + * Method to handle the incoming activity data. + * There are two kind of messages we currently know: + * - the first one is 11 bytes long and contains metadata (how many bytes to expect, when the data starts, etc.) + * - the second one is 20 bytes long and contains the actual activity data + *

+ * The first message type is parsed by this method, for every other length of the value param, bufferActivityData is called. + * + * @param value + */ + @Override + protected void handleActivityNotif(byte[] value) { + LOG.warn("sports details: " + Logging.formatBytes(value)); + + if (!isOperationRunning()) { + LOG.error("ignoring sports details notification because operation is not running. Data length: " + value.length); + getSupport().logMessageContent(value); + return; + } + + if (value.length < 2) { + LOG.error("unexpected sports details data length: " + value.length); + getSupport().logMessageContent(value); + return; + } + + if ((byte) (lastPacketCounter + 1) == value[0] ) { + lastPacketCounter++; + bufferActivityData(value); + } else { + GB.toast("Error " + getName() + ", invalid package counter: " + value[0] + ", last was: " + lastPacketCounter, Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(false); + return; + } + } + + /** + * Buffers the given activity summary data. If the total size is reached, + * it is converted to an object and saved in the database. + * @param value + */ + @Override + protected void bufferActivityData(byte[] value) { + buffer.write(value, 1, value.length - 1); // skip the counter + } + + @Override + protected String getLastSyncTimeKey() { + return lastSyncTimeKey; + } + + protected GregorianCalendar getLastSuccessfulSyncTime() { + GregorianCalendar calendar = BLETypeConversions.createCalendar(); + calendar.setTime(summary.getStartTime()); + return calendar; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsSummaryOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsSummaryOperation.java new file mode 100644 index 000000000..fb2d6bad1 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/FetchSportsSummaryOperation.java @@ -0,0 +1,253 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.concurrent.TimeUnit; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.Logging; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipService; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; +import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.WaitAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.BipActivityType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +/** + * An operation that fetches activity data. For every fetch, a new operation must + * be created, i.e. an operation may not be reused for multiple fetches. + */ +public class FetchSportsSummaryOperation extends AbstractFetchOperation { + private static final Logger LOG = LoggerFactory.getLogger(FetchSportsSummaryOperation.class); + + private ByteArrayOutputStream buffer = new ByteArrayOutputStream(140); + + public FetchSportsSummaryOperation(MiBand2Support support) { + super(support); + setName("fetching sport summaries"); + } + + @Override + protected void startFetching(TransactionBuilder builder) { + LOG.info("start" + getName()); + GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); + builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { + MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, + AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES}, + getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); + builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply + builder.notify(characteristicActivityData, true); + builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_DATA }); + } + + @Override + protected void handleActivityFetchFinish(boolean success) { + LOG.info(getName() + " has finished round " + fetchCount); + +// GregorianCalendar lastSyncTimestamp = saveSamples(); +// if (lastSyncTimestamp != null && needsAnotherFetch(lastSyncTimestamp)) { +// try { +// startFetching(); +// return; +// } catch (IOException ex) { +// LOG.error("Error starting another round of fetching activity data", ex); +// } +// } + + BaseActivitySummary summary = null; + if (success) { + summary = parseSummary(buffer); + try (DBHandler dbHandler = GBApplication.acquireDB()) { + DaoSession session = dbHandler.getDaoSession(); + Device device = DBHelper.getDevice(getDevice(), session); + User user = DBHelper.getUser(session); + summary.setDevice(device); + summary.setUser(user); + session.getBaseActivitySummaryDao().insertOrReplace(summary); + } catch (Exception ex) { + GB.toast(getContext(), "Error saving activity summary", Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + + super.handleActivityFetchFinish(success); + + if (summary != null) { + FetchSportsDetailsOperation nextOperation = new FetchSportsDetailsOperation(summary, getSupport(), getLastSyncTimeKey()); + try { + nextOperation.perform(); + } catch (IOException ex) { + GB.toast(getContext(), "Unable to fetch activity details: " + ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + LOG.warn("characteristic read: " + characteristic.getUuid() + ": " + Logging.formatBytes(characteristic.getValue())); + return super.onCharacteristicRead(gatt, characteristic, status); + } + + /** + * Method to handle the incoming activity data. + * There are two kind of messages we currently know: + * - the first one is 11 bytes long and contains metadata (how many bytes to expect, when the data starts, etc.) + * - the second one is 20 bytes long and contains the actual activity data + *

+ * The first message type is parsed by this method, for every other length of the value param, bufferActivityData is called. + * + * @param value + */ + @Override + protected void handleActivityNotif(byte[] value) { + LOG.warn("sports summary data: " + Logging.formatBytes(value)); + + if (!isOperationRunning()) { + LOG.error("ignoring activity data notification because operation is not running. Data length: " + value.length); + getSupport().logMessageContent(value); + return; + } + + if (value.length < 2) { + LOG.error("unexpected sports summary data length: " + value.length); + getSupport().logMessageContent(value); + return; + } + + if ((byte) (lastPacketCounter + 1) == value[0] ) { + lastPacketCounter++; + bufferActivityData(value); + } else { + GB.toast("Error " + getName() + ", invalid package counter: " + value[0] + ", last was: " + lastPacketCounter, Toast.LENGTH_LONG, GB.ERROR); + handleActivityFetchFinish(false); + return; + } + } + + /** + * Buffers the given activity summary data. If the total size is reached, + * it is converted to an object and saved in the database. + * @param value + */ + @Override + protected void bufferActivityData(byte[] value) { + buffer.write(value, 1, value.length - 1); // skip the counter + } + + private BaseActivitySummary parseSummary(ByteArrayOutputStream stream) { + BaseActivitySummary summary = new BaseActivitySummary(); + ByteBuffer buffer = ByteBuffer.wrap(stream.toByteArray()).order(ByteOrder.LITTLE_ENDIAN); +// summary.setVersion(BLETypeConversions.toUnsigned(buffer.getShort())); + buffer.getShort(); // version + int activityKind = ActivityKind.TYPE_UNKNOWN; + try { + int rawKind = BLETypeConversions.toUnsigned(buffer.getShort()); + BipActivityType activityType = BipActivityType.fromCode(rawKind); + activityKind = activityType.toActivityKind(); + } catch (Exception ex) { + LOG.error("Error mapping acivity kind: " + ex.getMessage(), ex); + } + summary.setActivityKind(activityKind); + + // FIXME: should honor timezone we were in at that time etc + long timestamp_start = BLETypeConversions.toUnsigned(buffer.getInt()) * 1000; + long timestamp_end = BLETypeConversions.toUnsigned(buffer.getInt()) * 1000; + + + // FIXME: should be done like this but seems to return crap when in DST + //summary.setStartTime(new Date(timestamp_start)); + //summary.setEndTime(new Date(timestamp_end)); + + // FIXME ... so do it like this + long duration = timestamp_end - timestamp_start; + summary.setStartTime(new Date(getLastStartTimestamp().getTimeInMillis())); + summary.setEndTime(new Date(getLastStartTimestamp().getTimeInMillis() + duration)); + + int baseLongitude = buffer.getInt(); + int baseLatitude = buffer.getInt(); + int baseAltitude = buffer.getInt(); + summary.setBaseLongitude(baseLongitude); + summary.setBaseLatitude(baseLatitude); + summary.setBaseAltitude(baseAltitude); +// summary.setBaseCoordinate(new GPSCoordinate(baseLatitude, baseLongitude, baseAltitude)); + +// summary.setDistanceMeters(Float.intBitsToFloat(buffer.getInt())); +// summary.setAscentMeters(Float.intBitsToFloat(buffer.getInt())); +// summary.setDescentMeters(Float.intBitsToFloat(buffer.getInt())); +// +// summary.setMinAltitude(Float.intBitsToFloat(buffer.getInt())); +// summary.setMaxAltitude(Float.intBitsToFloat(buffer.getInt())); +// summary.setMinLatitude(buffer.getInt()); +// summary.setMaxLatitude(buffer.getInt()); +// summary.setMinLongitude(buffer.getInt()); +// summary.setMaxLongitude(buffer.getInt()); +// +// summary.setSteps(BLETypeConversions.toUnsigned(buffer.getInt())); +// summary.setActiveTimeSeconds(BLETypeConversions.toUnsigned(buffer.getInt())); +// +// summary.setCaloriesBurnt(Float.intBitsToFloat(buffer.get())); +// summary.setMaxSpeed(Float.intBitsToFloat(buffer.get())); +// summary.setMinPace(Float.intBitsToFloat(buffer.get())); +// summary.setMaxPace(Float.intBitsToFloat(buffer.get())); +// summary.setTotalStride(Float.intBitsToFloat(buffer.get())); + + buffer.getInt(); // + buffer.getInt(); // + buffer.getInt(); // + +// summary.setTimeAscent(BLETypeConversions.toUnsigned(buffer.getInt())); +// buffer.getInt(); // +// summary.setTimeDescent(BLETypeConversions.toUnsigned(buffer.getInt())); +// buffer.getInt(); // +// summary.setTimeFlat(BLETypeConversions.toUnsigned(buffer.getInt())); +// +// summary.setAverageHR(BLETypeConversions.toUnsigned(buffer.getShort())); +// +// summary.setAveragePace(BLETypeConversions.toUnsigned(buffer.getShort())); +// summary.setAverageStride(BLETypeConversions.toUnsigned(buffer.getShort())); + + buffer.getShort(); // + + return summary; + } + + @Override + protected String getLastSyncTimeKey() { + return getDevice().getAddress() + "_" + "lastSportsActivityTimeMillis"; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/InitOperation.java similarity index 96% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/InitOperation.java index e1a5bd7ce..93204f441 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/InitOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/InitOperation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; @@ -41,7 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEOperation; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.util.GB; public class InitOperation extends AbstractBTLEOperation { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/UpdateFirmwareOperation.java similarity index 96% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/UpdateFirmwareOperation.java index 8647880e8..a711c1ee0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband2/operations/UpdateFirmwareOperation.java @@ -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. @@ -14,7 +14,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.operations; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; @@ -34,16 +34,14 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage; import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2Service; -import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2FWHelper; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceBusyAction; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetProgressAction; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.AbstractMiBand2Operation; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.AbstractMiBand2Operation; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.Mi2FirmwareInfo; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband2.MiBand2Support; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java new file mode 100644 index 000000000..2f5697fc0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3FirmwareInfo.java @@ -0,0 +1,86 @@ +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Davis + Mosenkovs + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + +public class MiBand3FirmwareInfo extends HuamiFirmwareInfo { + // this is the same as Mi Band 2 + private static final byte[] FW_HEADER = new byte[]{ + (byte) 0xa3, (byte) 0x68, (byte) 0x04, (byte) 0x3b, (byte) 0x02, (byte) 0xdb, + (byte) 0xc8, (byte) 0x58, (byte) 0xd0, (byte) 0x50, (byte) 0xfa, (byte) 0xe7, + (byte) 0x0c, (byte) 0x34, (byte) 0xf3, (byte) 0xe7, + }; + + private static final int FW_HEADER_OFFSET = 0x150; + + private static final byte FW_MAGIC = (byte) 0xf9; + private static final int FW_MAGIC_OFFSET = 0x17d; + + private static Map crcToVersion = new HashMap<>(); + + static { + // firmware + + // resources + } + + public MiBand3FirmwareInfo(byte[] bytes) { + super(bytes); + } + + @Override + protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { + if (ArrayUtils.startsWith(bytes, RES_HEADER)) { + if (bytes.length > 100000) { // don't know how to distinguish from Bip/Cor .res + return HuamiFirmwareType.INVALID; + } + return HuamiFirmwareType.RES; + } + if (ArrayUtils.equals(bytes, FW_HEADER, FW_HEADER_OFFSET) + && (bytes[FW_MAGIC_OFFSET] == FW_MAGIC)) { + // TODO: this is certainly not a correct validation, but it works for now + return HuamiFirmwareType.FIRMWARE; + } + return HuamiFirmwareType.INVALID; + } + + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return isHeaderValid() && device.getType() == DeviceType.MIBAND3; + } + + @Override + protected Map getCrcMap() { + return crcToVersion; + } + + @Override + protected String searchFirmwareVersion(byte[] fwbytes) { + // does not work for Mi Band 3 + return null; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java new file mode 100644 index 000000000..930977c9f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/miband3/MiBand3Support.java @@ -0,0 +1,34 @@ +/* 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 . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3FWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbip.AmazfitBipSupport; + +public class MiBand3Support extends AmazfitBipSupport { + + @Override + public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + return new MiBand3FWHelper(uri, context); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java index dd523f9f3..9e9bf4358 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/jyou/TeclastH30Support.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Sami Alaoui +/* Copyright (C) 2017-2018 Andreas Shimokawa, Sami Alaoui This file is part of Gadgetbridge. @@ -362,7 +362,7 @@ public class TeclastH30Support extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java index a823c5d6e..fc86275c7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewIoThread.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java index 3537efc64..8c29a7917 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewProtocol.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Daniele Gobbetti +/* Copyright (C) 2016-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java index 898a0b811..b63cdd629 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/liveview/LiveviewSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti +/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractInfo.java index c4fed4314..0c53d3b4a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java index 5a4028372..63579850b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1FirmwareInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java index 16d63fe6e..88a67cb46 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMi1SFirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java index cca5c9140..79a49e340 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/AbstractMiFirmwareInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java index dc25f9838..bd9e83062 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/BatteryInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java index 82fe63f60..76afe6a67 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CheckAuthenticationNeededAction.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java index 297b64eea..e57f678af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/CompositeMiFirmwareInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java index 090c97b5d..27dfed54f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/DeviceInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Sergey Trofimov This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java index e92eaa99a..bbd5f829a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1AFirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java index 236f55406..0e98b7a7d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1FirmwareInfo.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java index dd27d0016..b07006542 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java index c3018b4ec..0e8dca05a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW1.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java index 2fb9b348c..7869b29b8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/Mi1SFirmwareInfoFW2.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java index 94839f37a..877debd5f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/MiBandSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, atkyritsis, Carsten Pfeiffer, +/* Copyright (C) 2015-2018 Andreas Shimokawa, atkyritsis, Carsten Pfeiffer, Christian Fischer, Daniele Gobbetti, freezed-or-frozen, JohnnySun, Julien Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld @@ -761,7 +761,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { try { new FetchActivityOperation(this).perform(); } catch (IOException ex) { @@ -1056,9 +1056,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { if (value.length != 1) { LOG.error("Notifications should be 1 byte long."); LOG.info("RECEIVED DATA WITH LENGTH: " + value.length); - for (byte b : value) { - LOG.warn("DATA: " + String.format("0x%2x", b)); - } + LOG.warn("DATA: " + GB.hexdump(value, 0, value.length)); return; } switch (value[0]) { @@ -1087,9 +1085,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { LOG.info("Setting latency succeeded."); break; default: - for (byte b : value) { - LOG.warn("DATA: " + String.format("0x%2x", b)); - } + LOG.warn("DATA: " + GB.hexdump(value, 0, value.length)); } } @@ -1297,9 +1293,7 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport { if ((value.length - 2) % 6 != 0) { LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length); - for (byte b : value) { - LOG.warn("DATA: " + String.format("0x%4x", b)); - } + LOG.warn("DATA: " + GB.hexdump(value, 0, value.length)); } else { counter = (value[0] & 0xff) | ((value[1] & 0xff) << 8); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java index 32054d357..1e4e0cd04 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NoNotificationStrategy.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java index 896b349cd..40b2cbcff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/NotificationStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java index 36c42a956..dd88b69de 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/RealtimeSamplesSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java index 9c5a791f8..559fcbdab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/TestMi1AFirmwareInfo.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java index 30a4e1866..de27197d6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V1NotificationStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java index 865b30d2e..e9dc0a49d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/V2NotificationStrategy.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java index cec4e9a41..1ad53e1ff 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBand1Operation.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java index 4dacf9fb0..72ec8e8a2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/AbstractMiBandOperation.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java index 6aaded69c..52117d8d7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/FetchActivityOperation.java @@ -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. @@ -236,12 +236,12 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { // avoid too many notifications overloading the system if (progress - activityStruct.lastNotifiedProgress >= 8) { activityStruct.lastNotifiedProgress = progress; - GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), true, progress, getContext()); + GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, progress, getContext()); } if (activityStruct.isBlockFinished()) { sendAckDataTransfer(activityStruct.activityDataTimestampToAck, activityStruct.activityDataUntilNextHeader); - GB.updateTransferNotification("", false, 100, getContext()); + GB.updateTransferNotification(null, "", false, 100, getContext()); } } @@ -271,9 +271,10 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { // as we just did if (activityStruct.isFirstChunk() && dataUntilNextHeader != 0) { - GB.toast(getContext().getString(R.string.user_feedback_miband_activity_data_transfer, + + GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), getContext().getString(R.string.user_feedback_miband_activity_data_transfer, DateTimeUtils.formatDurationHoursMinutes((totalDataToRead / getBytesPerMinuteOfActivityData()), TimeUnit.MINUTES), - DateFormat.getDateTimeInstance().format(timestamp.getTime())), Toast.LENGTH_LONG, GB.INFO); + DateFormat.getDateTimeInstance().format(timestamp.getTime())), true, 0, getContext()); } LOG.info("total data to read: " + totalDataToRead + " len: " + (totalDataToRead / getBytesPerMinuteOfActivityData()) + " minute(s)"); LOG.info("data to read until next header: " + dataUntilNextHeader + " len: " + (dataUntilNextHeader / getBytesPerMinuteOfActivityData()) + " minute(s)"); @@ -326,7 +327,7 @@ public class FetchActivityOperation extends AbstractMiBand1Operation { TransactionBuilder builder = performInitialized("send stop sync data"); builder.write(getCharacteristic(MiBandService.UUID_CHARACTERISTIC_CONTROL_POINT), new byte[]{MiBandService.COMMAND_STOP_SYNC_DATA}); builder.queue(getQueue()); - GB.updateTransferNotification("Data transfer failed", false, 0, getContext()); + GB.updateTransferNotification(null,"Data transfer failed", false, 0, getContext()); handleActivityFetchFinish(); } catch (IOException e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/OperationStatus.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/OperationStatus.java index ad0f9f36e..4681d31bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/OperationStatus.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/OperationStatus.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java index d00e1e005..3fb097633 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband/operations/UpdateFirmwareOperation.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchSportsSummaryOperation.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchSportsSummaryOperation.java deleted file mode 100644 index 5ae6b2d83..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/miband2/operations/FetchSportsSummaryOperation.java +++ /dev/null @@ -1,148 +0,0 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer - - This file is part of Gadgetbridge. - - Gadgetbridge is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Gadgetbridge is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.operations; - -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCharacteristic; -import android.widget.Toast; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Calendar; -import java.util.GregorianCalendar; - -import nodomain.freeyourgadget.gadgetbridge.Logging; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import nodomain.freeyourgadget.gadgetbridge.service.devices.miband2.MiBand2Support; -import nodomain.freeyourgadget.gadgetbridge.util.GB; - -/** - * An operation that fetches activity data. For every fetch, a new operation must - * be created, i.e. an operation may not be reused for multiple fetches. - */ -public class FetchSportsSummaryOperation extends AbstractFetchOperation { - private static final Logger LOG = LoggerFactory.getLogger(FetchSportsSummaryOperation.class); - -// private List samples = new ArrayList<>(60*24); // 1day per default - - private byte lastPacketCounter; - - public FetchSportsSummaryOperation(MiBand2Support support) { - super(support); - } - - @Override - protected void startFetching(TransactionBuilder builder) { - GregorianCalendar sinceWhen = getLastSuccessfulSyncTime(); -// builder.write(characteristicFetch, BLETypeConversions.join(new byte[] { -// MiBand2Service.COMMAND_ACTIVITY_DATA_START_DATE, -// AmazfitBipService.COMMAND_ACTIVITY_DATA_TYPE_SPORTS_SUMMARIES}, -// getSupport().getTimeBytes(sinceWhen, TimeUnit.MINUTES))); -// builder.add(new WaitAction(1000)); // TODO: actually wait for the success-reply -// builder.notify(characteristicActivityData, true); -// builder.write(characteristicFetch, new byte[] { MiBand2Service.COMMAND_FETCH_DATA }); - } - - @Override - protected void handleActivityFetchFinish() { - LOG.info("Fetching activity data has finished round " + fetchCount); -// GregorianCalendar lastSyncTimestamp = saveSamples(); -// if (lastSyncTimestamp != null && needsAnotherFetch(lastSyncTimestamp)) { -// try { -// startFetching(); -// return; -// } catch (IOException ex) { -// LOG.error("Error starting another round of fetching activity data", ex); -// } -// } - - super.handleActivityFetchFinish(); - } - - @Override - public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { - LOG.warn("characteristic read: " + characteristic.getUuid() + ": " + Logging.formatBytes(characteristic.getValue())); - return super.onCharacteristicRead(gatt, characteristic, status); - } - - /** - * Method to handle the incoming activity data. - * There are two kind of messages we currently know: - * - the first one is 11 bytes long and contains metadata (how many bytes to expect, when the data starts, etc.) - * - the second one is 20 bytes long and contains the actual activity data - *

- * The first message type is parsed by this method, for every other length of the value param, bufferActivityData is called. - * - * @param value - */ - @Override - protected void handleActivityNotif(byte[] value) { - LOG.warn("sports data: " + Logging.formatBytes(value)); - - if (!isOperationRunning()) { - LOG.error("ignoring activity data notification because operation is not running. Data length: " + value.length); - getSupport().logMessageContent(value); - return; - } - - if ((value.length % 4) == 1) { - if ((byte) (lastPacketCounter + 1) == value[0] ) { - lastPacketCounter++; - bufferActivityData(value); - } else { - GB.toast("Error fetching activity data, invalid package counter: " + value[0], Toast.LENGTH_LONG, GB.ERROR); - handleActivityFetchFinish(); - return; - } - } else { - GB.toast("Error fetching activity data, unexpected package length: " + value.length, Toast.LENGTH_LONG, GB.ERROR); - LOG.warn("Unexpected activity data: " + Logging.formatBytes(value)); - } - } - - /** - * Creates samples from the given 17-length array - * @param value - */ - @Override - protected void bufferActivityData(byte[] value) { - // TODO: implement -// int len = value.length; -// -// if (len % 4 != 1) { -// throw new AssertionError("Unexpected activity array size: " + len); -// } -// -// for (int i = 1; i < len; i+=4) { -// } - } - - @Override - protected String getLastSyncTimeKey() { - return getDevice().getAddress() + "_" + "lastSportsSyncTimeMillis"; - } - - - protected GregorianCalendar getLastSuccessfulSyncTime() { - // FIXME: remove this! - GregorianCalendar calendar = BLETypeConversions.createCalendar(); - calendar.add(Calendar.DAY_OF_MONTH, -1); - return calendar; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java index 55b1a036c..d09a2a3cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/no1f1/No1F1Support.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Daniele Gobbetti, protomors +/* Copyright (C) 2017-2018 Andreas Shimokawa, Daniele Gobbetti, protomors This file is part of Gadgetbridge. @@ -299,7 +299,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { sendFetchCommand(No1F1Constants.CMD_FETCH_STEPS); } @@ -582,7 +582,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { LOG.info("CRC received: " + (data[2] & 0xff) + ", calculated: " + (crc & 0xff)); if (data[2] != crc) { GB.toast(getContext(), "Incorrect CRC. Try fetching data again.", Toast.LENGTH_LONG, GB.ERROR); - GB.updateTransferNotification("Data transfer failed", false, 0, getContext()); + GB.updateTransferNotification(null,"Data transfer failed", false, 0, getContext()); if (getDevice().isBusy()) { getDevice().unsetBusyTask(); getDevice().sendDeviceUpdateIntent(getContext()); @@ -612,7 +612,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } else if (data[0] == No1F1Constants.CMD_FETCH_SLEEP) { sendFetchCommand(No1F1Constants.CMD_FETCH_HEARTRATE); } else { - GB.updateTransferNotification("", false, 100, getContext()); + GB.updateTransferNotification(null,"", false, 100, getContext()); if (getDevice().isBusy()) { getDevice().unsetBusyTask(); getDevice().sendDeviceUpdateIntent(getContext()); @@ -620,7 +620,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { } } catch (Exception ex) { GB.toast(getContext(), "Error saving activity data: " + ex.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); - GB.updateTransferNotification("Data transfer failed", false, 0, getContext()); + GB.updateTransferNotification(null,"Data transfer failed", false, 0, getContext()); } } } else { @@ -657,7 +657,7 @@ public class No1F1Support extends AbstractBTLEDeviceSupport { firstTimestamp = sample.getTimestamp(); int progress = startProgress + 33 * (sample.getTimestamp() - firstTimestamp) / ((int) (Calendar.getInstance().getTimeInMillis() / 1000L) - firstTimestamp); - GB.updateTransferNotification(getContext().getString(R.string.busy_task_fetch_activity_data), true, progress, getContext()); + GB.updateTransferNotification(null, getContext().getString(R.string.busy_task_fetch_activity_data), true, progress, getContext()); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java index f43407c35..f385908aa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandler.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java index 74d751158..033205900 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerGBPebble.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java index c4d8b692d..14c3e8cf4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerHealthify.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java index aa776388f..4bf12143e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMarioTime.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java index 3b824a640..8cb570b40 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMisfit.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java index b97ad3563..3934558c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerMorpheuz.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java index adbf89a2f..0f81b14cf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerObsidian.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java index cd60ccff1..799312144 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerPebStyle.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti, Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti, Uwe Hermann This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSimplyLight.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSimplyLight.java new file mode 100644 index 000000000..f6c31da15 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSimplyLight.java @@ -0,0 +1,139 @@ +/* Copyright (C) 2018 Sergio Lopez + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble; + +import android.util.Pair; +import android.widget.Toast; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; +import nodomain.freeyourgadget.gadgetbridge.model.Weather; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +class AppMessageHandlerSimplyLight extends AppMessageHandler { + private static final int CLEAR = 0; + private static final int CLOUDY = 1; + private static final int FOG = 2; + private static final int LIGHT_RAIN = 3; + private static final int RAIN = 4; + private static final int THUNDERSTORM = 5; + private static final int SNOW = 6; + private static final int HAIL = 7; + private static final int WIND = 8; + private static final int EXTREME_WIND = 9; + private static final int TORNADO = 10; + private static final int HURRICANE = 11; + private static final int EXTREME_COLD = 12; + private static final int EXTREME_HEAT = 13; + private static final int SNOW_THUNDERSTORM = 14; + + private Integer KEY_TEMPERATURE; + private Integer KEY_CONDITION; + private Integer KEY_ERR; + + AppMessageHandlerSimplyLight(UUID uuid, PebbleProtocol pebbleProtocol) { + super(uuid, pebbleProtocol); + + try { + JSONObject appKeys = getAppKeys(); + KEY_TEMPERATURE = appKeys.getInt("temperature"); + KEY_CONDITION = appKeys.getInt("condition"); + KEY_ERR = appKeys.getInt("err"); + } catch (JSONException e) { + GB.toast("There was an error accessing the Simply Light watchface configuration.", Toast.LENGTH_LONG, GB.ERROR); + } catch (IOException ignore) { + } + } + + +private int getConditionForConditionCode(int conditionCode) { + if (conditionCode == 800 || conditionCode == 951) { + return CLEAR; + } else if (conditionCode > 800 && conditionCode < 900) { + return CLOUDY; + } else if (conditionCode >= 700 && conditionCode < 800) { + return FOG; + } else if (conditionCode >= 300 && conditionCode < 400) { + return LIGHT_RAIN; + } else if (conditionCode >= 500 && conditionCode < 600) { + return RAIN; + } else if (conditionCode >= 200 && conditionCode < 300) { + return THUNDERSTORM; + } else if (conditionCode >= 600 && conditionCode < 700) { + return SNOW; + } else if (conditionCode == 906) { + return HAIL; + } else if (conditionCode >= 907 && conditionCode < 957) { + return WIND; + } else if (conditionCode == 905 || (conditionCode >= 957 && conditionCode < 900)) { + return EXTREME_WIND; + } else if (conditionCode == 900) { + return TORNADO; + } else if (conditionCode == 901 || conditionCode == 902 || conditionCode == 962) { + return HURRICANE; + } else if (conditionCode == 903) { + return EXTREME_COLD; + } else if (conditionCode == 904) { + return EXTREME_HEAT; + } + + return 0; + } + + private byte[] encodeSimplyLightWeatherMessage(WeatherSpec weatherSpec) { + if (weatherSpec == null) { + return null; + } + + ArrayList> pairs = new ArrayList<>(2); + pairs.add(new Pair<>(KEY_TEMPERATURE, (Object) (weatherSpec.currentTemp - 273))); + pairs.add(new Pair<>(KEY_CONDITION, (Object) (getConditionForConditionCode(weatherSpec.currentConditionCode)))); + pairs.add(new Pair<>(KEY_ERR, (Object) 0)); + byte[] weatherMessage = mPebbleProtocol.encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, mUUID, pairs, null); + + ByteBuffer buf = ByteBuffer.allocate(weatherMessage.length); + + buf.put(weatherMessage); + + return buf.array(); + } + + @Override + public GBDeviceEvent[] onAppStart() { + WeatherSpec weatherSpec = Weather.getInstance().getWeatherSpec(); + if (weatherSpec == null) { + return new GBDeviceEvent[]{null}; + } + GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes(); + sendBytes.encodedBytes = encodeSimplyLightWeatherMessage(weatherSpec); + return new GBDeviceEvent[]{sendBytes}; + } + + @Override + public byte[] encodeUpdateWeather(WeatherSpec weatherSpec) { + return encodeSimplyLightWeatherMessage(weatherSpec); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java index 1c5fd489f..fb203c76b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerSquare.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java index b31241ab4..6a8106baf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTimeStylePebble.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java index e00b2c5fd..873585b0d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerTrekVolle.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java index 2c48ce707..67fd562bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/AppMessageHandlerZalewszczak.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java index a5fdd1d44..d1a616ac2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSession.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java index 35ece6461..b9c483f50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionAnalytics.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java index 82146827a..2ff8e58c5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthHR.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java index ac1a1d811..60dcde346 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthOverlayData.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java index 882302d0a..2c575b9b0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSleep.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, +/* Copyright (C) 2016-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java index 2c033eff4..f194201a4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionHealthSteps.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, +/* Copyright (C) 2016-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java index 89fbf288b..34e0ec902 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/DatalogSessionPebbleHealth.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 453e09c51..d359391e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -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, Uwe Hermann This file is part of Gadgetbridge. @@ -42,6 +42,7 @@ import java.net.InetAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -110,7 +111,7 @@ class PebbleIoThread extends GBDeviceIoThread { public static void sendAppMessage(GBDeviceEventAppMessage message) { final String jsEvent; try { - WebViewSingleton.checkAppRunning(message.appUUID); + WebViewSingleton.getInstance().checkAppRunning(message.appUUID); } catch (IllegalStateException ex) { LOG.warn("Unable to send app message: " + message, ex); return; @@ -124,9 +125,9 @@ class PebbleIoThread extends GBDeviceIoThread { jsEvent = "appmessage"; } - final String appMessage = PebbleUtils.parseIncomingAppMessage(message.message, message.appUUID); + final String appMessage = PebbleUtils.parseIncomingAppMessage(message.message, message.appUUID, message.id); LOG.debug("to WEBVIEW: event: " + jsEvent + " message: " + appMessage); - WebViewSingleton.invokeWebview(new WebViewSingleton.WebViewRunnable() { + WebViewSingleton.getInstance().invokeWebview(new WebViewSingleton.WebViewRunnable() { @Override public void invoke(WebView webView) { webView.evaluateJavascript("if (typeof Pebble == 'object') Pebble.evaluate('" + jsEvent + "',[" + appMessage + "]);", new ValueCallback() { @@ -192,7 +193,11 @@ class PebbleIoThread extends GBDeviceIoThread { for (ParcelUuid uuid : uuids) { LOG.info("found service UUID " + uuid); } - mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); + + final UUID UuidSDP = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); + mBtSocket = btDevice.createRfcommSocketToServiceRecord(UuidSDP); + + //mBtSocket = btDevice.createRfcommSocketToServiceRecord(uuids[0].getUuid()); mBtSocket.connect(); mInStream = mBtSocket.getInputStream(); mOutStream = mBtSocket.getOutputStream(); @@ -424,7 +429,7 @@ class PebbleIoThread extends GBDeviceIoThread { } if (GBApplication.getGBPrefs().isBackgroundJsEnabled()) { - WebViewSingleton.disposeWebView(); + WebViewSingleton.getInstance().disposeWebView(); } gbDevice.sendDeviceUpdateIntent(getContext()); @@ -553,9 +558,9 @@ class PebbleIoThread extends GBDeviceIoThread { LOG.info("got GBDeviceEventAppManagement START event for uuid: " + appMgmt.uuid); if (GBApplication.getGBPrefs().isBackgroundJsEnabled()) { if (mPebbleProtocol.hasAppMessageHandler(appMgmt.uuid)) { - WebViewSingleton.stopJavascriptInterface(); + WebViewSingleton.getInstance().stopJavascriptInterface(); } else { - WebViewSingleton.runJavascriptInterface(gbDevice, appMgmt.uuid); + WebViewSingleton.getInstance().runJavascriptInterface(gbDevice, appMgmt.uuid); } } break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java index 3119a6a55..8b0d85fc1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleKitSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa +/* Copyright (C) 2017-2018 Andreas Shimokawa This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index f061a7b2e..7da9430e3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -1,6 +1,6 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Frank Slezak, Julien Pivotto, Kevin Richter, Steffen Liebergeld, - Uwe Hermann +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Frank Slezak, Julien Pivotto, Kevin Richter, Sergio Lopez, Steffen + Liebergeld, Uwe Hermann This file is part of Gadgetbridge. @@ -394,6 +394,7 @@ public class PebbleProtocol extends GBDeviceProtocol { private static final UUID UUID_ZALEWSZCZAK_FANCY = UUID.fromString("014e17bf-5878-4781-8be1-8ef998cee1ba"); private static final UUID UUID_ZALEWSZCZAK_TALLY = UUID.fromString("abb51965-52e2-440a-b93c-843eeacb697d"); private static final UUID UUID_OBSIDIAN = UUID.fromString("ef42caba-0c65-4879-ab23-edd2bde68824"); + private static final UUID UUID_SIMPLY_LIGHT = UUID.fromString("04a6e68a-42d6-4738-87b2-1c80a994dee4"); private static final UUID UUID_ZERO = new UUID(0, 0); @@ -419,6 +420,7 @@ public class PebbleProtocol extends GBDeviceProtocol { mAppMessageHandlers.put(UUID_ZALEWSZCZAK_TALLY, new AppMessageHandlerZalewszczak(UUID_ZALEWSZCZAK_TALLY, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_OBSIDIAN, new AppMessageHandlerObsidian(UUID_OBSIDIAN, PebbleProtocol.this)); mAppMessageHandlers.put(UUID_GBPEBBLE, new AppMessageHandlerGBPebble(UUID_GBPEBBLE, PebbleProtocol.this)); + mAppMessageHandlers.put(UUID_SIMPLY_LIGHT, new AppMessageHandlerSimplyLight(UUID_SIMPLY_LIGHT, PebbleProtocol.this)); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java index 10b24e86a..c3d4f30cc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Kasha, Steffen Liebergeld This file is part of Gadgetbridge. @@ -23,6 +23,8 @@ import android.util.Pair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Iterator; @@ -44,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; public class PebbleSupport extends AbstractSerialDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(PebbleSupport.class); @Override public boolean connect() { @@ -113,12 +116,14 @@ public class PebbleSupport extends AbstractSerialDeviceSupport { object = byteArray; } else if (object instanceof Boolean) { object = (short) (((Boolean) object) ? 1 : 0); + } else if (object instanceof Double) { + object = ((Double) object).intValue(); } pairs.add(new Pair<>(Integer.parseInt(keyStr), object)); } getDeviceIOThread().write(((PebbleProtocol) getDeviceProtocol()).encodeApplicationMessagePush(PebbleProtocol.ENDPOINT_APPLICATIONMESSAGE, uuid, pairs, id)); } catch (JSONException e) { - e.printStackTrace(); + LOG.error("Error while parsing JSON", e); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java index cea21d198..e8df7511f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTClient.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa +/* Copyright (C) 2016-2018 Andreas Shimokawa This file is part of Gadgetbridge. @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Method; import java.util.UUID; +import java.util.concurrent.CountDownLatch; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -47,7 +48,13 @@ class PebbleGATTClient extends BluetoothGattCallback { private static final UUID CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.fromString("00000005-328E-0FBB-C642-1AA6699BDADA"); private static final UUID CHARACTERISTIC_CONFIGURATION_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); - private final BluetoothDevice mBtDevice; + //PPoGATT service (Pebble side) + private static final UUID PPOGATT_SERVICE_UUID = UUID.fromString("30000003-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID PPOGATT_CHARACTERISTIC_READ = UUID.fromString("30000004-328E-0FBB-C642-1AA6699BDADA"); + private static final UUID PPOGATT_CHARACTERISTIC_WRITE = UUID.fromString("30000006-328E-0FBB-C642-1AA6699BDADA"); + + private BluetoothGattCharacteristic writeCharacteristics; + private final Context mContext; private final PebbleLESupport mPebbleLESupport; @@ -56,11 +63,12 @@ class PebbleGATTClient extends BluetoothGattCallback { private boolean removeBond = false; private BluetoothGatt mBluetoothGatt; + private CountDownLatch mWaitWriteCompleteLatch; + PebbleGATTClient(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) { mContext = context; - mBtDevice = btDevice; mPebbleLESupport = pebbleLESupport; - connectToPebble(mBtDevice); + connectToPebble(btDevice); } public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { @@ -72,6 +80,8 @@ class PebbleGATTClient extends BluetoothGattCallback { int newMTU = characteristic.getIntValue(FORMAT_UINT16, 0); LOG.info("Pebble requested MTU: " + newMTU); mPebbleLESupport.setMTU(newMTU); + } else if (characteristic.getUuid().equals(PPOGATT_CHARACTERISTIC_READ)) { + mPebbleLESupport.handlePPoGATTPacket(characteristic.getValue().clone()); } else { LOG.info("onCharacteristicChanged()" + characteristic.getUuid().toString() + " " + GB.hexdump(characteristic.getValue(), 0, -1)); } @@ -112,9 +122,16 @@ class PebbleGATTClient extends BluetoothGattCallback { if (!mPebbleLESupport.isExpectedDevice(gatt.getDevice())) { return; } - - LOG.info("onCharacteristicWrite() " + characteristic.getUuid()); - if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) { + if (characteristic.getUuid().equals(PPOGATT_CHARACTERISTIC_WRITE)) { + if (status != BluetoothGatt.GATT_SUCCESS) { + LOG.error("something went wrong when writing to PPoGATT characteristics"); + } + if (mWaitWriteCompleteLatch != null) { + mWaitWriteCompleteLatch.countDown(); + } else { + LOG.warn("mWaitWriteCompleteLatch is null!"); + } + } else if (characteristic.getUuid().equals(PAIRING_TRIGGER_CHARACTERISTIC) || characteristic.getUuid().equals(CONNECTIVITY_CHARACTERISTIC)) { //mBtDevice.createBond(); // did not work when last tried if (oldPebble) { @@ -143,6 +160,12 @@ class PebbleGATTClient extends BluetoothGattCallback { } else if (CHARACTERISTICUUID.equals(CONNECTIVITY_CHARACTERISTIC)) { subscribeToMTU(gatt); } else if (CHARACTERISTICUUID.equals(MTU_CHARACTERISTIC)) { + if (mPebbleLESupport.clientOnly) { + subscribeToPPoGATT(gatt); + } else { + setMTU(gatt); + } + } else if (CHARACTERISTICUUID.equals(PPOGATT_CHARACTERISTIC_READ)) { setMTU(gatt); } } @@ -171,7 +194,11 @@ class PebbleGATTClient extends BluetoothGattCallback { // 2 - always 0 // 3 - unknown, set on kitkat (seems to help to get a "better" pairing) // 4 - unknown, set on some phones - characteristic.setValue(new byte[]{9}); + if (mPebbleLESupport.clientOnly) { + characteristic.setValue(new byte[]{0x11}); // needed in clientOnly mode (TODO: try 0x19) + } else { + characteristic.setValue(new byte[]{0x09}); // I just keep this, because it worked + } gatt.writeCharacteristic(characteristic); } else { LOG.info("This seems to be some <4.0 FW Pebble, reading pairing trigger"); @@ -239,6 +266,32 @@ class PebbleGATTClient extends BluetoothGattCallback { gatt.writeCharacteristic(characteristic); } + private void subscribeToPPoGATT(BluetoothGatt gatt) { + LOG.info("subscribing to PPoGATT read characteristic"); + BluetoothGattDescriptor descriptor = gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_READ).getDescriptor(CHARACTERISTIC_CONFIGURATION_DESCRIPTOR); + descriptor.setValue(new byte[]{1, 0}); + gatt.writeDescriptor(descriptor); + gatt.setCharacteristicNotification(gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_READ), true); + writeCharacteristics = gatt.getService(PPOGATT_SERVICE_UUID).getCharacteristic(PPOGATT_CHARACTERISTIC_WRITE); + } + + synchronized void sendDataToPebble(byte[] data) { + mWaitWriteCompleteLatch = new CountDownLatch(1); + writeCharacteristics.setValue(data.clone()); + + boolean success = mBluetoothGatt.writeCharacteristic(writeCharacteristics); + if (!success) { + LOG.error("could not send data to pebble (error writing characteristic)"); + } else { + try { + mWaitWriteCompleteLatch.await(); + } catch (InterruptedException e) { + LOG.warn("interrupted while waiting for write complete latch"); + } + } + mWaitWriteCompleteLatch = null; + } + public void close() { if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java index 9787c4689..6e3299d17 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleGATTServer.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti, Uwe Hermann +/* Copyright (C) 2016-2018 Andreas Shimokawa, Daniele Gobbetti, Uwe Hermann This file is part of Gadgetbridge. @@ -30,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; +import java.util.concurrent.CountDownLatch; class PebbleGATTServer extends BluetoothGattServerCallback { private static final Logger LOG = LoggerFactory.getLogger(PebbleGATTServer.class); @@ -43,6 +44,7 @@ class PebbleGATTServer extends BluetoothGattServerCallback { private Context mContext; private BluetoothGattServer mBluetoothGattServer; private BluetoothGattCharacteristic writeCharacteristics; + private CountDownLatch mWaitWriteCompleteLatch; PebbleGATTServer(PebbleLESupport pebbleLESupport, Context context, BluetoothDevice btDevice) { mContext = context; @@ -52,7 +54,9 @@ class PebbleGATTServer extends BluetoothGattServerCallback { boolean initialize() { BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE); - + if (bluetoothManager == null) { + return false; + } mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this); if (mBluetoothGattServer == null) { return false; @@ -71,16 +75,20 @@ class PebbleGATTServer extends BluetoothGattServerCallback { } synchronized void sendDataToPebble(byte[] data) { - //LOG.info("send data to pebble " + GB.hexdump(data, 0, -1)); + mWaitWriteCompleteLatch = new CountDownLatch(1); writeCharacteristics.setValue(data.clone()); - mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); - } - - synchronized private void sendAckToPebble(int serial) { - writeCharacteristics.setValue(new byte[]{(byte) (((serial << 3) | 1) & 0xff)}); - - mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); + boolean success = mBluetoothGattServer.notifyCharacteristicChanged(mBtDevice, writeCharacteristics, false); + if (!success) { + LOG.error("could not send data to pebble (error writing characteristic)"); + } else { + try { + mWaitWriteCompleteLatch.await(); + } catch (InterruptedException e) { + LOG.warn("interrupted while waiting for write complete latch"); + } + } + mWaitWriteCompleteLatch = null; } public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { @@ -110,39 +118,7 @@ class PebbleGATTServer extends BluetoothGattServerCallback { LOG.warn("unexpected write request"); return; } - if (!mPebbleLESupport.mIsConnected) { - mPebbleLESupport.mIsConnected = true; - synchronized (mPebbleLESupport) { - mPebbleLESupport.notify(); - } - } - //LOG.info("write request: offset = " + offset + " value = " + GB.hexdump(value, 0, -1)); - int header = value[0] & 0xff; - int command = header & 7; - int serial = header >> 3; - if (command == 0x01) { - LOG.info("got ACK for serial = " + serial); - if (mPebbleLESupport.mPPAck != null) { - mPebbleLESupport.mPPAck.countDown(); - } else { - LOG.warn("mPPAck countdownlatch is not present but it probably should"); - } - } - if (command == 0x02) { // some request? - LOG.info("got command 0x02"); - if (value.length > 1) { - sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we don't know what that means - mPebbleLESupport.createPipedInputReader(); // FIXME: maybe not here - } else { - sendDataToPebble(new byte[]{0x03}); // no we don't know what that means - } - } else if (command == 0) { // normal package - LOG.info("got PPoGATT package serial = " + serial + " sending ACK"); - - sendAckToPebble(serial); - - mPebbleLESupport.writeToPipedOutputStream(value, 1, value.length - 1); - } + mPebbleLESupport.handlePPoGATTPacket(value); } public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { @@ -194,7 +170,19 @@ class PebbleGATTServer extends BluetoothGattServerCallback { } public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) { - //LOG.info("onNotificationSent() status = " + status + " to device " + mmBtDevice.getAddress()); + + if (!mPebbleLESupport.isExpectedDevice(bluetoothDevice)) { + return; + } + if (status != BluetoothGatt.GATT_SUCCESS) { + LOG.error("something went wrong when writing to PPoGATT characteristics"); + } + if (mWaitWriteCompleteLatch != null) { + mWaitWriteCompleteLatch.countDown(); + } else { + LOG.warn("mWaitWriteCompleteLatch is null!"); + } +// LOG.info("onNotificationSent() status = " + status + " to device " + bluetoothDevice.getAddress()); } void close() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java index 4e0b64ed0..e4804e63d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/ble/PebbleLESupport.java @@ -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. @@ -18,6 +18,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,7 +27,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import java.util.concurrent.CountDownLatch; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -39,8 +40,10 @@ public class PebbleLESupport { private PipedOutputStream mPipedOutputStream; private int mMTU = 20; private int mMTULimit = Integer.MAX_VALUE; - boolean mIsConnected = false; - CountDownLatch mPPAck; + public boolean clientOnly = false; // currently experimental, and only possible for Pebble 2 + private boolean mIsConnected = false; + private HandlerThread mWriteHandlerThread; + private Handler mWriteHandler; public PebbleLESupport(Context context, final BluetoothDevice btDevice, PipedInputStream pipedInputStream, PipedOutputStream pipedOutputStream) throws IOException { mBtDevice = btDevice; @@ -52,12 +55,21 @@ public class PebbleLESupport { } catch (IOException e) { LOG.warn("could not connect input stream"); } + + mWriteHandlerThread = new HandlerThread("write handler thread"); + mWriteHandlerThread.start(); + mWriteHandler = new Handler(mWriteHandlerThread.getLooper()); + mMTULimit = GBApplication.getPrefs().getInt("pebble_mtu_limit", 512); mMTULimit = Math.max(mMTULimit, 20); mMTULimit = Math.min(mMTULimit, 512); - mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); - if (mPebbleGATTServer.initialize()) { + clientOnly = GBApplication.getPrefs().getBoolean("pebble_gatt_clientonly", false); + + if (!clientOnly) { + mPebbleGATTServer = new PebbleGATTServer(this, context, mBtDevice); + } + if (clientOnly || mPebbleGATTServer.initialize()) { mPebbleGATTClient = new PebbleGATTClient(this, context, mBtDevice); try { synchronized (this) { @@ -73,11 +85,11 @@ public class PebbleLESupport { throw new IOException("connection failed"); } - void writeToPipedOutputStream(byte[] value, int offset, int count) { + private void writeToPipedOutputStream(byte[] value, int offset, int count) { try { mPipedOutputStream.write(value, offset, count); } catch (IOException e) { - LOG.warn("error writing to output stream"); + LOG.warn("error writing to output stream", e); } } @@ -99,9 +111,12 @@ public class PebbleLESupport { mPipedOutputStream.close(); } catch (IOException ignore) { } + if (mWriteHandlerThread != null) { + mWriteHandlerThread.quit(); + } } - synchronized void createPipedInputReader() { + private synchronized void createPipedInputReader() { if (mPipeReader == null) { mPipeReader = new PipeReader(); } @@ -126,6 +141,60 @@ public class PebbleLESupport { mMTU = Math.min(mtu, mMTULimit); } + public void handlePPoGATTPacket(byte[] value) { + if (!mIsConnected) { + mIsConnected = true; + synchronized (this) { + this.notify(); + } + } + //LOG.info("write request: offset = " + offset + " value = " + GB.hexdump(value, 0, -1)); + int header = value[0] & 0xff; + int command = header & 7; + int serial = header >> 3; + if (command == 0x01) { + LOG.info("got ACK for serial = " + serial); + } + if (command == 0x02) { // some request? + LOG.info("got command 0x02"); + if (value.length > 1) { + sendDataToPebble(new byte[]{0x03, 0x19, 0x19}); // no we don't know what that means + createPipedInputReader(); // FIXME: maybe not here + } else { + sendDataToPebble(new byte[]{0x03}); // no we don't know what that means + } + } else if (command == 0) { // normal package + LOG.info("got PPoGATT package serial = " + serial + " sending ACK"); + + sendAckToPebble(serial); + + writeToPipedOutputStream(value, 1, value.length - 1); + } + } + + private void sendAckToPebble(int serial) { + sendDataToPebble(new byte[]{(byte) (((serial << 3) | 1) & 0xff)}); + } + + private synchronized void sendDataToPebble(final byte[] bytes) { + if (mPebbleGATTServer != null) { + mWriteHandler.post(new Runnable() { + @Override + public void run() { + mPebbleGATTServer.sendDataToPebble(bytes); + } + }); + } else { + // For now only in experimental client only code + mWriteHandler.post(new Runnable() { + @Override + public void run() { + mPebbleGATTClient.sendDataToPebble(bytes); + } + }); + } + } + private class PipeReader extends Thread { int mmSequence = 0; @@ -153,21 +222,16 @@ public class PebbleLESupport { int payloadToSend = bytesRead + 4; int srcPos = 0; - mPPAck = new CountDownLatch(1); while (payloadToSend > 0) { int chunkSize = (payloadToSend < (mMTU - 4)) ? payloadToSend : mMTU - 4; byte[] outBuf = new byte[chunkSize + 1]; outBuf[0] = (byte) ((mmSequence++ << 3) & 0xff); System.arraycopy(buf, srcPos, outBuf, 1, chunkSize); - mPebbleGATTServer.sendDataToPebble(outBuf); + sendDataToPebble(outBuf); srcPos += chunkSize; payloadToSend -= chunkSize; } - - mPPAck.await(); - mPPAck = null; - - } catch (IOException | InterruptedException e) { + } catch (IOException e) { LOG.info(e.getMessage()); Thread.currentThread().interrupt(); break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/CurrentPosition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/CurrentPosition.java index 89efb96d4..fc125b003 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/CurrentPosition.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/CurrentPosition.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017-2018 Andreas Shimokawa, Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBChromeClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBChromeClient.java index 7981447a3..55294501b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBChromeClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBChromeClient.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017-2018 Daniele Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBWebClient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBWebClient.java index ca0ecaf44..96a375823 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBWebClient.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/GBWebClient.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. @@ -31,6 +32,7 @@ import android.webkit.WebViewClient; import net.e175.klaus.solarpositioning.DeltaT; import net.e175.klaus.solarpositioning.SPA; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; @@ -40,21 +42,16 @@ import java.io.ByteArrayInputStream; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.CountDownLatch; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.Weather; - -import static nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton.internetHelper; -import static nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton.internetHelperBound; -import static nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton.internetHelperListener; -import static nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton.internetResponse; -import static nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton.latch; +import nodomain.freeyourgadget.gadgetbridge.util.WebViewSingleton; public class GBWebClient extends WebViewClient { private String[] AllowedDomains = new String[]{ "openweathermap.org", //for weather :) + "rawgit.com", //for trekvolle "tagesschau.de" //for internal watchapp tests }; private static final Logger LOG = LoggerFactory.getLogger(GBWebClient.class); @@ -82,27 +79,29 @@ public class GBWebClient extends WebViewClient { private WebResourceResponse mimicReply(Uri requestedUri) { - if (requestedUri.getHost() != null && (org.apache.commons.lang3.StringUtils.indexOfAny(requestedUri.getHost(), AllowedDomains) != -1)) { - if (internetHelperBound) { + if (requestedUri.getHost() != null && (StringUtils.indexOfAny(requestedUri.getHost(), AllowedDomains) != -1)) { + if (GBApplication.getGBPrefs().isBackgroundJsEnabled() && WebViewSingleton.getInstance().internetHelperBound) { LOG.debug("WEBVIEW forwarding request to the internet helper"); Bundle bundle = new Bundle(); bundle.putString("URL", requestedUri.toString()); Message webRequest = Message.obtain(); - webRequest.replyTo = internetHelperListener; webRequest.setData(bundle); try { - latch = new CountDownLatch(1); //the messenger should run on a single thread, hence we don't need to be worried about concurrency. This approach however is certainly not ideal. - internetHelper.send(webRequest); - latch.await(); - return internetResponse; - + return WebViewSingleton.getInstance().send(webRequest); } catch (RemoteException | InterruptedException e) { LOG.warn("Error downloading data from " + requestedUri, e); } } else { - LOG.debug("WEBVIEW request to openweathermap.org detected of type: " + requestedUri.getPath() + " params: " + requestedUri.getQuery()); - return mimicOpenWeatherMapResponse(requestedUri.getPath(), requestedUri.getQueryParameter("units")); + if (StringUtils.endsWith(requestedUri.getHost(), "openweathermap.org")){ + LOG.debug("WEBVIEW request to openweathermap.org detected of type: " + requestedUri.getPath() + " params: " + requestedUri.getQuery()); + return mimicOpenWeatherMapResponse(requestedUri.getPath(), requestedUri.getQueryParameter("units")); + } else if (StringUtils.endsWith(requestedUri.getHost(), "rawgit.com")) { + LOG.debug("WEBVIEW request to rawgit.com detected of type: " + requestedUri.getPath() + " params: " + requestedUri.getQuery()); + return mimicRawGitResponse(requestedUri.getPath()); + } else { + LOG.debug("WEBVIEW request to allowed domain detected but not intercepted: " + requestedUri.toString()); + } } } else { LOG.debug("WEBVIEW request:" + requestedUri.toString() + " not intercepted"); @@ -130,7 +129,24 @@ public class GBWebClient extends WebViewClient { return true; } - private static WebResourceResponse mimicOpenWeatherMapResponse(String type, String units) { + private WebResourceResponse mimicRawGitResponse(String path) { + if("/aHcVolle/TrekVolle/master/online.html".equals(path)) { //TrekVolle online check + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Map headers = new HashMap<>(); + headers.put("Access-Control-Allow-Origin", "*"); + return new WebResourceResponse("text/html", "utf-8", 200, "OK", + headers, + new ByteArrayInputStream("1".toString().getBytes()) + ); + } else { + return new WebResourceResponse("text/html", "utf-8", new ByteArrayInputStream("1".toString().getBytes())); + } + } + + return null; + } + + private WebResourceResponse mimicOpenWeatherMapResponse(String type, String units) { if (Weather.getInstance() == null) { LOG.warn("WEBVIEW - Weather instance is null, cannot update weather"); @@ -145,6 +161,8 @@ public class GBWebClient extends WebViewClient { JSONObject main = resp.getJSONObject("main"); convertTemps(main, units); //caller might want different units + JSONObject wind = resp.getJSONObject("wind"); + convertSpeeds(wind, units); resp.put("cod", 200); resp.put("coord", coordObject(currentPosition)); @@ -200,6 +218,14 @@ public class GBWebClient extends WebViewClient { return sys; } + private static void convertSpeeds(JSONObject wind, String units) throws JSONException { + if ("metric".equals(units)) { + wind.put("speed", (wind.getDouble("speed") * 3.6f) ); + } else if ("imperial".equals(units)) { //it's 2018... this is so sad + wind.put("speed", (wind.getDouble("speed") * 2.237f) ); + } + } + private static void convertTemps(JSONObject main, String units) throws JSONException { if ("metric".equals(units)) { main.put("temp", (int) main.get("temp") - 273); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/JSInterface.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/JSInterface.java index e50606dbe..81eb49654 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/JSInterface.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/webview/JSInterface.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Daniele Gobbetti +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java index 17e02276a..caebf7a1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vibratissimo/VibratissimoSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer This file is part of Gadgetbridge. @@ -206,7 +206,7 @@ public class VibratissimoSupport extends AbstractBTLEDeviceSupport { } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java new file mode 100644 index 000000000..4c5ee35f0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xwatch/XWatchSupport.java @@ -0,0 +1,572 @@ +/* Copyright (C) 2018 Andreas Shimokawa, ladbsoft + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; +import android.media.AudioManager; +import android.net.Uri; +import android.view.KeyEvent; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchSampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchService; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.User; +import nodomain.freeyourgadget.gadgetbridge.entities.XWatchActivitySample; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.State; +import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; +import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.DeviceInfo; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class XWatchSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(XWatchSupport.class); + private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo(); + TransactionBuilder builder = null; + private DeviceInfo mDeviceInfo; + private byte dayToFetch; //0 = Today; 1 = Yesterday ... + private byte maxDayToFetch; + long lastButtonTimestamp; + + public XWatchSupport() { + super(LOG); + + addSupportedService(XWatchService.UUID_SERVICE); + addSupportedService(XWatchService.UUID_WRITE); + addSupportedService(XWatchService.UUID_NOTIFY); + } + + public static byte[] crcChecksum(byte[] data) { + byte[] return_data = new byte[(data.length + 1)]; + byte checksum = 0; + + for (int i = 0; i < data.length; i++) { + return_data[i] = data[i]; + checksum += data[i]; + } + return_data[return_data.length - 1] = checksum; + + return return_data; + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZING, getContext())); + + enableNotifications(builder) + .setDateTime(builder) + .setInitialized(builder); + + return builder; + } + + /** + * Last action of initialization sequence. Sets the device to initialized. + * It is only invoked if all other actions were successfully run, so the device + * must be initialized, then. + * + * @param builder + */ + private void setInitialized(TransactionBuilder builder) { + builder.add(new SetDeviceStateAction(getDevice(), State.INITIALIZED, getContext())); + } + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public boolean connectFirstTime() { + for (int i = 0; i < 5; i++) { + if (connect()) { + return true; + } + } + return false; + } + + private XWatchSupport setDateTime(TransactionBuilder builder) { + byte[] data; + + LOG.debug("Sending current date to the XWatch"); + BluetoothGattCharacteristic deviceData = getCharacteristic(XWatchService.UUID_WRITE); + + String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); + String y = time.substring(2, 4); + String M = time.substring(4, 6); + String d = time.substring(6, 8); + String H = time.substring(8, 10); + String m = time.substring(10, 12); + String s = time.substring(12, 14); + System.out.println(y + ":" + M + ":" + d + ":" + H + ":" + m + ":" + time.substring(12, 14)); + + data = new byte[]{(byte) 1, + (byte) Integer.parseInt(y, 16), + (byte) Integer.parseInt(M, 16), + (byte) Integer.parseInt(d, 16), + (byte) Integer.parseInt(H, 16), + (byte) Integer.parseInt(m, 16), + (byte) Integer.parseInt(s, 16), + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0}; + + data = crcChecksum(data); + + builder.write(deviceData, data); + + return this; + } + + private XWatchSupport enableNotifications(TransactionBuilder builder) { + LOG.debug("Enabling action button"); + BluetoothGattCharacteristic deviceInfo = getCharacteristic(XWatchService.UUID_NOTIFY); + builder.notify(deviceInfo, true); + return this; + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + //Not supported + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + // not supported + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + // not supported + } + + @Override + public void onSetAlarms(ArrayList alarms) { + //TODO: Implement + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + //TODO: Implement + } + + @Override + public void onDeleteNotification(int id) { + //TODO: Implement + } + + @Override + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("Set date and time"); + setDateTime(builder); + builder.queue(getQueue()); + } catch (IOException ex) { + LOG.error("Unable to set time and date on XWatch device", ex); + } + } + + @Override + public void onSetCallState(CallSpec callSpec) { + //TODO: Implement (if necessary) + } + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + // not supported + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + // not supported + } + + @Override + public void onReboot() { + //Not supported + } + + @Override + public void onHeartRateTest() { + //Not supported + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + //Not supported + } + + @Override + public void onFindDevice(boolean start) { + //TODO: Implement + } + + @Override + public void onSetConstantVibration(int intensity) { + //TODO: Implement + } + + @Override + public void onFetchRecordedData(int dataTypes) { + try { + if(builder == null) { + builder = performInitialized("fetchActivityData"); + } + requestSummarizedData(builder); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + //Not supported + } + + @Override + public void onInstallApp(Uri uri) { + //Not supported + } + + @Override + public void onAppInfoReq() { + // not supported + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + // not supported + } + + @Override + public void onAppDelete(UUID uuid) { + // not supported + } + + @Override + public void onAppReorder(UUID[] uuids) { + // not supported + } + + @Override + public void onScreenshotReq() { + // not supported + } + + @Override + public boolean onCharacteristicChanged(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic) { + super.onCharacteristicChanged(gatt, characteristic); + + UUID characteristicUUID = characteristic.getUuid(); + if (XWatchService.UUID_NOTIFY.equals(characteristicUUID)) { + byte[] data = characteristic.getValue(); + if (data[0] == XWatchService.COMMAND_ACTIVITY_TOTALS) { + handleSummarizedData(characteristic.getValue()); + } else if (data[0] == XWatchService.COMMAND_ACTIVITY_DATA) { + handleDetailedData(characteristic.getValue()); + } else if (data[0] == XWatchService.COMMAND_ACTION_BUTTON) { + handleButtonPressed(characteristic.getValue()); + } else if (data[0] == XWatchService.COMMAND_CONNECTED) { + handleDeviceInfo(data, BluetoothGatt.GATT_SUCCESS); + } else { + LOG.info("Handled characteristic with unknown data: " + characteristicUUID); + logMessageContent(characteristic.getValue()); + } + } else { + LOG.info("Unhandled characteristic changed: " + characteristicUUID); + logMessageContent(characteristic.getValue()); + } + return false; + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + return super.onCharacteristicChanged(gatt, characteristic); + //TODO: Implement (if necessary) + } + + @Override + public boolean onCharacteristicWrite(BluetoothGatt gatt, + BluetoothGattCharacteristic characteristic, int status) { + return super.onCharacteristicWrite(gatt, characteristic, status); + //TODO: Implement (if necessary) + } + + public XWatchActivitySample createActivitySample(Device device, User user, int timestampInSeconds, SampleProvider provider) { + XWatchActivitySample sample = new XWatchActivitySample(); + sample.setDevice(device); + sample.setUser(user); + sample.setTimestamp(timestampInSeconds); + sample.setProvider(provider); + + return sample; + } + + private void handleDeviceInfo(byte[] value, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + mDeviceInfo = new DeviceInfo(value); + LOG.warn("Device info: " + mDeviceInfo); + versionCmd.hwVersion = "1.0"; + versionCmd.fwVersion = "1.0"; + handleGBDeviceEvent(versionCmd); + } + } + + @Override + public void onSendConfiguration(String config) { + // nothing yet + } + + @Override + public void onTestNewFunction() { + //Not supported + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + //Not supported + } + + private void handleSummarizedData(byte[] value) { + int daysIntTotal; + int daysIntPart; + + if (value.length != 16) { + LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length); + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%4x", b)); + } + } else { + daysIntPart = (value[1] & 255) << 24; + daysIntTotal = daysIntPart; + daysIntPart = (value[2] & 255) << 16; + daysIntTotal += daysIntPart; + daysIntPart = (value[3] & 255) << 8; + daysIntTotal += daysIntPart; + daysIntPart = (value[4] & 255); + daysIntTotal += daysIntPart; + + dayToFetch = 0; + maxDayToFetch = (byte) Integer.bitCount(daysIntTotal); + + try { + requestDetailedData(builder); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + } + + private void handleDetailedData(byte[] value) { + int category, intensity, steps = 0; + + if (value.length != 16) { + LOG.warn("GOT UNEXPECTED SENSOR DATA WITH LENGTH: " + value.length); + for (byte b : value) { + LOG.warn("DATA: " + String.format("0x%4x", b)); + } + } else { + try (DBHandler dbHandler = GBApplication.acquireDB()) { + XWatchSampleProvider provider = new XWatchSampleProvider(getDevice(), dbHandler.getDaoSession()); + User user = DBHelper.getUser(dbHandler.getDaoSession()); + Device device = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()); + int timestampInSeconds = 0; + + timestampInSeconds = getTimestampFromData( + value[2], + value[3], + value[4], + value[5] + ); + + category = ActivityKind.TYPE_ACTIVITY; + intensity = (value[7] & 255) + ((value[8] & 255) << 8); + steps = (value[9] & 255) + ((value[10] & 255) << 8); + + XWatchActivitySample sample = createActivitySample(device, user, timestampInSeconds, provider); + sample.setRawIntensity(intensity); + sample.setSteps(steps); + sample.setRawKind(category); + + if (LOG.isDebugEnabled()) { + LOG.debug("sample: " + sample); + } + + provider.addGBActivitySample(sample); + + if (value[5] == 95) { + dayToFetch++; + if(dayToFetch <= maxDayToFetch) { + try { + builder = performInitialized("fetchActivityData"); + requestDetailedData(builder); + performConnected(builder.getTransaction()); + } catch (IOException e) { + GB.toast(getContext(), "Error fetching activity data: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + } + } catch (Exception ex) { + GB.toast(getContext(), ex.getMessage(), Toast.LENGTH_LONG, GB.ERROR, ex); + } + } + } + + private void handleButtonPressed(byte[] value) { + long currentTimestamp = System.currentTimeMillis(); + + AudioManager audioManager = (AudioManager)getContext().getSystemService(Context.AUDIO_SERVICE); + if(audioManager.isWiredHeadsetOn()) { + if (currentTimestamp - lastButtonTimestamp < 1000) { + if (audioManager.isMusicActive()) { + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)); + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT)); + } else { + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_NEXT)); + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_NEXT)); + } + } else { + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); + audioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); + } + } + + lastButtonTimestamp = currentTimestamp; + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + //Not supported + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + //Not supported + } + + private void requestSummarizedData(TransactionBuilder builder) { + byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_TOTALS, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0}; + + fetch = XWatchSupport.crcChecksum(fetch); + builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch); + } + + private void requestDetailedData(TransactionBuilder builder) { + byte[] fetch = new byte[]{(byte) XWatchService.COMMAND_ACTIVITY_DATA, + (byte) dayToFetch, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0, + (byte) 0}; + + fetch = XWatchSupport.crcChecksum(fetch); + builder.write(getCharacteristic(XWatchService.UUID_WRITE), fetch); + } + + private int getTimestampFromData(byte year, byte month, byte day, byte hoursminutes) { + int timestamp = 0; + int yearInt, monthInt, dayInt, hoursMinutesInt = 0; + int hours, minutes = 0; + + yearInt = Integer.valueOf(String.format("%02x", year, 16)); + monthInt = Integer.valueOf(String.format("%02x", month, 16)); + dayInt = Integer.valueOf(String.format("%02x", day, 16)); + hoursMinutesInt = Integer.valueOf(String.format("%02x", hoursminutes), 16); + + minutes = hoursMinutesInt % 4; + hours = (hoursMinutesInt - minutes) / 4; + minutes = minutes * 15; + + GregorianCalendar cal = new GregorianCalendar( + 2000 + yearInt, + monthInt - 1, + dayInt, + hours, + minutes + ); + + timestamp = (int)(cal.getTimeInMillis() / 1000); + + return timestamp; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBAutoFetchReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBAutoFetchReceiver.java new file mode 100644 index 000000000..f632733f5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBAutoFetchReceiver.java @@ -0,0 +1,39 @@ +/* Copyright (C) 2018 Daniele Gobbetti, Martin + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes; + + +public class GBAutoFetchReceiver extends BroadcastReceiver { + private static final Logger LOG = LoggerFactory.getLogger(GBAutoFetchReceiver.class); + + @Override + public void onReceive(Context context, Intent intent) { + //LOG.info("User is present!"); + GBApplication.deviceService().onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY); + } +} + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBCallControlReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBCallControlReceiver.java index 263ca85d8..bbee17b84 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBCallControlReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBCallControlReceiver.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java index 86fc8ac3a..a0f95ebe2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/receivers/GBMusicControlReceiver.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti, Gabe Schrecker This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java index 1adf9917a..e73527eab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/AbstractSerialDeviceSupport.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Julien +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Julien Pivotto, Steffen Liebergeld This file is part of Gadgetbridge. @@ -180,7 +180,7 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport } @Override - public void onFetchActivityData() { + public void onFetchRecordedData(int dataTypes) { byte[] bytes = gbDeviceProtocol.encodeSynchronizeActivityData(); sendToDevice(bytes); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java index 7276b280b..376509a83 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceIoThread.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java index 8072b6827..5070ce8e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/serial/GBDeviceProtocol.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Julien +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Julien Pivotto, Steffen Liebergeld This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index 794f82652..7bf8b9cfb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Felix + Konstantin Maurer, Taavi Eomäe This file is part of Gadgetbridge. @@ -16,10 +17,10 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.util; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ContentUris; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Color; @@ -30,8 +31,14 @@ import android.os.ParcelUuid; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.FileProvider; import android.support.v4.content.LocalBroadcastManager; +import android.text.TextUtils; +import java.io.File; +import java.io.IOException; import java.net.URISyntaxException; import java.util.Locale; @@ -39,7 +46,13 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; public class AndroidUtils { - public static ParcelUuid[] toParcelUUids(Parcelable[] uuids) { + /** + * Creates a new {@link ParcelUuid} array with the contents of the given uuids. + * The given array is expected to contain only {@link ParcelUuid} elements. + * @param uuids an array of {@link ParcelUuid} elements + * @return a {@link ParcelUuid} array instance with the same contents + */ + public static ParcelUuid[] toParcelUuids(Parcelable[] uuids) { if (uuids == null) { return null; } @@ -121,6 +134,28 @@ public class AndroidUtils { + Integer.toHexString(Color.blue(color)); } + /** + * As seen on stackoverflow https://stackoverflow.com/a/36714242/1207186 + * Try to find the file path of a document uri + * @param context the application context + * @param uri the Uri for which the path should be resolved + * @return the path corresponding to the Uri as a String + * @throws IllegalArgumentException on any problem decoding the uri to a path + */ + public static @NonNull String getFilePath(@NonNull Context context, @NonNull Uri uri) throws IllegalArgumentException { + try { + String path = internalGetFilePath(context, uri); + if (TextUtils.isEmpty(path)) { + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri); + } + return path; + } catch (IllegalArgumentException ex) { + throw ex; + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri, ex); + } + } + /** * As seen on stackoverflow https://stackoverflow.com/a/36714242/1207186 * Try to find the file path of a document uri @@ -129,9 +164,10 @@ public class AndroidUtils { * @return the path corresponding to the Uri as a String * @throws URISyntaxException */ - public static String getFilePath(Context context, Uri uri) throws URISyntaxException { + private static @Nullable String internalGetFilePath(@NonNull Context context, @NonNull Uri uri) throws URISyntaxException { String selection = null; String[] selectionArgs = null; + // Uri is different in versions after KITKAT (Android 4.4), we need to if (Build.VERSION.SDK_INT >= 19 && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri)) { if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { @@ -140,8 +176,13 @@ public class AndroidUtils { return Environment.getExternalStorageDirectory() + "/" + split[1]; } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { final String id = DocumentsContract.getDocumentId(uri); - uri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + if (!TextUtils.isEmpty(id)) { + if (id.startsWith("raw:")) { + return id.replaceFirst("raw:", ""); + } + uri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + } } else if ("com.android.providers.media.documents".equals(uri.getAuthority())) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); @@ -164,18 +205,26 @@ public class AndroidUtils { MediaStore.Images.Media.DATA }; Cursor cursor = null; - try { - cursor = context.getContentResolver() - .query(uri, projection, selection, selectionArgs, null); - int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); - if (cursor.moveToFirst()) { - return cursor.getString(column_index); - } - } catch (Exception e) { + cursor = context.getContentResolver() + .query(uri, projection, selection, selectionArgs, null); + int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); + if (cursor.moveToFirst()) { + return cursor.getString(column_index); } } else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } - return null; + throw new IllegalArgumentException("Unable to decode the given uri to a file path: " + uri); + } + + public static void viewFile(String path, String action, Context context) throws IOException { + Intent intent = new Intent(action); + File file = new File(path); + + Uri contentUri = FileProvider.getUriForFile(context, + context.getApplicationContext().getPackageName() + ".screenshot_provider", file); + intent.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setData(contentUri); + context.startActivity(intent); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java index e7350dae3..0bfbca0e5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ArrayUtils.java @@ -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. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BitmapUtil.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BitmapUtil.java index 756ba8c4f..e83beaf10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BitmapUtil.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/BitmapUtil.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Frank Slezak +/* Copyright (C) 2017-2018 Frank Slezak This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java index b3780bf2f..ba6a01b7f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Carsten Pfeiffer +/* Copyright (C) 2015-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java index 068024b32..b9a577ce8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DateTimeUtils.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2015-2018 Andreas Shimokawa, AndrewH, Carsten Pfeiffer, + Daniele Gobbetti This file is part of Gadgetbridge. @@ -20,7 +21,9 @@ import android.text.format.DateUtils; import com.github.pfichtner.durationformatter.DurationFormatter; +import java.text.FieldPosition; import java.text.ParseException; +import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -33,9 +36,35 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; public class DateTimeUtils { private static SimpleDateFormat DAY_STORAGE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + public static SimpleDateFormat ISO_8601_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US){ + //see https://github.com/Freeyourgadget/Gadgetbridge/issues/1076#issuecomment-383834116 and https://stackoverflow.com/a/30221245 + + @Override + public Date parse(String text, ParsePosition pos) { + if (text.length() > 3) { + text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2); + } + return super.parse(text, pos); + + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) { + StringBuffer rfcFormat = super.format(date, toAppendTo, pos); + return rfcFormat.insert(rfcFormat.length() - 2, ":"); + } + + }; //no public access, we have to workaround Android bugs public static String formatDateTime(Date date) { - return DateUtils.formatDateTime(GBApplication.getContext(), date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); + return DateUtils.formatDateTime(GBApplication.getContext(), date.getTime(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_NO_YEAR); + } + + public static String formatIso8601(Date date) { + if(GBApplication.isRunningNougatOrLater()){ + return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.US).format(date); + } + return ISO_8601_FORMAT.format(date); } public static String formatDate(Date date) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 4041e5151..feab94917 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -1,5 +1,6 @@ -/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer, - Daniele Gobbetti, João Paulo Barraca, protomors, Quallenauge, Sami Alaoui +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, + Daniele Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, + Sami Alaoui, tiparega This file is part of Gadgetbridge. @@ -20,6 +21,7 @@ package nodomain.freeyourgadget.gadgetbridge.util; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.support.annotation.NonNull; import android.widget.Toast; import org.slf4j.Logger; @@ -42,10 +44,12 @@ import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.Q8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2HRXCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; @@ -53,6 +57,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; @@ -193,6 +198,7 @@ public class DeviceHelper { List result = new ArrayList<>(); result.add(new AmazfitBipCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new AmazfitCorCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new MiBand3Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand2HRXCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm result.add(new MiBandCoordinator()); @@ -202,8 +208,10 @@ public class DeviceHelper { result.add(new HPlusCoordinator()); result.add(new No1F1Coordinator()); result.add(new MakibesF68Coordinator()); + result.add(new Q8Coordinator()); result.add(new EXRIZUK8Coordinator()); result.add(new TeclastH30Coordinator()); + result.add(new XWatchCoordinator()); result.add(new ZeTimeCoordinator()); return result; @@ -248,11 +256,18 @@ public class DeviceHelper { return gbDevice; } - private List getBondedDevices(BluetoothAdapter btAdapter) { + private @NonNull List getBondedDevices(@NonNull BluetoothAdapter btAdapter) { Set pairedDevices = btAdapter.getBondedDevices(); + if (pairedDevices == null) { + return Collections.emptyList(); + } + List result = new ArrayList<>(pairedDevices.size()); DeviceHelper deviceHelper = DeviceHelper.getInstance(); for (BluetoothDevice pairedDevice : pairedDevices) { + if (pairedDevice == null) { + continue; // just to be safe, see https://github.com/Freeyourgadget/Gadgetbridge/pull/1052 + } if (pairedDevice.getName() != null && (pairedDevice.getName().startsWith("Pebble-LE ") || pairedDevice.getName().startsWith("Pebble Time LE "))) { continue; // ignore LE Pebble (this is part of the main device now (volatileAddress) } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java.orig b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java.orig new file mode 100644 index 000000000..fca5bd1d5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java.orig @@ -0,0 +1,311 @@ +/* Copyright (C) 2015-2018 0nse, Andreas Shimokawa, Carsten Pfeiffer, + Daniele Gobbetti, João Paulo Barraca, ladbsoft, protomors, Quallenauge, + Sami Alaoui, tiparega + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.util; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.support.annotation.NonNull; +import android.widget.Toast; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; +import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.hplus.Q8Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbip.AmazfitBipCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor.AmazfitCorCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband2.MiBand2HRXCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband3.MiBand3Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.liveview.LiveviewCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; +<<<<<<< HEAD +import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeCoordinator; +======= +import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchCoordinator; +>>>>>>> master +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.entities.DeviceAttributes; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class DeviceHelper { + + private static final Logger LOG = LoggerFactory.getLogger(DeviceHelper.class); + + private static final DeviceHelper instance = new DeviceHelper(); + + public static DeviceHelper getInstance() { + return instance; + } + + // lazily created + private List coordinators; + + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + for (DeviceCoordinator coordinator : getAllCoordinators()) { + DeviceType deviceType = coordinator.getSupportedType(candidate); + if (deviceType.isSupported()) { + return deviceType; + } + } + return DeviceType.UNKNOWN; + } + + public boolean getSupportedType(GBDevice device) { + for (DeviceCoordinator coordinator : getAllCoordinators()) { + if (coordinator.supports(device)) { + return true; + } + } + return false; + } + + public GBDevice findAvailableDevice(String deviceAddress, Context context) { + Set availableDevices = getAvailableDevices(context); + for (GBDevice availableDevice : availableDevices) { + if (deviceAddress.equals(availableDevice.getAddress())) { + return availableDevice; + } + } + return null; + } + + /** + * Returns the list of all available devices that are supported by Gadgetbridge. + * Note that no state is known about the returned devices. Even if one of those + * devices is connected, it will report the default not-connected state. + * + * Clients interested in the "live" devices being managed should use the class + * DeviceManager. + * @param context + * @return + */ + public Set getAvailableDevices(Context context) { + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + + Set availableDevices = new LinkedHashSet(); + + if (btAdapter == null) { + GB.toast(context, context.getString(R.string.bluetooth_is_not_supported_), Toast.LENGTH_SHORT, GB.WARN); + } else if (!btAdapter.isEnabled()) { + GB.toast(context, context.getString(R.string.bluetooth_is_disabled_), Toast.LENGTH_SHORT, GB.WARN); + } + List dbDevices = getDatabaseDevices(); + // these come first, as they have the most information already + availableDevices.addAll(dbDevices); + if (btAdapter != null) { + List bondedDevices = getBondedDevices(btAdapter); + availableDevices.addAll(bondedDevices); + } + + Prefs prefs = GBApplication.getPrefs(); + String miAddr = prefs.getString(MiBandConst.PREF_MIBAND_ADDRESS, ""); + if (miAddr.length() > 0) { + GBDevice miDevice = new GBDevice(miAddr, "MI", DeviceType.MIBAND); + availableDevices.add(miDevice); + } + + String pebbleEmuAddr = prefs.getString("pebble_emu_addr", ""); + String pebbleEmuPort = prefs.getString("pebble_emu_port", ""); + if (pebbleEmuAddr.length() >= 7 && pebbleEmuPort.length() > 0) { + GBDevice pebbleEmuDevice = new GBDevice(pebbleEmuAddr + ":" + pebbleEmuPort, "Pebble qemu", DeviceType.PEBBLE); + availableDevices.add(pebbleEmuDevice); + } + return availableDevices; + } + + public GBDevice toSupportedDevice(BluetoothDevice device) { + GBDeviceCandidate candidate = new GBDeviceCandidate(device, GBDevice.RSSI_UNKNOWN, device.getUuids()); + return toSupportedDevice(candidate); + } + + public GBDevice toSupportedDevice(GBDeviceCandidate candidate) { + for (DeviceCoordinator coordinator : getAllCoordinators()) { + if (coordinator.supports(candidate)) { + return coordinator.createDevice(candidate); + } + } + return null; + } + + public DeviceCoordinator getCoordinator(GBDeviceCandidate device) { + synchronized (this) { + for (DeviceCoordinator coord : getAllCoordinators()) { + if (coord.supports(device)) { + return coord; + } + } + } + return new UnknownDeviceCoordinator(); + } + + public DeviceCoordinator getCoordinator(GBDevice device) { + synchronized (this) { + for (DeviceCoordinator coord : getAllCoordinators()) { + if (coord.supports(device)) { + return coord; + } + } + } + return new UnknownDeviceCoordinator(); + } + + public synchronized List getAllCoordinators() { + if (coordinators == null) { + coordinators = createCoordinators(); + } + return coordinators; + } + + private List createCoordinators() { + List result = new ArrayList<>(); + result.add(new AmazfitBipCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new AmazfitCorCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new MiBand3Coordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new MiBand2HRXCoordinator()); // Note: must come before MiBand2 because detection is hacky, atm + result.add(new MiBand2Coordinator()); // Note: MiBand2 must come before MiBand because detection is hacky, atm + result.add(new MiBandCoordinator()); + result.add(new PebbleCoordinator()); + result.add(new VibratissimoCoordinator()); + result.add(new LiveviewCoordinator()); + result.add(new HPlusCoordinator()); + result.add(new No1F1Coordinator()); + result.add(new MakibesF68Coordinator()); + result.add(new Q8Coordinator()); + result.add(new EXRIZUK8Coordinator()); + result.add(new TeclastH30Coordinator()); +<<<<<<< HEAD + result.add(new ZeTimeCoordinator()); +======= + result.add(new XWatchCoordinator()); +>>>>>>> master + + return result; + } + + private List getDatabaseDevices() { + List result = new ArrayList<>(); + try (DBHandler lockHandler = GBApplication.acquireDB()) { + List activeDevices = DBHelper.getActiveDevices(lockHandler.getDaoSession()); + for (Device dbDevice : activeDevices) { + GBDevice gbDevice = toGBDevice(dbDevice); + if (gbDevice != null && DeviceHelper.getInstance().getSupportedType(gbDevice)) { + result.add(gbDevice); + } + } + return result; + + } catch (Exception e) { + GB.toast("Error retrieving devices from database", Toast.LENGTH_SHORT, GB.ERROR); + return Collections.emptyList(); + } + } + + /** + * Converts a known device from the database to a GBDevice. + * Note: The device might not be supported anymore, so callers should verify that. + * @param dbDevice + * @return + */ + public GBDevice toGBDevice(Device dbDevice) { + DeviceType deviceType = DeviceType.fromKey(dbDevice.getType()); + GBDevice gbDevice = new GBDevice(dbDevice.getIdentifier(), dbDevice.getName(), deviceType); + List deviceAttributesList = dbDevice.getDeviceAttributesList(); + if (deviceAttributesList.size() > 0) { + gbDevice.setModel(dbDevice.getModel()); + DeviceAttributes attrs = deviceAttributesList.get(0); + gbDevice.setFirmwareVersion(attrs.getFirmwareVersion1()); + gbDevice.setFirmwareVersion2(attrs.getFirmwareVersion2()); + gbDevice.setVolatileAddress(attrs.getVolatileIdentifier()); + } + + return gbDevice; + } + + private @NonNull List getBondedDevices(@NonNull BluetoothAdapter btAdapter) { + Set pairedDevices = btAdapter.getBondedDevices(); + if (pairedDevices == null) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(pairedDevices.size()); + DeviceHelper deviceHelper = DeviceHelper.getInstance(); + for (BluetoothDevice pairedDevice : pairedDevices) { + if (pairedDevice == null) { + continue; // just to be safe, see https://github.com/Freeyourgadget/Gadgetbridge/pull/1052 + } + if (pairedDevice.getName() != null && (pairedDevice.getName().startsWith("Pebble-LE ") || pairedDevice.getName().startsWith("Pebble Time LE "))) { + continue; // ignore LE Pebble (this is part of the main device now (volatileAddress) + } + GBDevice device = deviceHelper.toSupportedDevice(pairedDevice); + if (device != null) { + result.add(device); + } + } + return result; + } + + /** + * Attempts to removing the bonding with the given device. Returns true + * if bonding was supposedly successful and false if anything went wrong + * @param device + * @return + */ + public boolean removeBond(GBDevice device) throws GBException { + BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter(); + if (defaultAdapter != null) { + BluetoothDevice remoteDevice = defaultAdapter.getRemoteDevice(device.getAddress()); + if (remoteDevice != null) { + try { + Method method = BluetoothDevice.class.getMethod("removeBond", (Class[]) null); + Object result = method.invoke(remoteDevice, (Object[]) null); + return Boolean.TRUE.equals(result); + } catch (Exception e) { + throw new GBException("Error removing bond to device: " + device, e); + } + } + } + return false; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java index dfff7a629..9ec1ad2da 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/FileUtils.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, JohnnySun +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Felix + Konstantin Maurer, JohnnySun, Taavi Eomäe This file is part of Gadgetbridge. @@ -219,7 +220,7 @@ public class FileUtils { try { dirs = context.getExternalFilesDirs(null); } catch (NullPointerException | UnsupportedOperationException ex) { - // workaround for robolectric 3.1.2 not implementinc getExternalFilesDirs() + // workaround for robolectric 3.1.2 not implementing getExternalFilesDirs() // https://github.com/robolectric/robolectric/issues/2531 File dir = context.getExternalFilesDir(null); if (dir != null) { @@ -309,4 +310,14 @@ public class FileUtils { } throw new IOException("Cannot create temporary directory in " + parent); } + + /** + * Replaces some wellknown invalid characters in the given filename + * to underscrores. + * @param name the file name to make valid + * @return the valid file name + */ + public static String makeValidFileName(String name) { + return name.replaceAll("\0/:\\r\\n\\\\", "_"); + } } \ No newline at end of file diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java index e45e77240..1af2c4e89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GB.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele - Gobbetti, Uwe Hermann, Yar +/* Copyright (C) 2015-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Felix Konstantin Maurer, Taavi Eomäe, Uwe Hermann, Yar This file is part of Gadgetbridge. @@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.util; import android.app.Activity; import android.app.Notification; +import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; @@ -29,6 +30,7 @@ import android.os.Handler; import android.os.Looper; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; import android.widget.Toast; import org.slf4j.Logger; @@ -50,7 +52,13 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService; +import static nodomain.freeyourgadget.gadgetbridge.GBApplication.isRunningOreoOrLater; + public class GB { + + public static final String NOTIFICATION_CHANNEL_ID = "gadgetbridge"; + public static final String NOTIFICATION_CHANNEL_ID_TRANSFER = "gadgetbridge transfer"; + public static final int NOTIFICATION_ID = 1; public static final int NOTIFICATION_ID_INSTALL = 2; public static final int NOTIFICATION_ID_LOW_BATTERY = 3; @@ -84,7 +92,7 @@ public class GB { } Boolean connected = device.isInitialized(); - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); builder.setContentTitle(deviceName) .setTicker(deviceName + " - " + text) .setContentText(text) @@ -99,7 +107,7 @@ public class GB { PendingIntent disconnectPendingIntent = PendingIntent.getService(context, 0, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT); builder.addAction(R.drawable.ic_notification_disconnected, context.getString(R.string.controlcenter_disconnect), disconnectPendingIntent); if (GBApplication.isRunningLollipopOrLater() && DeviceHelper.getInstance().getCoordinator(device).supportsActivityDataFetching()) { //for some reason this fails on KK - deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_ACTIVITY_DATA); + deviceCommunicationServiceIntent.setAction(DeviceService.ACTION_FETCH_RECORDED_DATA); PendingIntent fetchPendingIntent = PendingIntent.getService(context, 1, deviceCommunicationServiceIntent, PendingIntent.FLAG_ONE_SHOT); builder.addAction(R.drawable.ic_action_fetch_activity_data, context.getString(R.string.controlcenter_fetch_activity_data), fetchPendingIntent); } @@ -119,7 +127,7 @@ public class GB { } public static Notification createNotification(String text, Context context) { - NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID); builder.setTicker(text) .setContentText(text) .setSmallIcon(R.drawable.ic_notification_disconnected) @@ -150,12 +158,16 @@ public class GB { if (notification == null) { return; } - NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); +// TODO: I believe it's better do always use the NMC instead of the old call, but old code works + NotificationManagerCompat nm = NotificationManagerCompat.from(context); +// NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); nm.notify(id, notification); } private static void removeNotification(int id, Context context) { - NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); +// TODO: I believe it's better do always use the NMC instead of the old call, but old code works + NotificationManagerCompat nm = NotificationManagerCompat.from(context); +// NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); nm.cancel(id); } @@ -316,17 +328,29 @@ public class GB { } } - private static Notification createTransferNotification(String text, boolean ongoing, + private static Notification createTransferNotification(String title, String text, boolean ongoing, int percentage, Context context) { Intent notificationIntent = new Intent(context, ControlCenterv2.class); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + if(isRunningOreoOrLater()) { + NotificationChannel channel = notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID_TRANSFER); + if(channel == null) { + channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_TRANSFER, + context.getString(R.string.notification_channel_name), + NotificationManager.IMPORTANCE_LOW); + notificationManager.createNotificationChannel(channel); + } + } notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); - NotificationCompat.Builder nb = new NotificationCompat.Builder(context) + NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID_TRANSFER) + .setTicker((title == null) ? context.getString(R.string.app_name) : title) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setContentTitle(context.getString(R.string.app_name)) + .setContentTitle((title == null) ? context.getString(R.string.app_name) : title) + .setStyle(new NotificationCompat.BigTextStyle().bigText(text)) .setContentText(text) .setContentIntent(pendingIntent) .setOngoing(ongoing); @@ -348,11 +372,11 @@ public class GB { removeNotification(NOTIFICATION_ID_LOW_BATTERY, context); } - public static void updateTransferNotification(String text, boolean ongoing, int percentage, Context context) { + public static void updateTransferNotification(String title, String text, boolean ongoing, int percentage, Context context) { if (percentage == 100) { removeNotification(NOTIFICATION_ID_TRANSFER, context); } else { - Notification notification = createTransferNotification(text, ongoing, percentage, context); + Notification notification = createTransferNotification(title, text, ongoing, percentage, context); updateNotification(notification, NOTIFICATION_ID_TRANSFER, context); } } @@ -365,7 +389,7 @@ public class GB { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); - NotificationCompat.Builder nb = new NotificationCompat.Builder(context) + NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(context.getString(R.string.app_name)) .setContentText(text) .setTicker(text) @@ -395,7 +419,7 @@ public class GB { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); - NotificationCompat.Builder nb = new NotificationCompat.Builder(context) + NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(context.getString(R.string.notif_battery_low_title)) .setContentText(text) .setContentIntent(pendingIntent) @@ -429,7 +453,7 @@ public class GB { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); - NotificationCompat.Builder nb = new NotificationCompat.Builder(context) + NotificationCompat.Builder nb = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentTitle(context.getString(R.string.notif_export_failed_title)) .setContentText(text) .setContentIntent(pendingIntent) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java index 7561467bf..1bd889bb1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/GBPrefs.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2016-2018 Carsten Pfeiffer, Daniele Gobbetti, Felix + Konstantin Maurer This file is part of Gadgetbridge. @@ -21,6 +22,7 @@ import java.util.Date; public class GBPrefs { public static final String PACKAGE_BLACKLIST = "package_blacklist"; + public static final String PACKAGE_PEBBLEMSG_BLACKLIST = "package_pebblemsg_blacklist"; public static final String CALENDAR_BLACKLIST = "calendar_blacklist"; public static final String AUTO_RECONNECT = "general_autocreconnect"; private static final String AUTO_START = "general_autostartonboot"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java index a4f75191b..f89ab5088 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/ImportExportSharedPreferences.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Alberto, Carsten Pfeiffer, Daniele Gobbetti +/* Copyright (C) 2017-2018 Alberto, Carsten Pfeiffer, Daniele Gobbetti, + Taavi Eomäe This file is part of Gadgetbridge. @@ -130,7 +131,14 @@ public class ImportExportSharedPreferences { for (int z=0;z apps_pebble_blacklist = new HashSet<>(); + text=text.replace("[","").replace("]",""); + for (int z=0;z calendars_blacklist = new HashSet<>(); text = text.replace("[", "").replace("]", ""); @@ -140,7 +148,7 @@ public class ImportExportSharedPreferences { GBApplication.setCalendarsBlackList(calendars_blacklist); } } else if (!PREFERENCES.equals(name)) { - throw new Exception("Unkown type " + name); + throw new Exception("Unknown type " + name); } break; case XmlPullParser.END_TAG: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java index 2c0440eb6..9e192dcc3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/JavaExtensions.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 ivanovlev +/* Copyright (C) 2017-2018 ivanovlev This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index eff512dbd..21246c436 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -1,5 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, ivanovlev, lazarosfs, McSym28, - Yaron Shahrabani +/* Copyright (C) 2017-2018 Andreas Shimokawa, Daniele Gobbetti, ivanovlev, + lazarosfs, McSym28, Ted Stein, Yaron Shahrabani This file is part of Gadgetbridge. @@ -19,9 +19,10 @@ package nodomain.freeyourgadget.gadgetbridge.util; import org.apache.commons.lang3.text.WordUtils; +import java.text.Normalizer; import java.util.HashMap; import java.util.Map; -import java.text.Normalizer; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; public class LanguageUtils { @@ -67,13 +68,16 @@ public class LanguageUtils { put('د', "d"); put('ذ', "th"); put('ر', "r"); put('ز', "z"); put('س', "s"); put('ش', "sh"); put('ص', "9"); put('ض', "9'"); put('ط', "6"); put('ظ', "6'"); put('ع', "3"); put('غ', "3'"); put('ف', "f"); put('ق', "q"); put('ك', "k"); put('ل', "l"); put('م', "m"); put('ن', "n"); put('ه', "h"); - put('و', "w"); put('ي', "y"); put('ى', "a"); + put('و', "w"); put('ي', "y"); put('ى', "a"); put('ﺓ', ""); put('آ', "2"); put('ئ', "2"); put('إ', "2"); put('ؤ', "2"); put('أ', "2"); put('ء', "2"); // Farsi put('پ', "p"); put('چ', "ch"); put('ڜ', "ch"); put('ڤ', "v"); put('ڥ', "v"); put('ڨ', "g"); put('گ', "g"); put('ݣ', "g"); + // Polish + put('Ł', "L"); put('ł', "l"); + //TODO: these must be configurable. If someone wants to transliterate cyrillic it does not mean his device has no German umlauts //all or nothing is really bad here } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java index 3d9225540..5b22182c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LimitedQueue.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti, Julien Pivotto +/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti, Julien Pivotto This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java index 140eed573..4076529df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/NotificationUtils.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Lukas Veneziano This file is part of Gadgetbridge. @@ -47,6 +48,8 @@ public class NotificationUtils { case SNAPCHAT: case TELEGRAM: case THREEMA: + case KONTALK: + case ANTOX: case TWITTER: case WHATSAPP: case VIBER: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java index fcbee77c6..c0bbceee8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/PebbleUtils.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti, Frank Slezak +/* Copyright (C) 2016-2018 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Frank Slezak This file is part of Gadgetbridge. @@ -129,21 +130,24 @@ public class PebbleUtils { return null; } - public static String parseIncomingAppMessage(String msg, UUID uuid) { + public static String parseIncomingAppMessage(String msg, UUID uuid, int transactionId) { JSONObject jsAppMessage = new JSONObject(); JSONObject knownKeys = PebbleUtils.getAppConfigurationKeys(uuid); SparseArray appKeysMap = new SparseArray<>(); - - if (knownKeys == null || msg == null) { - return "{}"; - } - String inKey, outKey; - //knownKeys contains "name"->"index", we need to reverse that - for (Iterator key = knownKeys.keys(); key.hasNext(); ) { - inKey = key.next(); - appKeysMap.put(knownKeys.optInt(inKey), inKey); + +// TODO: The fact that knownKeys is null for the passed UUID means that the +// watchapp was installed by some other app, hence we cannot communicate with it. +// The user could be warned somehow. + if (knownKeys == null || msg == null) { + msg = "[]"; + } else { + //knownKeys contains "name"->"index", we need to reverse that + for (Iterator key = knownKeys.keys(); key.hasNext(); ) { + inKey = key.next(); + appKeysMap.put(knownKeys.optInt(inKey), inKey); + } } try { @@ -168,6 +172,9 @@ public class PebbleUtils { } } jsAppMessage.put("payload", outgoing); + JSONObject data = new JSONObject(); + data.put("transactionId", transactionId); + jsAppMessage.put("data", data); } catch (Exception e) { LOG.warn("Unable to parse incoming app message", e); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java index 3a13319a7..a07e464b3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Prefs.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer, JohnnySun +/* Copyright (C) 2016-2018 Carsten Pfeiffer, JohnnySun This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java index d7b6757d9..641773a4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/StringUtils.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Carsten Pfeiffer, João Paulo Barraca +/* Copyright (C) 2017-2018 Carsten Pfeiffer, João Paulo Barraca This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/TimePreference.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/TimePreference.java index 2342e7608..043cc4697 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/TimePreference.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/TimePreference.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 José Rebelo +/* Copyright (C) 2017-2018 José Rebelo This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java index d9387cf87..9aa07ad99 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/UriHelper.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2016-2017 Carsten Pfeiffer +/* Copyright (C) 2016-2018 Carsten Pfeiffer This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java index 855241cf2..5cbfd9ed0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/Version.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Michal Novotny +/* Copyright (C) 2017-2018 Andreas Shimokawa, Carsten Pfeiffer, Michal Novotny This file is part of Gadgetbridge. diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/WebViewSingleton.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/WebViewSingleton.java index c306e0367..62bc0e0db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/WebViewSingleton.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/WebViewSingleton.java @@ -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. @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.MutableContextWrapper; import android.content.ServiceConnection; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -30,6 +31,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.RemoteException; import android.support.annotation.NonNull; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; @@ -53,32 +55,33 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.JSInt public class WebViewSingleton { private static final Logger LOG = LoggerFactory.getLogger(WebViewSingleton.class); + private static WebViewSingleton instance = new WebViewSingleton(); - private WebView instance = null; + private WebView webView = null; private MutableContextWrapper contextWrapper; private Looper mainLooper; - private static WebViewSingleton webViewSingleton = new WebViewSingleton(); - private static UUID currentRunningUUID; - public static Messenger internetHelper = null; - public static boolean internetHelperBound; - public static CountDownLatch latch; - public static WebResourceResponse internetResponse; - public final static Messenger internetHelperListener = new Messenger(new IncomingHandler()); + private UUID currentRunningUUID; + private Messenger internetHelper = null; + public boolean internetHelperBound; + private boolean internetHelperInstalled; + private CountDownLatch latch; + private WebResourceResponse internetResponse; + private Messenger internetHelperListener; private WebViewSingleton() { } public static synchronized void ensureCreated(Activity context) { - if (webViewSingleton.instance == null) { - webViewSingleton.contextWrapper = new MutableContextWrapper(context); - webViewSingleton.mainLooper = context.getMainLooper(); - webViewSingleton.instance = new WebView(webViewSingleton.contextWrapper); + if (instance.webView == null) { + instance.contextWrapper = new MutableContextWrapper(context); + instance.mainLooper = context.getMainLooper(); + instance.webView = new WebView(instance.contextWrapper); WebView.setWebContentsDebuggingEnabled(true); - webViewSingleton.instance.setWillNotDraw(true); - webViewSingleton.instance.clearCache(true); - webViewSingleton.instance.setWebViewClient(new GBWebClient()); - webViewSingleton.instance.setWebChromeClient(new GBChromeClient()); - WebSettings webSettings = webViewSingleton.instance.getSettings(); + instance.webView.setWillNotDraw(true); + instance.webView.clearCache(true); + instance.webView.setWebViewClient(new GBWebClient()); + instance.webView.setWebChromeClient(new GBChromeClient()); + WebSettings webSettings = instance.webView.getSettings(); webSettings.setJavaScriptEnabled(true); //needed to access the DOM webSettings.setDomStorageEnabled(true); @@ -88,7 +91,7 @@ public class WebViewSingleton { } //Internet helper outgoing connection - private static ServiceConnection internetHelperConnection = new ServiceConnection() { + private ServiceConnection internetHelperConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { LOG.info("internet helper service bound"); internetHelperBound = true; @@ -102,8 +105,20 @@ public class WebViewSingleton { } }; + public static WebViewSingleton getInstance() { + return instance; + } + + public WebResourceResponse send(Message webRequest) throws RemoteException, InterruptedException { + webRequest.replyTo = internetHelperListener; + latch = new CountDownLatch(1); //the messenger should run on a single thread, hence we don't need to be worried about concurrency. This approach however is certainly not ideal. + internetHelper.send(webRequest); + latch.await(); + return internetResponse; + } + //Internet helper inbound (responses) handler - private static class IncomingHandler extends Handler { + private class IncomingHandler extends Handler { private String getCharsetFromHeaders(String contentType) { if (contentType != null && contentType.toLowerCase().trim().contains("charset=")) { @@ -135,9 +150,9 @@ public class WebViewSingleton { } @NonNull - public static WebView getWebView(Context context) { - webViewSingleton.contextWrapper.setBaseContext(context); - return webViewSingleton.instance; + public WebView getWebView(Context context) { + contextWrapper.setBaseContext(context); + return webView; } /** @@ -145,16 +160,21 @@ public class WebViewSingleton { * @param uuid the uuid of the application expected to be running * @throws IllegalStateException when the webview is not active or the app is not running */ - public static void checkAppRunning(@NonNull UUID uuid) { - if (webViewSingleton.instance == null) { - throw new IllegalStateException("webViewSingleton.instance is null!"); + public void checkAppRunning(@NonNull UUID uuid) { + if (webView == null) { + throw new IllegalStateException("instance.webView is null!"); } if (!uuid.equals(currentRunningUUID)) { throw new IllegalStateException("Expected app " + uuid + " is not running, but " + currentRunningUUID + " is."); } } - public static void runJavascriptInterface(@NonNull GBDevice device, @NonNull UUID uuid) { + public void runJavascriptInterface(@NonNull Activity context, @NonNull GBDevice device, @NonNull UUID uuid) { + ensureCreated(context); + runJavascriptInterface(device, uuid); + } + + public void runJavascriptInterface(@NonNull GBDevice device, @NonNull UUID uuid) { if (uuid.equals(currentRunningUUID)) { LOG.debug("WEBVIEW uuid not changed keeping the old context"); } else { @@ -170,16 +190,26 @@ public class WebViewSingleton { webView.loadUrl("file:///android_asset/app_config/configure.html?rand=" + Math.random() * 500); } }); - if (!internetHelperBound) { - Intent intent = new Intent(); - intent.setComponent(new ComponentName("nodomain.freeyourgadget.internethelper", "nodomain.freeyourgadget.internethelper.MyService")); - webViewSingleton.contextWrapper.getApplicationContext().bindService(intent, internetHelperConnection, Context.BIND_AUTO_CREATE); + if (contextWrapper != null && !internetHelperBound && !internetHelperInstalled) { + String internetHelperPkg = "nodomain.freeyourgadget.internethelper"; + String internetHelperCls = internetHelperPkg + ".MyService"; + try { + contextWrapper.getPackageManager().getApplicationInfo(internetHelperPkg, 0); + Intent intent = new Intent(); + intent.setComponent(new ComponentName(internetHelperPkg, internetHelperCls)); + contextWrapper.getApplicationContext().bindService(intent, internetHelperConnection, Context.BIND_AUTO_CREATE); + internetHelperListener = new Messenger(new IncomingHandler()); + internetHelperInstalled = true; + } + catch (PackageManager.NameNotFoundException e) { + internetHelperInstalled = false; + LOG.info("WEBVIEW: Internet helper not installed, only mimicked HTTP requests will work."); + } } } - } - public static void stopJavascriptInterface() { + public void stopJavascriptInterface() { invokeWebview(new WebViewRunnable() { @Override public void invoke(WebView webView) { @@ -189,10 +219,10 @@ public class WebViewSingleton { }); } - public static void disposeWebView() { + public void disposeWebView() { if (internetHelperBound) { LOG.debug("WEBVIEW: will unbind the internet helper"); - webViewSingleton.contextWrapper.getApplicationContext().unbindService(internetHelperConnection); + contextWrapper.getApplicationContext().unbindService(internetHelperConnection); internetHelperBound = false; } currentRunningUUID = null; @@ -207,34 +237,34 @@ public class WebViewSingleton { webView.loadUrl("about:blank"); // webView.freeMemory(); webView.pauseTimers(); -// instance.destroy(); -// instance = null; +// webView.destroy(); +// webView = null; // contextWrapper = null; // jsInterface = null; } }); } - public static void invokeWebview(final WebViewRunnable runnable) { - if (webViewSingleton.instance == null || webViewSingleton.mainLooper == null) { + public void invokeWebview(final WebViewRunnable runnable) { + if (webView == null || mainLooper == null) { LOG.warn("Webview already disposed, ignoring runnable"); return; } - new Handler(webViewSingleton.mainLooper).post(new Runnable() { + new Handler(mainLooper).post(new Runnable() { @Override public void run() { - if (webViewSingleton.instance == null) { + if (webView == null) { LOG.warn("Webview already disposed, cannot invoke runnable"); return; } - runnable.invoke(webViewSingleton.instance); + runnable.invoke(webView); } }); } public interface WebViewRunnable { /** - * Called in the main thread with a non-null webView instance + * Called in the main thread with a non-null webView webView * @param webView the webview, never null */ void invoke(WebView webView); diff --git a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java index e93d9f628..b58560c73 100644 --- a/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java +++ b/app/src/main/java/ru/gelin/android/weather/notification/ParcelableWeather2.java @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2017 Andreas Shimokawa, Daniele Gobbetti +/* Copyright (C) 2015-2018 Andreas Shimokawa, Daniele Gobbetti, Taavi Eomäe This file is part of Gadgetbridge. @@ -56,6 +56,10 @@ public class ParcelableWeather2 implements Parcelable { conditionBundle.getStringArray("weather_condition_types"); weatherSpec.currentTemp = conditionBundle.getInt("weather_current_temp"); + weatherSpec.windDirection = mapDirToDeg(conditionBundle.getString("weather_wind_direction")); + weatherSpec.windSpeed = getSpeedInKMH(conditionBundle.getInt("weather_wind_speed"), + conditionBundle.getString("weather_wind_speed_unit")); + String[] currentConditionType = conditionBundle.getStringArray("weather_condition_types"); if (currentConditionType != null) { weatherSpec.currentConditionCode = weatherConditionTypesToOpenWeatherMapIds(currentConditionType[0]); @@ -120,7 +124,7 @@ public class ParcelableWeather2 implements Parcelable { } catch (JSONException e) { LOG.error("error while construction JSON", e); } - LOG.debug("Forecast JSON for WEBVIEW: " + reconstructedOWMForecast.toString()); + LOG.debug("Forecast JSON for Webview: " + reconstructedOWMForecast.toString()); } } @@ -254,4 +258,28 @@ public class ParcelableWeather2 implements Parcelable { return 3200; } -} + private int getSpeedInKMH(int speed, String incomingUnit) { + float kmhSpeed = 0; + switch (incomingUnit) { + case "MPS": + kmhSpeed = speed * 3.6f; + break; + case "MPH": + kmhSpeed = speed * 1.6093f; + break; + case "KPH": + kmhSpeed = speed; + break; + } + return Math.round(kmhSpeed); + } + + private int mapDirToDeg(String dir) { + return Math.round(WindDirection.valueOf(dir).ordinal() * 22.5f); + } + + private enum WindDirection { // see upstream code, we can't be more precise than getting the quadrant + N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW + } + +} \ No newline at end of file diff --git a/app/src/main/res/color/blacklist_checkboxes.xml b/app/src/main/res/color/blacklist_checkboxes.xml new file mode 100644 index 000000000..df27b113b --- /dev/null +++ b/app/src/main/res/color/blacklist_checkboxes.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_biking.xml b/app/src/main/res/drawable/ic_activity_biking.xml new file mode 100644 index 000000000..7001d100b --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_biking.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_deep_sleep.xml b/app/src/main/res/drawable/ic_activity_deep_sleep.xml new file mode 100644 index 000000000..abdaea833 --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_deep_sleep.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_light_sleep.xml b/app/src/main/res/drawable/ic_activity_light_sleep.xml new file mode 100644 index 000000000..abdaea833 --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_light_sleep.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_not_measured.xml b/app/src/main/res/drawable/ic_activity_not_measured.xml new file mode 100644 index 000000000..7a6bb2d01 --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_not_measured.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_running.xml b/app/src/main/res/drawable/ic_activity_running.xml new file mode 100644 index 000000000..14c06266a --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_running.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_tracks.xml b/app/src/main/res/drawable/ic_activity_tracks.xml new file mode 100644 index 000000000..14c06266a --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_tracks.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_unknown.xml b/app/src/main/res/drawable/ic_activity_unknown.xml new file mode 100644 index 000000000..4e323aeaa --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_unknown.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_activity_walking.xml b/app/src/main/res/drawable/ic_activity_walking.xml new file mode 100644 index 000000000..831c0b345 --- /dev/null +++ b/app/src/main/res/drawable/ic_activity_walking.xml @@ -0,0 +1,7 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_block_black.xml b/app/src/main/res/drawable/ic_block_black.xml new file mode 100644 index 000000000..4510bf5bb --- /dev/null +++ b/app/src/main/res/drawable/ic_block_black.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete_cross.xml b/app/src/main/res/drawable/ic_delete_cross.xml new file mode 100644 index 000000000..ab38bb6d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_cross.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..34ddd53d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_reset.xml b/app/src/main/res/drawable/ic_reset.xml new file mode 100644 index 000000000..de25eb445 --- /dev/null +++ b/app/src/main/res/drawable/ic_reset.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml new file mode 100644 index 000000000..0fc49c923 --- /dev/null +++ b/app/src/main/res/drawable/ic_select_all.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..904066636 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/level_list_device.xml b/app/src/main/res/drawable/level_list_device.xml index 33f1b885b..4f4fcd9f8 100644 --- a/app/src/main/res/drawable/level_list_device.xml +++ b/app/src/main/res/drawable/level_list_device.xml @@ -6,6 +6,7 @@ + @@ -19,6 +20,7 @@ + diff --git a/app/src/main/res/layout/activity_alarm_details.xml b/app/src/main/res/layout/activity_alarm_details.xml index 165ca836b..2e7bdcbb5 100644 --- a/app/src/main/res/layout/activity_alarm_details.xml +++ b/app/src/main/res/layout/activity_alarm_details.xml @@ -1,115 +1,132 @@ - - + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true"> - - - + - + android:scaleX="0.7" + android:scaleY="0.7" + android:layout_marginBottom="20dp" /> - + android:baselineAligned="false" + android:orientation="horizontal"> - + - + - + - + - + - + + + + + + + + + diff --git a/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml b/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml index 533fad9af..97effba5c 100644 --- a/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml +++ b/app/src/main/res/layout/activity_controlcenterv2_app_bar_main.xml @@ -31,10 +31,7 @@ android:layout_alignParentEnd="true" android:layout_gravity="bottom|end" app:srcCompat="@drawable/ic_add" - app:elevation="6dp" - app:pressedTranslationZ="12dp" - android:layout_marginBottom="30dp" - android:layout_marginEnd="10dp" /> + android:layout_margin="16dp" /> diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index 0a8a7e76d..32aa9a386 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -102,7 +102,7 @@ android:text="set music info" />