1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-16 18:34:03 +02:00

Merge branch 'master' into background-javascript

This commit is contained in:
Andreas Shimokawa 2017-05-09 14:04:33 +02:00
commit 3751273cd0
69 changed files with 1360 additions and 808 deletions

View File

@ -1,6 +1,25 @@
###Changelog
### Changelog
###Version 0.18.5
#### Version 0.19.2
* Pebble: Fix recurring calendar events only appearing once per week
* HPlus: Fix crash when receiving calls without phone number
* HPlus: Detect unicode support on Zeband Plus
* No longer quit Gadgetbridge when bluetooth gets turned off
#### Version 0.19.1
* Fix crash at startup
* HPlus: Improve reconnection to device
* Improve transliteration
#### Version 0.19.0
* Pebble: allow calendar sync with Timeline (Title, Location, Description)
* Pebble: display calendar icon for reminders from AOSP Calendar
* HPlus: try to fix latin characters showing as random Chinese text
* Improve reconnection with BLE devices
* Improve generic notification reliability by trying to restart the notification listener when stale/crashed
* Other small bugfixes
#### Version 0.18.5
* Applied some material design guidelines to Charts and (pebble) app management
* Changed colours: deep sleep is now dark blue, light sleep is now light blue
* Support for exporting and importing of preferences in addition to the database
@ -11,24 +30,24 @@
* HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642)
* HPlus: display battery state and warn on low battery
###Version 0.18.4
#### Version 0.18.4
* Mi Band 2: Display realtime steps in Live Activity
* Mi Band: Attempt to recognize Mi Band model with hwVersion = 8
* Alarms activity improvements and fixes
* Make Buttons in the main activity easier to hit
###Version 0.18.3
#### Version 0.18.3
* Fix bug that caused the same value in weekly charts for every day on Android 6 and older
###Version 0.18.2
#### Version 0.18.2
* Mi Band 2: Fix crash on "chat" or "social network" text notification (#603)
###Version 0.18.1
#### Version 0.18.1
* Pebble: Fix Firmware insstallation on Pebble Time Round (broken since 0.16.0)
* Start VibrationActivity when using "find device" button with Vibratissimo
* Support material fork of K9
###Version 0.18.0
#### Version 0.18.0
* All new GUI for the control center
* Add Portuguese pt_PT and pt_BR translations
* Add Czech translation
@ -44,13 +63,13 @@
* Mi Band 2: Set 12h/24h time format, following the Android configuration (#573)
* Improved BLE discovery and connectivity
####Version 0.17.5
#### Version 0.17.5
* Automatically start the service on boot (can be turned off)
* Pebble: PebbleKit compatibility improvements (Datalogging)
* Pebble: Display music shuffle and repeat states for some players
* Pebble 2/LE: Speed up data transfer
####Version 0.17.4
#### Version 0.17.4
* Better integration with android music players
* Privacy options for calls (hide caller name/number)
* Send a notification to the connected if the Android Alarm Clock rings (com.android.deskclock)
@ -63,23 +82,23 @@
* HPlus: Add device specific preferences and icon
* HPlus: Support for Makibes F68
####Version 0.17.3
#### 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: 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)
####Version 0.17.2
#### Version 0.17.2
* Pebble: Fix temperature unit in Timestyle Pebble watchface
* Add optional Cyrillic transliteration (for devices lacking the font)
####Version 0.17.1
#### Version 0.17.1
* Pebble: Fix installation of some watchapps
* Pebble: Try to improve PebbleKit compatibility
* HPlus: Fix bug setting current date
####Version 0.17.0
#### Version 0.17.0
* Add weather support through "Weather Notification" app
* Various fixes for K9 mail when using the generic notification receiver
* Add a preference to hide the persistent notification icon of Gadgetbridge
@ -97,7 +116,7 @@
* HPlus: Experimental synchronization of activity data (only sleep, steps and intensity)
* HPlus: Fix some disconnection issues
####Version 0.16.0
#### Version 0.16.0
* New devices: HPlus (e.g. Zeblaze ZeBand), contributed by João Paulo Barraca
* ZeBand: Initial support: notifications, heart rate, sleep monitoring, user configuration, date+time
* Pebble 2: Fix Pebble Classic FW 3.x app variant being prioritized over native Pebble 2 app variant
@ -108,15 +127,15 @@
* Support sharing firmwares/watchapps/watchfaces to Gadgetbridge
* Support for the "Subsonic" music player (#474)
####Version 0.15.2
#### Version 0.15.2
* Mi Band: Fix crash with unknown notification sources
####Version 0.15.1
#### Version 0.15.1
* Improved handling of notifications for some apps
* Pebble 2/LE: Add setting to limit GATT MTU for debugging broken BLE stacks
* Mi Band 2: Display battery status
####Version 0.15.0
#### Version 0.15.0
* New device: Liveview
* Liveview: initial support (set the time and receive notifications)
* Pebble: log pebble app logs if option is enabled in pebble development settings
@ -124,20 +143,20 @@
* Pebble: Further improve compatibility for watchface configuration
* Mi Band 2: Initial support for firmware update (tested so far: 1.0.0.39)
####Version 0.14.4
#### Version 0.14.4
* Pebble 2/LE: Fix multiple bugs in reconnection code, honor reconnect tries from settings
* Mi Band 2: Experimental support for activity recognition
* Mi Band 2: Fix time setting code
####Version 0.14.3
#### Version 0.14.3
* Pebble: Experimental support for pairing and using all Pebble models via BLE
* Mi Band 1: Fix regression causing display of wrong activity data (#440)
* Mi Band 2: Support for continuous heart rate measurements in live activity view
####Version 0.14.2
#### Version 0.14.2
* Pebble 2: Fix a bug where the Pebble got disconnected by other unrelated LE devices
####Version 0.14.1
#### Version 0.14.1
* Mi Band 2: Initial experimental support for activity data
* Mi Band 2: Send the fitness goal (steps) to the band
* Pebble 2: Work around firmware installation issues (tested with upgrading 4.2 to 4.3)
@ -145,7 +164,7 @@
* Pebble: add Kickstart watch face to app manager on FW 4.x
* Charts: display the total time range, not just the range with available data
####Version 0.14.0
#### Version 0.14.0
* Pebble 2: Initial experimental support for P2/PT2 using BLE
* Pebble: Special support in device discovery activity (MUST be used to get Pebble 2 working)
* Pebble: Improve compatibility for watchface configuration
@ -154,16 +173,16 @@
* Mi Band 2: configuration option to display the time + date or just the time
* Mi Band 2: honor the wear location configuration option
####Version 0.13.9
#### Version 0.13.9
* Pebble: use the last known location for setting sunrise and sunset
* Pebble: fix Health disappearing forever when deactivating through app manager (and get it back for affected users)
* Mi Band 2: More fixes for connection issues (#408)
####Version 0.13.8
#### Version 0.13.8
* Mi Band 2: fix connection issues for users of Mi Fit (#408, #425)
* Mi Band 1A: fix firmware update for certain 1A models
####Version 0.13.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
* Mi Band: allow to delete Mi Band address from development settings
@ -172,16 +191,16 @@
* Attempt to fix spurious device discovery problems
* Correctly recognize Toffeed, Slimsocial and MaterialFBook as facebook notification sources
####Version 0.13.6
#### Version 0.13.6
* Mi Band 2: Support for multiple alarms (3 at the moment)
* Mi Band 2: Fix for alarms not working when just one is enabled
####Version 0.13.5
#### Version 0.13.5
* Mi Band 2: Support setting one alarm
* Pebble: Health compatibility for Firmware 4.2
* Improve support for K9 when generic notifications are used (K9 notifications set to never)
####Version 0.13.4
#### Version 0.13.4
* Mi Band: Initial support for recording heart and displaying rate values
* Mi Band: Support for testing vibration patterns directly from the preferences
* Mi Band: Clean up vibration preferences
@ -192,36 +211,36 @@
* Pebble: new icons and colours for certain apps
* Debug-screen: added button to test "new functionality", currently live sensor data for Mi Band 1
####Version 0.13.3
#### Version 0.13.3
* Fix regressions with missing bars and labels in charts
* Allow to set notification type in Debug activity
* Move "Disconnect" back to the bottom of the context menu
* Mi Band 2: Display Message and Phone icons
####Version 0.13.2
#### Version 0.13.2
* Support deleting devices (and their data) in control center
* Sort devices lexicographically in control center
* Do not forward group summary notifications (could fix some duplicate notifications)
* Pebble: Support for health on FW 4.1
* Mi Band: Fix offline charts not displaying heartrate for Mi 1S
####Version 0.13.1
#### Version 0.13.1
* Improved BLE scanning for Android 5.0+
* Pebble: try to work around duplicate Telegram messages and support Telegram icon
* Pebble: fix some incompatibilities with certain PebbleKit Android apps
####Version 0.13.0
#### Version 0.13.0
* Initial working Mi Band 2 support (only notifications, no activity and heart rate support)
* Experimental support for Vibratissimo devices
####Version 0.12.2
#### Version 0.12.2
* Fix for user attribute database table getting spammed and store sleep and steps goals properly
####Version 0.12.1 (release withdrawn)
#### Version 0.12.1 (release withdrawn)
* Pebble: Fix activity data being associated with the wrong device and/or user in some cases causing them to invisible in charts
* Remove special handling for Conversations notifications since upstream dropped special pebble support
####Version 0.12.0 (release withdrawn)
#### Version 0.12.0 (release withdrawn)
* NB: User action needed to migrate existing data!
* Store activity data per device and provider to allow multiple devices of the same kind with separate data. Migration is available, except for Pebble Misfit data. Existing data from multiple devices of the same kind (eg. multiple Mi Bands) will get merged while importing.
* In Control Center, display known devices even when Bluetooth is off
@ -230,10 +249,10 @@
* Pebble: Optionally allow raw Pebble Health data to be stored in database completely (for later interpretation, when we are able to decode it)
* Mi Band: fix displaying of deep sleep vs. light sleep (was inverted)
####Version 0.11.2
#### Version 0.11.2
* Mi Band: support for devices that cannot pair with the band (#349)
####Version 0.11.1
#### Version 0.11.1
* Various fixes (including crashes) for location settings
* Pebble: Support Pebble Time 2 emulator (needs recompilation of Gadgetbridge)
* Fix a rare crash when, due to Bluetooth problems, when a device has no name
@ -246,19 +265,19 @@
* Charts: only display heart rate samples on devices that support that
* Add more logging to detect problems with external directories (#343)
####Version 0.11.0
#### Version 0.11.0
* Pebble: new App Manager (keeps track of installed apps and allows app sorting on FW 3.x)
* Pebble: call dismissal with canned SMS (FW 3.x)
* Pebble: watchapp configuration presets
* Pebble: fix regression with FW 2.x (almost everything was broken in 0.10.2)
####Version 0.10.2
#### Version 0.10.2
* Pebble: allow to manually paste configuration data for legacy configuration pages
* Pebble: various improvements to the configuration page
* Pebble: Support FW 4.0-dp1 and Pebble2 emulator (needs recompilation of Gadgetbridge)
* Pebble: Fix a problem with key events when using the Pebble music player
####Version 0.10.1
#### Version 0.10.1
* Pebble: set extended music info by dissecting notifications on Android 5.0+
* Pebble: various other improvements to music playback
* Pebble: allow ignoring activity trackers individually (to keep the data on the pebble)
@ -266,7 +285,7 @@
* Mi Band: initial and untested support for Mi Band 2
* Allow setting the application language
####Version 0.10.0
#### Version 0.10.0
* Pebble: option to send sunrise and sunset events to timeline
* Pebble: fix problems with unknown app keys while configuring watchfaces
* Mi Band: BLE connection fixes
@ -274,7 +293,7 @@
* Re-enable device paring activity on Android 6 (BLE scanning needs the location preference)
* Display device address in device info
####Version 0.9.8
#### Version 0.9.8
* Pebble: fix more reconnect issues
* Pebble: fix deep sleep not being detected with Firmware 3.12 when using Pebble Health
* Pebble: option in AppManager to delete files from cache
@ -283,7 +302,7 @@
* Pebble: fix music information being messed up
* Honour "Do Not Disturb" for phone calls and SMS
####Version 0.9.7
#### Version 0.9.7
* Pebble: hopefully fix some reconnect issues
* Mi Band: fix live activity monitoring running forever if back button pressed
* Mi Band: allow low latency firmware updates, fixes update with some phones
@ -291,14 +310,14 @@
* Show aliases for BT Devices if they had been renamed in BT Settings
* Do not show a hint about App Manager when a Mi Band is connected
####Version 0.9.6
#### Version 0.9.6
* Again some UI/theme improvements
* New preference to reconnect after connection loss (defaults to true)
* Fix crash when dealing with certain old preference values
* Mi Band: automatically reconnect when back in range after connection loss
* Mi Band 1S: display heart rate value again when invoked via the Debug view
####Version 0.9.5
#### Version 0.9.5
* Several UI Improvements
* Easier First-time setup by using a FAB
* Optional Dark Theme
@ -308,7 +327,7 @@
* Mi Band 1S: Initial live heartrate tracking
* Fix certain crash in charts activity on slower devices (#277)
####Version 0.9.4
#### Version 0.9.4
* Pebble: support pebble health datalog messages of firmware 3.11 (this adds support for deep sleep!)
* Pebble: try to reconnect on new notifications and phone calls when connection was lost unexpectedly
* Pebble: delay between reconnection attempts (from 1 up to 64 seconds)
@ -318,24 +337,24 @@
* Mi Band 1S: full support for firmware upgrade/downgrade (both for Mi Band and heart rate sensor) (#234)
* Mi Band 1S: fix device detection for certain versions
####Version 0.9.3
#### Version 0.9.3
* Pebble: Fix Pebble Health activation (was not available in the App Manager)
* Simplify connection state display (only connecting->connected)
* Small improvements to the pairing activity
* Mi Band 1S: Fix for mi band firmware update
####Version 0.9.2
#### Version 0.9.2
* Mi Band: Fix update of second (HR) firmware on Mi1S (#234)
* Fix ordering issue of device infos being displayed
####Version 0.9.1
#### Version 0.9.1
* Mi Band: fix sporadic connection problems (stuck on "Initializing" #249)
* Mi Band: enable low latency connection (faster) during initialization and activity sync
* Mi Band: better feedback for firmware update
* Device Item is now clickable also when the information entries are visible
* Fix enabling log file writing #261
####Version 0.9.0
#### Version 0.9.0
* Pebble: Support for configuring watchfaces/apps locally (clay) or though webbrowser (some do not work)
* Pebble: hide the alarm management activity as it's unsupported
* Mi Band: Improve firmware detection and updates, including 1S support
@ -344,19 +363,19 @@
* Do not display activity samples when navigating too far in the past
* Fix auto connect which was broken under some circumstances
####Version 0.8.2
#### Version 0.8.2
* Fix database creation and updates (thanks @feclare)
* Add experimental widget to set the alarm time to a configurable number of hours in the future (thanks @0nse)
* Use ckChangeLog to display the Changelog within Gadgetbridge
* Workaround to fix logfile rotation (bug in logback-android)
####Version 0.8.1
#### Version 0.8.1
* Pebble: install (and start) freshly-installed apps on the watch instead of showing a Toast that tells the user to do so. (only applies to firmware 3.x)
* Pebble: fix crash while receiving Health data
* Mi Band 1S: support for synchronizing activity data (#205)
* Mi Band 1S: support for reading the heart rate via the "Debug Screen" #178
####Version 0.8.0
#### Version 0.8.0
* Pebble: Support Pebble Health: steps/activity data are stored correctly. Sleep time is considered as light sleep. Deep sleep is discarded. The pebble will send data where it seems appropriate, there is no action to perform on the watch for this to happen.
* Pebble: Fix support for newer version of morpheuz (>=3.3?)
* Pebble: Allow to select the preferred activity tracker via settings activity (Health, Misfit, Morpheuz)
@ -366,17 +385,17 @@
* Very basic support Android 6 runtime permission
* Fix layout of the alarms activity
####Version 0.7.4
#### Version 0.7.4
* Refactored the settings activity: User details are now generic instead of miband specific. Old settings are preserved.
* Pebble: Fix regression with broken active reconnect since 0.7.0
* Pebble: Support activation and deactivation of Pebble Health. Activation uses the User details as seen above. Insights are NOT activated.
Please be aware that deactivation does NOT delete the data stored on the watch (but it seems to stop the tracking), and we do not know how to switch to metric length units.
####Version 0.7.3
#### Version 0.7.3
* Pebble: Report connection state to PebbleKit companion apps via content provider. NOTE: Makes Gadgetbridge mutual exclusive with the original Pebble app.
* Ignore generic notification when from SMSSecure when SMS Notifications are on
####Version 0.7.2
#### Version 0.7.2
* Pebble: Allow replying to generic notifications that contain a wearable reply action (tested with Signal)
* Pebble: Support setting up a common suffix for canned replies (defaults to " (canned reply)")
* Mi Band: Avoid NPEs when aborting an erroneous sync #205
@ -384,11 +403,11 @@
* Add a confirmation dialog when performing a db import
* Sort blacklist by package names
####Version 0.7.1
#### Version 0.7.1
* Pebble: allow reinstallation of apps in pbw-cache from App Manager (long press menu)
* Pebble: Fix regression which freezes Gadgetbridge when disconnecting via long-press menu
####Version 0.7.0
#### Version 0.7.0
* Read upcoming events (up to 7 days in the future). Requires READ_CALENDAR permission
* Fix double SMS on Sony Android and Android 6.0
* Pebble: Support replying to SMS form the watch (canned replies)
@ -400,7 +419,7 @@
* Mi Band: Display unique devices Names, not just "MI"
* Some new and updated icons
####Version 0.6.9
#### Version 0.6.9
* Pebble: Store app details in pbw-cache and display them in app manager on firmware 3.x
* Pebble: Increase maximum notification body length from 255 to 512 bytes on firmware 3.x
* Pebble: Support installing .pbl (language files) on firmware 3.x
@ -413,7 +432,7 @@
* Mi Band: KitKat: hopefully fixed showing the progress bar during activity data synchronization (#155)
* Mi Band 1S: hopefully fixed connection errors (#178) Notifications probably do not work yet, though
####Version 0.6.8
#### Version 0.6.8
* Mi Band: support for Firmware upgrade/downgrade on Mi Band 1A (white LEDs, no heartrate sensor)
* Pebble: fix regression in 0.6.7 when installing pbw/pbz files from content providers (eg. download manager)
* Pebble: fix installation of pbw files on firmware 3.x when using content providers (eg. download manager)
@ -421,26 +440,26 @@
+ Treat Signal notifications as chat notifications
* Fix crash when contacts cannot be read on Android 6.0 (non-granted permissions)
####Version 0.6.7
#### Version 0.6.7
* Pebble: Allow installation of 3.x apps on OG Pebble (FW will be released soon)
* Fix crashes on startup when logging is enabled or when entering the app manager on some phones
+ Fix Pebble being detected as MI when unpaired and autoconnect is enabled
* Fix Crash when not having K9 Mail permissions (happens when installing K9 after Gadgetbridge) (#175)
####Version 0.6.6
#### Version 0.6.6
* Mi Band: Huge performance improvement fetching activity data
* Mi Band: attempt at fixing connection problems (#156)
* Pebble: Try to interpret sleep data from Misfit data
* Fix exporting the activity database on devices with read-only external storage (#153)
* Fix totally wrong sleep time in the sleep chart
####Version 0.6.5
#### Version 0.6.5
* Mi Band: Support "Locate Device" with Mi Band 1A (and Mi Band 1 with new firmware)
* Pebble: Support syncing steps from Misfit (untested features must be turned on to see them), intensity=steps, no sleep support yet
* Disable activity fetching when not supported
* Small improvements to live activity charts
####Version 0.6.4
#### Version 0.6.4
* Support pull down to synchronize activity data (#138)
* Display tabs in the Charts activity (#139)
* Mi Band: initial support for Mi Band 1a (the one with white LEDs) (thanks @sarg) (#136)
@ -448,23 +467,23 @@
* Register/unregister BroadcastReceivers instead of enabling/disabling them with PackageManager (#134)
(should fix disconnection because the service is being killed)
####Version 0.6.3
#### Version 0.6.3
* Pebble: support installation of language files (.pbl) on FW 2.x
* Try to prevent service being killed by disallowing backups
####Version 0.6.2
#### Version 0.6.2
* Mi Band: support firmware version 1.0.10.14 (and onwards?) vibration
* Mi Band: get device name from official BT SIG endpoint
* Mi Band: initial support for displaying live activity data, screen stays on
####Version 0.6.1
#### Version 0.6.1
* Pebble: Allow muting (blacklisting) Apps from within generic notifications on the watch
* Pebble: Detect all known Pebble Versions including new "chalk" platform (Pebble Time Round)
* Option to ignore phone calls (useful for Pebble Dialer)
* Mi Band: Added progressbar for activity data transfer and fixes for firmware transfer progressbar
* Bugfix for app blacklist (some checkboxes where wrongly drawn as checked)
####Version 0.6.0
#### Version 0.6.0
* Pebble: WIP implementation of PebbleKit Intents to make some 3rd party Android apps work with the Pebble (eg. Ventoo)
* Pebble: Option to set reconnection attempts in settings (one attempt usually takes about 5 seconds)
* Support controlling all audio players that react to media buttons (can be chosen in settings)
@ -473,13 +492,13 @@
* Allow opening firmware / app files from the download manager "app" (technically a content provider)
* Mi Band: whitelisted a few firmware versions
####Version 0.5.4
#### Version 0.5.4
* Mi Band: allow the transfer of activity data without clearing MiBand's memory
* Pebble: for generic notifications use generic icon instead of SMS icons on FW 3.x (thanks @roidelapluie)
* Pebble: use different icons and background colors for specific groups of applications (chat, mail, etc) (thanks @roidelapluie)
* In settings, support blacklisting apps for generic notifications
####Version 0.5.3
#### Version 0.5.3
* Pebble: For generic notifications, support dismissing individual notifications and "Open on Phone" feature (OG & PT)
* 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
@ -487,7 +506,7 @@
* Device state again visible on lockscreen
* Date display and navigation now working properly for all charts
####Version 0.5.2
#### Version 0.5.2
* Pebble: support "dismiss all" action also on Pebble Time/FW 3.x notifications
* Mi Band: show a notification when the battery is below 10%
* Graphs are now using the same theme as the rest of the application
@ -495,11 +514,11 @@
* Remove unused settings option in charts view
* Build target is now Android SDK 23 (Marshmallow)
####Version 0.5.1
#### Version 0.5.1
* Pebble: support taking screenshot from Pebble Time
* Fix broken "find lost device" which was broken in 0.5.0
####Version 0.5.0
#### Version 0.5.0
* Mi Band: fix setting wear location
* Pebble: experimental watchapp installation support for FW 3.x/Pebble Time
* Pebble: support Pebble emulator via TCP connection (needs rebuild with INTERNET permission)
@ -508,7 +527,7 @@
* Support going forward/backwards in time in the activity charts
* Various small bugfixes to the App/FW Installation Activity
####Version 0.4.6
#### Version 0.4.6
* Mi Band: Fixed negative number of steps displayed (#91)
* Mi Band: fixed (re-) connection problems after band getting disconnected
* Pebble: new option to enable untested code (enable only if you like bad surprises)
@ -518,7 +537,7 @@
* Small firmware installation improvements
* Various refactorings and code cleanups
####Version 0.4.5
#### Version 0.4.5
* Enhancement to activity graphs: new graph showing the number of steps done today and in the last week
* New preference to set the desired fitness goal (number of steps to walk in one day)
* Mi Band: support for setting the fitness goal (the band will show the progress to the goal with the LEDs and vibrates when the goal is reached)
@ -526,7 +545,7 @@
* Mi Band: support for flashing firmware from .fw files (upgrades and downgrades are possible)
* Fixed crash when synchronizing activity data in the graphs activity and changing device orientation
####Version 0.4.4
#### Version 0.4.4
* Set Gadgetbridge notification visibility to public, to show the connection status on the lockscreen
* Support for backup up and restoring of the activity database (via Debug activity)
* Support for graceful upgrades and downgrades, keeping your activity database intact
@ -536,24 +555,24 @@
* Pebble: make FW 3.x notifications available by default
* Mi Band: Set the graphs activity as the default action available with a single tap on the connected device
####Version 0.4.3
#### Version 0.4.3
* Mi Band: Support for setting alarms
* Mi Band: Bugfix for activity data synchronization
####Version 0.4.2
#### Version 0.4.2
* Material style for Lollipop
* Support for finding a lost device (vibrate until cancelled)
* Mi Band: Support for vibration profiles, configurable for notifications
* Pebble: Support taking screenshots from the device context menu (Pebble Time not supported yet)
####Version 0.4.1
#### Version 0.4.1
* New icons, thanks xphnx!
* Improvements to Sleep Monitor charts
* Pebble: use new Sleep Monitor for Morpheuz (previously Mi Band only)
* Pebble: experimental support for FW 3.x notification protocol
* Pebble: dev option to force latest notification protocol
####Version 0.4.0
#### Version 0.4.0
* Pebble: Initial Morpheuz protocol support for getting sleep data
* Pebble: Support launching of watchapps though the AppManager Activity
* Pebble: Support CM 12.1 default music app (Eleven)
@ -567,33 +586,33 @@
* Fix Debug activity (SMS and E-Mail buttons were broken)
* Add Turkish translation contributed by Tarik Sekmen
####Version 0.3.5
#### Version 0.3.5
* Add discovery and pairing Activity for Pebble and Mi Band
* Listen for Pebble Message Intents and forward notifications (used by Conversations)
* Make strings translatable and add German, Italian, Russian, Spanish and Korean translations
* Mi Band: Display battery status
####Version 0.3.4
#### Version 0.3.4
* Pebble: Huge speedup for app/firmware installation.
* Pebble: Use a separate notification with progress bar for installation procedure
* Pebble: Bugfix for being stuck while waiting for a slot, when none is available
* Mi Band: Display connection status in notification (previously Pebble only)
####Version 0.3.3
#### Version 0.3.3
* Pebble: Try to reduce battery usage by acknowledging datalog packets
* Mi Band: Set current time on the device (thanks to PR by @danielegobbetti)
* More robust connection state handling and display
####Version 0.3.2
#### Version 0.3.2
* Mi Band: Fix for notifications only working after manual connection
* Mi Band: Display firmware version
* Pebble: Display hardware revision
* Pebble: Check if firmware is compatible before allowing installation
####Version 0.3.1
#### Version 0.3.1
* Mi Band: Fix for notifications only working in Debug
####Version 0.3.0
#### Version 0.3.0
* Mi Band: Initial support (see README.md)
* Pebble: Firmware installation (USE AT YOUR OWN RISK)
* Pebble: Fix installation problems with certain .pbw files
@ -601,7 +620,7 @@
* Add icon for activity tracker apps (icon by xphnx)
* Let the application quit when in reconnecting state
####Version 0.2.0
#### Version 0.2.0
* Experimental pbw installation support (watchfaces/apps)
* New icons for device and app lists
* Fix for device list not refreshing when Bluetooth gets turned on
@ -609,31 +628,31 @@
* Fix for crash on some devices when creating a debug notification
* Lots of internal changes preparing multi device support
####Version 0.1.5
#### Version 0.1.5
* Fix for DST (summer time)
* Option to sync time on connect (enabled by default)
* Opening .pbw files with Gadgetbridge prints some package information
(This was not meant to be released yet, but the DST fix made a new release necessary)
####Version 0.1.4
#### Version 0.1.4
* New AppManager shows installed Apps/Watchfaces (removal possible via context menu)
* Allow back navigation in ActionBar (Debug and AppMananger Activities)
* Make sure Intent broadcasts do not leave Gadgetbridge
* Show hint in the Main Activity (tap to connect etc)
####Version 0.1.3
#### Version 0.1.3
* Remove the connect button, list all supported devices and connect on tap instead
* Display connection status and firmware of connected devices in the device list
* Remove quit button from the service notification, put a quit item in the context menu instead
####Version 0.1.2
#### Version 0.1.2
* Added option to start Gadgetbridge and connect automatically when Bluetooth is turned on
* stop service if Bluetooth is turned off
* try to reconnect if connection was lost
####Version 0.1.1
#### Version 0.1.1
* Fixed various bugs regarding K-9 Mail notifications.
* "Generic notification support" in Setting now opens Androids "Notification access" dialog.
####Version 0.1.0
#### Version 0.1.0
* Initial release

View File

@ -2,7 +2,7 @@
names ()
{
echo -e "\n exit;\n**Contributors (sorted by number of commits):**\n";
git log --format='%aN:%aE' origin/master | sed 's/@users.github.com/@users.noreply.github.com/g' | awk 'BEGIN{FS=":"}{ct[$2]+=1;if (length($1) > length(e[$2])) {e[$2]=$1}}END{for (i in e) { n[e[i]]=i;c[e[i]]+=ct[i] }; for (a in n) print c[a]"\t* "a" <"n[a]">";}' | sort -n -r | cut -f 2-
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-
}
quine ()
{
@ -33,14 +33,17 @@
* Sergey Trofimov <sarg@sarg.org.ru>
* JohnnySun <bmy001@gmail.com>
* Uwe Hermann <uwe@hermann-uwe.de>
* Alberto <albertsal83@gmail.com>
* 0nse <0nse@users.noreply.github.com>
* Gergely Peidl <gergely@peidl.net>
* Christian Fischer <sw-dev@computerlyrik.de>
* 6arms1leg <m.brnsfld@googlemail.com>
* walkjivefly <mark@walkjivefly.com>
* Normano64 <per.bergqwist@gmail.com>
* Avamander <Avamander@users.noreply.github.com>
* Ⲇⲁⲛⲓ Φi <daniphii@outlook.com>
* Yar <yaroslav.isakov@gmail.com>
* Yaron Shahrabani <sh.yaron@gmail.com>
* xzovy <caleb@caleb-cooper.net>
* xphnx <xphnx@users.noreply.github.com>
* Tarik Sekmen <tarik@ilixi.org>
@ -57,6 +60,7 @@
* Hasan Ammar <ammarh@gmail.com>
* Gilles MOREL <contact@gilles-morel.fr>
* Gilles Émilien MOREL <Almtesh@users.noreply.github.com>
* Daniel Hauck <maill@dhauck.eu>
* Chris Perelstein <chris.perelstein@gmail.com>
* Carlos Ferreira <calbertoferreira@gmail.com>
* atkyritsis <at.kyritsis@gmail.com>

View File

@ -26,13 +26,12 @@ need to create an account and transmit any of your data to the vendor's servers.
* Incoming calls notification and display
* Outgoing call display
* Reject/hangup calls
* Reject calls (optionally with predefined texts) / hangup calls
* SMS notification
* K-9 Mail notification support
* Support for generic notifications (above filtered out)
* Support for up to 16 predefined replies for SMS and Android Wear compatible notifications (experimental, tested with Signal)
* Support for generic notifications
* Support for up to 16 predefined replies for SMS and Android Wear compatible notifications (experimental, tested with Signal and Conversations)
* Dismiss individual notifications, mute or open corresponding app on phone from the action menu (generic notifications)
* Dismiss all notifications from the action menu (non-generic notifications)
* Dismiss all notifications from the action menu (SMS and PebbleKit notifications)
* Music playback info (artist, album, track)
* Music control: play/pause, next track, previous track, volume up, volume down
* List and remove installed apps/watchfaces
@ -41,7 +40,8 @@ need to create an account and transmit any of your data to the vendor's servers.
* Install language files (.pbl)
* Take and share screenshots from the Pebble's screen
* PebbleKit support for 3rd Party Android Apps (experimental)
* Fetch activity data from Pebble Health, Misfit and Morpheuz (experimental)
* Fetch activity data from Pebble Health
* Build-in support for Misfit and Morpheuz (experimental)
* Configure watchfaces / apps (limited compatibility, experimental)
## Notes about Firmware >=3.0 (Pebble Time, updated OG)

View File

@ -26,8 +26,8 @@ android {
targetSdkVersion 25
// note: always bump BOTH versionCode and versionName!
versionName "0.18.5"
versionCode 92
versionName "0.19.2"
versionCode 95
vectorDrawables.useSupportLibrary = true
}
buildTypes {

View File

@ -40,6 +40,7 @@ import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -89,11 +90,18 @@ public class GBApplication extends Application {
public static final String ACTION_QUIT
= "nodomain.freeyourgadget.gadgetbridge.gbapplication.action.quit";
private static GBApplication app;
private static Logging logging = new Logging() {
@Override
protected String createLogDirectory() throws IOException {
File dir = FileUtils.getExternalFilesDir();
return dir.getAbsolutePath();
if (GBEnvironment.env().isLocalTest()) {
return System.getProperty(Logging.PROP_LOGFILES_DIR);
} else {
File dir = FileUtils.getExternalFilesDir();
return dir.getAbsolutePath();
}
}
};
@ -111,12 +119,17 @@ public class GBApplication extends Application {
// don't do anything here, add it to onCreate instead
}
public static Logging getLogging() {
return logging;
}
protected DeviceService createDeviceService() {
return new GBDeviceService(this);
}
@Override
public void onCreate() {
app = this;
super.onCreate();
if (lockHandler != null) {
@ -128,6 +141,13 @@ public class GBApplication extends Application {
prefs = new Prefs(sharedPrefs);
gbPrefs = new GBPrefs(prefs);
if (!GBEnvironment.isEnvironmentSetup()) {
GBEnvironment.setupEnvironment(GBEnvironment.createDeviceEnvironment());
// setup db after the environment is set up, but don't do it in test mode
// in test mode, it's done individually, see TestBase
setupDatabase();
}
// don't do anything here before we set up logging, otherwise
// slf4j may be implicitly initialized before we properly configured it.
setupLogging(isFileLoggingEnabled());
@ -138,10 +158,6 @@ public class GBApplication extends Application {
setupExceptionHandler();
GB.environment = GBEnvironment.createDeviceEnvironment();
setupDatabase(this);
deviceManager = new DeviceManager(this);
createWebViewActivity();
@ -206,8 +222,14 @@ public class GBApplication extends Application {
return prefs.getBoolean("minimize_priority", false);
}
static void setupDatabase(Context context) {
DBOpenHelper helper = new DBOpenHelper(context, DATABASE_NAME, null);
public void setupDatabase() {
DaoMaster.OpenHelper helper;
GBEnvironment env = GBEnvironment.env();
if (env.isTest()) {
helper = new DaoMaster.DevOpenHelper(this, null, null);
} else {
helper = new DBOpenHelper(this, DATABASE_NAME, null);
}
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
if (lockHandler == null) {
@ -325,10 +347,23 @@ public class GBApplication extends Application {
return NotificationManager.INTERRUPTION_FILTER_ALL;
}
public static HashSet<String> blacklist = null;
private static HashSet<String> blacklist = null;
public static boolean isBlacklisted(String packageName) {
return blacklist != null && blacklist.contains(packageName);
}
public static void setBlackList(Set<String> packageNames) {
if (packageNames == null) {
blacklist = new HashSet<>();
} else {
blacklist = new HashSet<>(packageNames);
}
saveBlackList();
}
private static void loadBlackList() {
blacklist = (HashSet<String>) sharedPrefs.getStringSet("package_blacklist", null);
blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
if (blacklist == null) {
blacklist = new HashSet<>();
}
@ -337,16 +372,15 @@ public class GBApplication extends Application {
private static void saveBlackList() {
SharedPreferences.Editor editor = sharedPrefs.edit();
if (blacklist.isEmpty()) {
editor.putStringSet("package_blacklist", null);
editor.putStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
} else {
editor.putStringSet("package_blacklist", blacklist);
editor.putStringSet(GBPrefs.PACKAGE_BLACKLIST, blacklist);
}
editor.apply();
}
public static void addToBlacklist(String packageName) {
if (!blacklist.contains(packageName)) {
blacklist.add(packageName);
if (blacklist.add(packageName)) {
saveBlackList();
}
}
@ -470,4 +504,8 @@ public class GBApplication extends Application {
public DeviceManager getDeviceManager() {
return deviceManager;
}
public static GBApplication app() {
return app;
}
}

View File

@ -20,6 +20,10 @@ package nodomain.freeyourgadget.gadgetbridge;
* Some more or less useful utility methods to aid local (non-device) testing.
*/
public class GBEnvironment {
// DO NOT USE A LOGGER HERE. Will break LoggingTest!
// private static final Logger LOG = LoggerFactory.getLogger(GBEnvironment.class);
private static GBEnvironment environment;
private boolean localTest;
private boolean deviceTest;
@ -41,4 +45,15 @@ public class GBEnvironment {
return localTest;
}
public static synchronized GBEnvironment env() {
return environment;
}
static synchronized boolean isEnvironmentSetup() {
return environment != null;
}
public synchronized static void setupEnvironment(GBEnvironment env) {
environment = env;
}
}

View File

@ -20,7 +20,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBOpenHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -36,7 +35,7 @@ public class LockHandler implements DBHandler {
public LockHandler() {
}
public void init(DaoMaster daoMaster, DBOpenHelper helper) {
public void init(DaoMaster daoMaster, DaoMaster.OpenHelper helper) {
if (isValid()) {
throw new IllegalStateException("DB must be closed before initializing it again");
}
@ -82,7 +81,7 @@ public class LockHandler implements DBHandler {
throw new IllegalStateException("session must be null");
}
// this will create completely new db instances and in turn update this handler through #init()
GBApplication.setupDatabase(GBApplication.getContext());
GBApplication.app().setupDatabase();
}
@Override

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, walkjivefly
This file is part of Gadgetbridge.

View File

@ -21,7 +21,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v4.content.LocalBroadcastManager;
@ -33,8 +32,6 @@ import android.view.MenuItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.IdentityHashMap;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.adapter.AppBlacklistAdapter;
@ -55,8 +52,6 @@ public class AppBlacklistActivity extends GBActivity {
}
};
private IdentityHashMap<ApplicationInfo, String> nameMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -1,5 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
Daniele Gobbetti, walkjivefly
This file is part of Gadgetbridge.

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 0nse, Andreas Shimokawa, Carsten Pfeiffer,
/* Copyright (C) 2015-2017 0nse, Alberto, Andreas Shimokawa, Carsten Pfeiffer,
Daniele Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,3 +1,19 @@
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.adapter;
import android.content.Context;
@ -40,13 +56,13 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
applicationInfoList = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
// sort the package list by label and blacklist status
mNameMap = new IdentityHashMap<ApplicationInfo, String>(applicationInfoList.size());
mNameMap = new IdentityHashMap<>(applicationInfoList.size());
for (ApplicationInfo ai : applicationInfoList) {
CharSequence name = mPm.getApplicationLabel(ai);
if (name == null) {
name = ai.packageName;
}
if (GBApplication.blacklist.contains(ai.packageName)) {
if (GBApplication.isBlacklisted(ai.packageName)) {
// sort blacklisted first by prefixing with a '!'
name = "!" + name;
}
@ -78,7 +94,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
holder.deviceAppNameLabel.setText(mNameMap.get(appInfo));
holder.deviceImageView.setImageDrawable(appInfo.loadIcon(mPm));
holder.checkbox.setChecked(GBApplication.blacklist.contains(appInfo.packageName));
holder.checkbox.setChecked(GBApplication.isBlacklisted(appInfo.packageName));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -150,7 +166,7 @@ public class AppBlacklistAdapter extends RecyclerView.Adapter<AppBlacklistAdapte
for (ApplicationInfo ai : originalList) {
CharSequence name = mPm.getApplicationLabel(ai);
if (((String) name).contains(filterPattern) ||
if (name.toString().contains(filterPattern) ||
(ai.packageName.contains(filterPattern))) {
filteredList.add(ai);
}

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 João Paulo Barraca
/* Copyright (C) 2016-2017 Carsten Pfeiffer, João Paulo Barraca
This file is part of Gadgetbridge.
@ -121,6 +121,9 @@ public final class HPlusConstants {
public static final byte DATA_DAY_SUMMARY_ALT = 0x39;
public static final byte DATA_SLEEP = 0x1A;
public static final byte DATA_VERSION = 0x18;
public static final byte DATA_VERSION1 = 0x2E;
public static final byte DATA_UNKNOWN = 0x4d;
public static final String PREF_HPLUS_SCREENTIME = "hplus_screentime";
public static final String PREF_HPLUS_ALLDAYHR = "hplus_alldayhr";
@ -129,61 +132,63 @@ public final class HPlusConstants {
public static final String PREF_HPLUS_WRIST = "hplus_wrist";
public static final String PREF_HPLUS_SIT_START_TIME = "hplus_sit_start_time";
public static final String PREF_HPLUS_SIT_END_TIME = "hplus_sit_end_time";
public static final String PREF_HPLUS_UNICODE = "hplus_unicode";
public static final Map<Character, Byte> transliterateMap = new HashMap<Character, Byte>(){
public static final Map<Character, byte[]> transliterateMap = new HashMap<Character, byte[]>(){
{
//These are missing
put('ó', (byte) 111);
put('Ó', (byte) 79);
put('í', (byte) 105);
put('Í', (byte) 73);
put('ú', (byte) 117);
put('Ú', (byte) 85);
put('ó', new byte[]{(byte) 111});
put('Ó', new byte[]{(byte) 79});
put('í', new byte[]{(byte) 105});
put('Í', new byte[]{(byte) 73});
put('ú', new byte[]{(byte) 117});
put('Ú', new byte[]{(byte) 85});
//These mostly belong to the extended ASCII table
put('Ç', (byte) 128);
put('ü', (byte) 129);
put('é', (byte) 130);
put('â', (byte) 131);
put('ä', (byte) 132);
put('à', (byte) 133);
put('ã', (byte) 134);
put('ç', (byte) 135);
put('ê', (byte) 136);
put('ë', (byte) 137);
put('Ï', (byte) 139);
put('è', (byte) 138);
put('Î', (byte) 140);
put('Ì', (byte) 141);
put('Ã', (byte) 142);
put('Ä', (byte) 143);
put('É', (byte) 144);
put('æ', (byte) 145);
put('Æ', (byte) 146);
put('ô', (byte) 147);
put('ö', (byte) 148);
put('ò', (byte) 149);
put('û', (byte) 150);
put('ù', (byte) 151);
put('ÿ', (byte) 152);
put('Ö', (byte) 153);
put('Ü', (byte) 154);
put('¢', (byte) 155);
put('£', (byte) 156);
put('¥', (byte) 157);
put('ƒ', (byte) 159);
put('á', (byte) 160);
put('ñ', (byte) 164);
put('Ñ', (byte) 165);
put('ª', (byte) 166);
put('º', (byte) 167);
put('¿', (byte) 168);
put('¬', (byte) 170);
put('½', (byte) 171);
put('¼', (byte) 172);
put('¡', (byte) 173);
put('«', (byte) 174);
put('»', (byte) 175);
put('Ç', new byte[]{(byte) 128});
put('ü', new byte[]{(byte) 129});
put('é', new byte[]{(byte) 130});
put('â', new byte[]{(byte) 131});
put('ä', new byte[]{(byte) 132});
put('à', new byte[]{(byte) 133});
put('ã', new byte[]{(byte) 134});
put('ç', new byte[]{(byte) 135});
put('ê', new byte[]{(byte) 136});
put('ë', new byte[]{(byte) 137});
put('Ï', new byte[]{(byte) 139});
put('è', new byte[]{(byte) 138});
put('Î', new byte[]{(byte) 140});
put('Ì', new byte[]{(byte) 141});
put('Ã', new byte[]{(byte) 142});
put('Ä', new byte[]{(byte) 143});
put('É', new byte[]{(byte) 144});
put('æ', new byte[]{(byte) 145});
put('Æ', new byte[]{(byte) 146});
put('ô', new byte[]{(byte) 147});
put('ö', new byte[]{(byte) 148});
put('ò', new byte[]{(byte) 149});
put('û', new byte[]{(byte) 150});
put('ù', new byte[]{(byte) 151});
put('ÿ', new byte[]{(byte) 152});
put('Ö', new byte[]{(byte) 153});
put('Ü', new byte[]{(byte) 154});
put('¢', new byte[]{(byte) 155});
put('£', new byte[]{(byte) 156});
put('¥', new byte[]{(byte) 157});
put('ƒ', new byte[]{(byte) 159});
put('á', new byte[]{(byte) 160});
put('ñ', new byte[]{(byte) 164});
put('Ñ', new byte[]{(byte) 165});
put('ª', new byte[]{(byte) 166});
put('º', new byte[]{(byte) 167});
put('¿', new byte[]{(byte) 168});
put('¬', new byte[]{(byte) 170});
put('½', new byte[]{(byte) 171});
put('¼', new byte[]{(byte) 172});
put('¡', new byte[]{(byte) 173});
put('«', new byte[]{(byte) 174});
put('»', new byte[]{(byte) 175});
put('°', new byte[]{(byte) 0xa1, (byte) 0xe3});
}
};
}

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer, João Paulo Barraca
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, João
Paulo Barraca
This file is part of Gadgetbridge.
@ -24,6 +25,7 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.bluetooth.le.ScanFilter;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.ParcelUuid;
@ -197,7 +199,6 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
}else{
return HPlusConstants.ARG_TIMEMODE_12H;
}
}
public static byte getUnit(String address) {
@ -210,25 +211,25 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
}
}
public static byte getUserWeight(String address) {
public static byte getUserWeight() {
ActivityUser activityUser = new ActivityUser();
return (byte) (activityUser.getWeightKg() & 0xFF);
}
public static byte getUserHeight(String address) {
public static byte getUserHeight() {
ActivityUser activityUser = new ActivityUser();
return (byte) (activityUser.getHeightCm() & 0xFF);
}
public static byte getUserAge(String address) {
public static byte getUserAge() {
ActivityUser activityUser = new ActivityUser();
return (byte) (activityUser.getAge() & 0xFF);
}
public static byte getUserGender(String address) {
public static byte getUserGender() {
ActivityUser activityUser = new ActivityUser();
if (activityUser.getGender() == ActivityUser.GENDER_MALE)
@ -237,7 +238,7 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
return HPlusConstants.ARG_GENDER_FEMALE;
}
public static int getGoal(String address) {
public static int getGoal() {
ActivityUser activityUser = new ActivityUser();
return activityUser.getStepsGoal();
@ -281,4 +282,13 @@ public class HPlusCoordinator extends AbstractDeviceCoordinator {
return prefs.getInt(HPlusConstants.PREF_HPLUS_SIT_END_TIME, 0);
}
public static void setUnicodeSupport(String address, boolean state){
SharedPreferences.Editor editor = prefs.getPreferences().edit();
editor.putBoolean(HPlusConstants.PREF_HPLUS_UNICODE + "_" + address, state);
editor.commit();
}
public static boolean getUnicodeSupport(String address){
return (prefs.getBoolean(HPlusConstants.PREF_HPLUS_UNICODE, false));
}
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2017 João Paulo Barraca
/* Copyright (C) 2017 Andreas Shimokawa, João Paulo Barraca
This file is part of Gadgetbridge.

View File

@ -23,14 +23,24 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.alertnotification.AlertLevel;
public class VibrationProfile {
public static final Context CONTEXT = GBApplication.getContext();
public static final String ID_STACCATO = CONTEXT.getString(R.string.p_staccato);
public static final String ID_SHORT = CONTEXT.getString(R.string.p_short);
public static final String ID_MEDIUM = CONTEXT.getString(R.string.p_medium);
public static final String ID_LONG = CONTEXT.getString(R.string.p_long);
public static final String ID_WATERDROP = CONTEXT.getString(R.string.p_waterdrop);
public static final String ID_RING = CONTEXT.getString(R.string.p_ring);
public static final String ID_ALARM_CLOCK = CONTEXT.getString(R.string.p_alarm_clock);
public static final String ID_STACCATO;
public static final String ID_SHORT;
public static final String ID_MEDIUM;
public static final String ID_LONG;
public static final String ID_WATERDROP;
public static final String ID_RING;
public static final String ID_ALARM_CLOCK;
static {
Context CONTEXT = GBApplication.getContext();
ID_STACCATO = CONTEXT.getString(R.string.p_staccato);
ID_SHORT = CONTEXT.getString(R.string.p_short);
ID_MEDIUM = CONTEXT.getString(R.string.p_medium);
ID_LONG = CONTEXT.getString(R.string.p_long);
ID_WATERDROP = CONTEXT.getString(R.string.p_waterdrop);
ID_RING = CONTEXT.getString(R.string.p_ring);
ID_ALARM_CLOCK = CONTEXT.getString(R.string.p_alarm_clock);
}
public static VibrationProfile getProfile(String id, short repeat) {
if (ID_STACCATO.equals(id)) {

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2017 Carsten Pfeiffer
/* Copyright (C) 2017 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
@ -28,7 +28,7 @@ public class AutoStartReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (GBApplication.getGBPrefs().getAutoStart()) {
if (GBApplication.getGBPrefs().getAutoStart() && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Log.i(TAG, "Boot completed, starting Gadgetbridge");
GBApplication.deviceService().start();
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015-2017 João Paulo Barraca
/* Copyright (C) 2016-2017 Andreas Shimokawa, João Paulo Barraca
This file is part of Gadgetbridge.

View File

@ -50,7 +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) {
GBApplication.quit();
GBApplication.deviceService().disconnect();
}
}
}

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti,
Daniel Hauck
This file is part of Gadgetbridge.
@ -33,6 +34,7 @@ import java.util.List;
import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncState;
@ -85,82 +87,87 @@ public class CalendarReceiver extends BroadcastReceiver {
public CalendarReceiver(GBDevice gbDevice) {
LOG.info("Created calendar receiver.");
mGBDevice = gbDevice;
syncCalendar();
onReceive(GBApplication.getContext(), new Intent());
}
@Override
public void onReceive(Context context, Intent intent) {
LOG.info("got calendar changed broadcast");
syncCalendar();
List<CalendarEvents.CalendarEvent> eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext());
syncCalendar(eventList);
}
public void syncCalendar() {
LOG.info("Syncing with calendar.");
List<CalendarEvents.CalendarEvent> eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext());
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList) {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
DaoSession session = dbHandler.getDaoSession();
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvents.CalendarEvent e : eventList) {
eventTable.put(e.getId(), e);
if (!eventState.containsKey(e.getId())) {
qb = session.getCalendarSyncStateDao().queryBuilder();
CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(e.getId())))
.build().unique();
if (calendarSyncState == null) {
eventState.put(e.getId(), new EventSyncState(e, EventState.NOT_SYNCED));
} else if (calendarSyncState.getHash() == e.hashCode()) {
eventState.put(e.getId(), new EventSyncState(e, EventState.NEEDS_UPDATE));
} else {
eventState.put(e.getId(), new EventSyncState(e, EventState.SYNCED));
}
}
}
// add all missing calendar ids on the device to sync status (so that they are deleted later)
List<CalendarSyncState> CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list();
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName());
}
}
Enumeration<Long> ids = eventState.keys();
while (ids.hasMoreElements()) {
qb = session.getCalendarSyncStateDao().queryBuilder();
Long i = ids.nextElement();
EventSyncState es = eventState.get(i);
if (eventTable.containsKey(i)) {
if (es.getState() == EventState.SYNCED) {
if (!es.getEvent().equals(eventTable.get(i))) {
eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE));
}
}
} else {
if (es.getState() == EventState.NOT_SYNCED) {
// delete for current device only
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
.buildDelete().executeDeleteWithoutDetachingEntities();
eventState.remove(i);
} else {
es.setState(EventState.NEEDS_DELETE);
eventState.put(i, es);
}
}
updateEvents(deviceId, session);
}
} catch (Exception e) {
e.printStackTrace();
syncCalendar(eventList, session);
} catch (Exception e1) {
GB.toast("Database Error while syncing Calendar", Toast.LENGTH_SHORT, GB.ERROR);
}
}
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList, DaoSession session) {
LOG.info("Syncing with calendar.");
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvents.CalendarEvent e : eventList) {
long id = e.getId();
eventTable.put(id, e);
if (!eventState.containsKey(e.getId())) {
qb = session.getCalendarSyncStateDao().queryBuilder();
CalendarSyncState calendarSyncState = qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(id)))
.build().unique();
if (calendarSyncState == null) {
eventState.put(id, new EventSyncState(e, EventState.NOT_SYNCED));
LOG.info("event id=" + id + " is yet unknown to device id=" + deviceId);
} else if (calendarSyncState.getHash() == e.hashCode()) {
eventState.put(id, new EventSyncState(e, EventState.SYNCED));
LOG.info("event id=" + id + " is up to date on device id=" + deviceId);
}
else {
eventState.put(id, new EventSyncState(e, EventState.NEEDS_UPDATE));
LOG.info("event id=" + id + " is not up to date on device id=" + deviceId);
}
}
}
// add all missing calendar ids on the device to sync status (so that they are deleted later)
List<CalendarSyncState> CalendarSyncStateList = qb.where(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId)).build().list();
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
LOG.info("insert null event for orphanded calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName());
}
}
Enumeration<Long> ids = eventState.keys();
while (ids.hasMoreElements()) {
qb = session.getCalendarSyncStateDao().queryBuilder();
Long i = ids.nextElement();
EventSyncState es = eventState.get(i);
if (eventTable.containsKey(i)) {
if (es.getState() == EventState.SYNCED) {
if (!es.getEvent().equals(eventTable.get(i))) {
eventState.put(i, new EventSyncState(eventTable.get(i), EventState.NEEDS_UPDATE));
}
}
} else {
if (es.getState() == EventState.NOT_SYNCED) {
// delete for current device only
qb.where(qb.and(CalendarSyncStateDao.Properties.DeviceId.eq(deviceId), CalendarSyncStateDao.Properties.CalendarEntryId.eq(i)))
.buildDelete().executeDeleteWithoutDetachingEntities();
eventState.remove(i);
} else {
es.setState(EventState.NEEDS_DELETE);
eventState.put(i, es);
}
}
updateEvents(deviceId, session);
}
}
private void updateEvents(Long deviceId, DaoSession session) {
@ -183,7 +190,7 @@ public class CalendarReceiver extends BroadcastReceiver {
c.setTimeInMillis(calendarEvent.getBegin());
c.set(Calendar.HOUR, 0);
calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000);
calendarEventSpec.durationInSeconds = 24 * 60 * 60; //TODO: commented because it is commented above
calendarEventSpec.durationInSeconds = 24 * 60 * 60;
}
calendarEventSpec.description = calendarEvent.getDescription();
calendarEventSpec.location = calendarEvent.getLocation();

View File

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
@ -30,17 +29,20 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -76,7 +78,7 @@ public class NotificationListener extends NotificationListenerService {
private LimitedQueue mActionLookup = new LimitedQueue(16);
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@ -89,7 +91,7 @@ public class NotificationListener extends NotificationListenerService {
StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications();
int handle = intent.getIntExtra("handle", -1);
for (StatusBarNotification sbn : sbns) {
if ((int) sbn.getPostTime() == handle) {
if ((sbn.getPackageName().hashCode() * 31 + sbn.getId()) == handle) {
if (action.equals(ACTION_OPEN)) {
try {
PendingIntent pi = sbn.getNotification().contentIntent;
@ -112,7 +114,7 @@ public class NotificationListener extends NotificationListenerService {
StatusBarNotification[] sbns = NotificationListener.this.getActiveNotifications();
int handle = intent.getIntExtra("handle", -1);
for (StatusBarNotification sbn : sbns) {
if ((int) sbn.getPostTime() == handle) {
if ((sbn.getPackageName().hashCode() * 31 + sbn.getId()) == handle) {
if (GBApplication.isRunningLollipopOrLater()) {
String key = sbn.getKey();
NotificationListener.this.cancelNotification(key);
@ -177,16 +179,8 @@ public class NotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
/*
* return early if DeviceCommunicationService is not running,
* else the service would get started every time we get a notification.
* unfortunately we cannot enable/disable NotificationListener at runtime like we do with
* broadcast receivers because it seems to invalidate the permissions that are
* necessary for NotificationListenerService
*/
if (!isServiceRunning()) {
if (shouldIgnore(sbn))
return;
}
switch (GBApplication.getGrantedInterruptionFilter()) {
case NotificationManager.INTERRUPTION_FILTER_ALL:
@ -201,53 +195,8 @@ public class NotificationListener extends NotificationListenerService {
String source = sbn.getPackageName();
Notification notification = sbn.getNotification();
if (handleMediaSessionNotification(notification))
return;
Prefs prefs = GBApplication.getPrefs();
if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) {
PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE);
if (powermanager.isScreenOn()) {
// LOG.info("Not forwarding notification, screen seems to be on and settings do not allow this");
return;
}
}
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) {
// LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags);
return;
}
/* do not display messages from "android"
* This includes keyboard selection message, usb connection messages, etc
* Hope it does not filter out too much, we will see...
*/
if (source.equals("android") ||
source.equals("com.android.systemui") ||
source.equals("com.android.dialer") ||
source.equals("com.cyanogenmod.eleven")) {
LOG.info("Not forwarding notification, is a system event");
return;
}
if (source.equals("com.moez.QKSMS") ||
source.equals("com.android.mms") ||
source.equals("com.sonyericsson.conversations") ||
source.equals("com.android.messaging") ||
source.equals("org.smssecure.smssecure")) {
if (!"never".equals(prefs.getString("notification_mode_sms", "when_screen_off"))) {
return;
}
}
if (GBApplication.blacklist != null && GBApplication.blacklist.contains(source)) {
LOG.info("Not forwarding notification, application is blacklisted");
return;
}
NotificationSpec notificationSpec = new NotificationSpec();
notificationSpec.id = source.hashCode() * 31 + sbn.getId();
// determinate Source App Name ("Label")
PackageManager pm = getPackageManager();
@ -261,26 +210,17 @@ public class NotificationListener extends NotificationListenerService {
notificationSpec.sourceName = (String) pm.getApplicationLabel(ai);
}
boolean preferBigText = false;
boolean preferBigText = true; //changed to true since now we update the former ID
notificationSpec.type = AppNotificationType.getInstance().get(source);
if (source.startsWith("com.fsck.k9")) {
// we dont want group summaries at all for k9
if ((notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) {
return;
}
preferBigText = true;
}
if (notificationSpec.type == null) {
notificationSpec.type = NotificationType.UNKNOWN;
}
LOG.info("Processing notification from source " + source + " with flags: " + notification.flags);
LOG.info("Processing notification " + notificationSpec.id + " from source " + source + " with flags: " + notification.flags);
dissectNotificationTo(notification, notificationSpec, preferBigText);
notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better
// ignore Gadgetbridge's very own notifications, except for those from the debug screen
if (getApplicationContext().getPackageName().equals(source)) {
@ -292,11 +232,6 @@ public class NotificationListener extends NotificationListenerService {
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification);
List<NotificationCompat.Action> actions = wearableExtender.getActions();
if (actions.isEmpty() && (notification.flags & Notification.FLAG_GROUP_SUMMARY) == Notification.FLAG_GROUP_SUMMARY) { //this could cause #395 to come back
LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set and no wearable action present. Notification flags: " + notification.flags);
return;
}
for (NotificationCompat.Action act : actions) {
if (act != null && act.getRemoteInputs() != null) {
LOG.info("found wearable action: " + act.getTitle() + " " + sbn.getTag());
@ -306,11 +241,17 @@ public class NotificationListener extends NotificationListenerService {
}
}
if ((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) == 0 && NotificationCompat.isGroupSummary(notification)) { //this could cause #395 to come back
LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set and no wearable action present. Notification flags: " + notification.flags);
return;
}
GBApplication.deviceService().onNotification(notificationSpec);
}
private void dissectNotificationTo(Notification notification, NotificationSpec notificationSpec, boolean preferBigText) {
Bundle extras = notification.extras;
Bundle extras = NotificationCompat.getExtras(notification);
//dumpExtras(extras);
@ -320,10 +261,32 @@ public class NotificationListener extends NotificationListenerService {
}
CharSequence contentCS = null;
if (preferBigText && extras.containsKey(Notification.EXTRA_BIG_TEXT)) {
contentCS = extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
} else if (extras.containsKey(Notification.EXTRA_TEXT)) {
contentCS = extras.getCharSequence(Notification.EXTRA_TEXT);
if (extras.containsKey(Notification.EXTRA_MESSAGES)) {
Parcelable[] parcelables = extras.getParcelableArray(NotificationCompat.EXTRA_MESSAGES);
String contentBuilder = "";
CharSequence sender;
CharSequence prevSender = "";
CharSequence message;
for (Parcelable p : parcelables) {
if (!(p instanceof Bundle))
continue;
sender = ((Bundle) p).getCharSequence("sender");
message = ((Bundle) p).getCharSequence("text");
if (sender == null || message == null)
continue;
if (!sender.equals(prevSender) && !sender.equals(notificationSpec.title)) {
contentBuilder += sender.toString() + ": ";
prevSender = sender;
}
contentBuilder += message.toString() + "\n";
}
contentCS = contentBuilder;
} else {
if (preferBigText && extras.containsKey(Notification.EXTRA_BIG_TEXT)) {
contentCS = extras.getCharSequence(NotificationCompat.EXTRA_BIG_TEXT);
} else if (extras.containsKey(Notification.EXTRA_TEXT)) {
contentCS = extras.getCharSequence(NotificationCompat.EXTRA_TEXT);
}
}
if (contentCS != null) {
notificationSpec.body = contentCS.toString();
@ -344,31 +307,18 @@ public class NotificationListener extends NotificationListenerService {
/**
* Try to handle media session notifications that tell info about the current play state.
*
* @param notification The notification to handle.
* @param mediaSession The mediasession to handle.
* @return true if notification was handled, false otherwise
*/
public boolean handleMediaSessionNotification(Notification notification) {
// this code requires Android 5.0 or newer
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return false;
}
public boolean handleMediaSessionNotification(MediaSessionCompat.Token mediaSession) {
MusicSpec musicSpec = new MusicSpec();
MusicStateSpec stateSpec = new MusicStateSpec();
Bundle extras = notification.extras;
if (extras == null)
return false;
if (extras.get(Notification.EXTRA_MEDIA_SESSION) == null)
return false;
MediaController c;
MediaControllerCompat c;
try {
c = new MediaController(getApplicationContext(), (MediaSession.Token) extras.get(Notification.EXTRA_MEDIA_SESSION));
c = new MediaControllerCompat(getApplicationContext(), mediaSession);
PlaybackState s = c.getPlaybackState();
PlaybackStateCompat s = c.getPlaybackState();
stateSpec.position = (int) (s.getPosition() / 1000);
stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed());
stateSpec.repeat = 1;
@ -388,57 +338,41 @@ public class NotificationListener extends NotificationListenerService {
break;
}
MediaMetadata d = c.getMetadata();
MediaMetadataCompat d = c.getMetadata();
if (d == null)
return false;
if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST))
musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST);
musicSpec.artist = d.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM))
musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM);
musicSpec.album = d.getString(MediaMetadataCompat.METADATA_KEY_ALBUM);
if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE))
musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE);
musicSpec.track = d.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION))
musicSpec.duration = (int) d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000;
musicSpec.duration = (int) d.getLong(MediaMetadataCompat.METADATA_KEY_DURATION) / 1000;
if (d.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS))
musicSpec.trackCount = (int) d.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS);
musicSpec.trackCount = (int) d.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS);
if (d.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER))
musicSpec.trackNr = (int) d.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
musicSpec.trackNr = (int) d.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER);
// finally, tell the device about it
GBApplication.deviceService().onSetMusicInfo(musicSpec);
GBApplication.deviceService().onSetMusicState(stateSpec);
return true;
} catch (NullPointerException e) {
} catch (NullPointerException | RemoteException e) {
return false;
}
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
//FIXME: deduplicate code
if (!isServiceRunning() || sbn == null) {
if (shouldIgnore(sbn))
return;
}
String source = sbn.getPackageName();
Notification notification = sbn.getNotification();
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) {
return;
}
if (source.equals("android") ||
source.equals("com.android.systemui") ||
source.equals("com.android.dialer") ||
source.equals("com.cyanogenmod.eleven")) {
return;
}
Prefs prefs = GBApplication.getPrefs();
if (prefs.getBoolean("autoremove_notifications", false)) {
LOG.info("notification removed, will ask device to delete it");
GBApplication.deviceService().onDeleteNotification((int) sbn.getPostTime()); //FIMXE: a truly unique id would be better
GBApplication.deviceService().onDeleteNotification(sbn.getPackageName().hashCode() * 31 + sbn.getId());
}
}
@ -451,4 +385,88 @@ public class NotificationListener extends NotificationListenerService {
LOG.debug(String.format("Notification extra: %s %s (%s)", key, value.toString(), value.getClass().getName()));
}
}
private boolean shouldIgnore(StatusBarNotification sbn) {
/*
* return early if DeviceCommunicationService is not running,
* else the service would get started every time we get a notification.
* unfortunately we cannot enable/disable NotificationListener at runtime like we do with
* broadcast receivers because it seems to invalidate the permissions that are
* necessary for NotificationListenerService
*/
if (!isServiceRunning() || sbn == null) {
return true;
}
if (shouldIgnoreSource(sbn.getPackageName()))
return true;
if (shouldIgnoreNotification(sbn.getNotification()))
return true;
return false;
}
private boolean shouldIgnoreSource(String source) {
Prefs prefs = GBApplication.getPrefs();
/* do not display messages from "android"
* This includes keyboard selection message, usb connection messages, etc
* Hope it does not filter out too much, we will see...
*/
if (source.equals("android") ||
source.equals("com.android.systemui") ||
source.equals("com.android.dialer") ||
source.equals("com.cyanogenmod.eleven")) {
LOG.info("Ignoring notification, is a system event");
return true;
}
if (source.equals("com.moez.QKSMS") ||
source.equals("com.android.mms") ||
source.equals("com.sonyericsson.conversations") ||
source.equals("com.android.messaging") ||
source.equals("org.smssecure.smssecure")) {
if (!"never".equals(prefs.getString("notification_mode_sms", "when_screen_off"))) {
return true;
}
}
if (GBApplication.isBlacklisted(source)) {
LOG.info("Ignoring notification, application is blacklisted");
return true;
}
return false;
}
private boolean shouldIgnoreNotification(Notification notification) {
MediaSessionCompat.Token mediaSession = NotificationCompat.getMediaSession(notification);
//try to handle media session notifications
if (mediaSession != null && handleMediaSessionNotification(mediaSession))
return true;
//ignore notifications marked as LocalOnly https://developer.android.com/reference/android/app/Notification.html#FLAG_LOCAL_ONLY
if (NotificationCompat.getLocalOnly(notification))
return true;
Prefs prefs = GBApplication.getPrefs();
if (!prefs.getBoolean("notifications_generic_whenscreenon", false)) {
PowerManager powermanager = (PowerManager) getSystemService(POWER_SERVICE);
if (powermanager.isScreenOn()) {
// LOG.info("Not forwarding notification, screen seems to be on and settings do not allow this");
return true;
}
}
if ((notification.flags & Notification.FLAG_ONGOING_EVENT) == Notification.FLAG_ONGOING_EVENT) {
// LOG.info("Not forwarding notification, FLAG_ONGOING_EVENT is set. Notification flags: " + notification.flags);
return true;
}
return false;
}
}

View File

@ -332,7 +332,8 @@ public class GBDeviceService implements DeviceService {
.putExtra(EXTRA_CALENDAREVENT_TIMESTAMP, calendarEventSpec.timestamp)
.putExtra(EXTRA_CALENDAREVENT_DURATION, calendarEventSpec.durationInSeconds)
.putExtra(EXTRA_CALENDAREVENT_TITLE, calendarEventSpec.title)
.putExtra(EXTRA_CALENDAREVENT_DESCRIPTION, calendarEventSpec.description);
.putExtra(EXTRA_CALENDAREVENT_DESCRIPTION, calendarEventSpec.description)
.putExtra(EXTRA_CALENDAREVENT_LOCATION, calendarEventSpec.location);
invokeService(intent);
}

View File

@ -44,6 +44,9 @@ public class AppNotificationType extends HashMap<String, NotificationType> {
put("com.sonyericsson.conversations", NotificationType.GENERIC_SMS);
put("org.smssecure.smssecure", NotificationType.GENERIC_SMS);
// Generic Calendar
put("com.android.calendar", NotificationType.GENERIC_CALENDAR);
// Conversations
put("eu.siacs.conversations", NotificationType.CONVERSATIONS);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,5 +1,5 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
Gobbetti, Daniel Hauck
This file is part of Gadgetbridge.
@ -22,6 +22,11 @@ import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract.Instances;
import android.text.format.Time;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
@ -30,6 +35,7 @@ import java.util.List;
import java.util.Objects;
public class CalendarEvents {
private static final Logger LOG = LoggerFactory.getLogger(CalendarEvents.class);
// needed for pebble: time, duration, layout, reminders, actions
// layout: type, title, subtitle, body (max 512), tinyIcon, smallIcon, largeIcon
@ -41,9 +47,10 @@ public class CalendarEvents {
private static final String[] EVENT_INSTANCE_PROJECTION = new String[]{
Instances._ID,
Instances.BEGIN,
Instances.END,
Instances.EVENT_ID,
Instances.DURATION,
Instances.TITLE,
Instances.DESCRIPTION,
Instances.EVENT_LOCATION,
@ -77,10 +84,18 @@ public class CalendarEvents {
return false;
}
while (evtCursor.moveToNext()) {
long start = evtCursor.getLong(1);
long end = evtCursor.getLong(2);
if (end == 0) {
LOG.info("no end time, will parse duration string");
Time time = new Time(); //FIXME: deprecated FTW
time.parse(evtCursor.getString(3));
end = start + time.toMillis(false);
}
CalendarEvent calEvent = new CalendarEvent(
evtCursor.getLong(1),
evtCursor.getLong(2),
evtCursor.getLong(3),
start,
end,
evtCursor.getLong(0),
evtCursor.getString(4),
evtCursor.getString(5),
evtCursor.getString(6),
@ -93,7 +108,7 @@ public class CalendarEvents {
}
}
public class CalendarEvent {
public static class CalendarEvent {
private long begin;
private long end;
private long id;

View File

@ -129,6 +129,7 @@ public interface DeviceService extends EventHandler {
String EXTRA_CALENDAREVENT_DURATION = "calendarevent_duration";
String EXTRA_CALENDAREVENT_TITLE = "calendarevent_title";
String EXTRA_CALENDAREVENT_DESCRIPTION = "calendarevent_description";
String EXTRA_CALENDAREVENT_LOCATION = "calendarevent_location";
void start();

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.

View File

@ -29,6 +29,7 @@ public enum NotificationType {
GENERIC_EMAIL(PebbleIconID.GENERIC_EMAIL, PebbleColor.JaegerGreen),
GENERIC_NAVIGATION(PebbleIconID.LOCATION, PebbleColor.Orange),
GENERIC_SMS(PebbleIconID.GENERIC_SMS, PebbleColor.VividViolet),
GENERIC_CALENDAR(PebbleIconID.TIMELINE_CALENDAR, PebbleColor.Blue),
FACEBOOK(PebbleIconID.NOTIFICATION_FACEBOOK, PebbleColor.Liberty),
FACEBOOK_MESSENGER(PebbleIconID.NOTIFICATION_FACEBOOK_MESSENGER, PebbleColor.VeryLightBlue),
RIOT(PebbleIconID.NOTIFICATION_HIPCHAT, PebbleColor.LavenderIndigo),

View File

@ -318,6 +318,8 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
context.getString(R.string.notif_battery_low_bigtext_number_of_charges, String.valueOf(deviceEvent.numCharges))
: ""
, context);
} else {
GB.removeBatteryNotification(context);
}
gbDevice.sendDeviceUpdateIntent(context);

View File

@ -1,6 +1,6 @@
/* Copyright (C) 2015-2017 Andreas Shimokawa, Avamander, Carsten Pfeiffer,
Daniele Gobbetti, ivanovlev, Julien Pivotto, Kasha, Sergey Trofimov, Steffen
Liebergeld, Uwe Hermann
Daniele Gobbetti, Daniel Hauck, ivanovlev, João Paulo Barraca, Julien
Pivotto, Kasha, Sergey Trofimov, Steffen Liebergeld, Uwe Hermann
This file is part of Gadgetbridge.
@ -18,6 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
@ -26,9 +28,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.widget.Toast;
@ -107,6 +111,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_BOO
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_DESCRIPTION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_DURATION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_ID;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_LOCATION;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TIMESTAMP;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TITLE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_CALENDAREVENT_TYPE;
@ -153,6 +158,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_WEA
public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final Logger LOG = LoggerFactory.getLogger(DeviceCommunicationService.class);
@SuppressLint("StaticFieldLeak") // only used for test cases
private static DeviceSupportFactory DEVICE_SUPPORT_FACTORY = null;
private boolean mStarted = false;
@ -203,14 +209,13 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
String action = intent.getAction();
if (action.equals(GBDevice.ACTION_DEVICE_CHANGED)) {
GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
// FIXME: mGBDevice was null here once
if (mGBDevice.equals(device)) {
if (mGBDevice != null && mGBDevice.equals(device)) {
mGBDevice = device;
boolean enableReceivers = mDeviceSupport != null && (mDeviceSupport.useAutoConnect() || mGBDevice.isInitialized());
setReceiversEnableState(enableReceivers, mGBDevice.isInitialized(), DeviceHelper.getInstance().getCoordinator(device));
GB.updateNotification(mGBDevice.getName() + " " + mGBDevice.getStateString(), mGBDevice.isInitialized(), context);
} else {
LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + mGBDevice);
LOG.error("Got ACTION_DEVICE_CHANGED from unexpected device: " + device);
}
}
}
@ -374,6 +379,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
calendarEventSpec.durationInSeconds = intent.getIntExtra(EXTRA_CALENDAREVENT_DURATION, -1);
calendarEventSpec.title = intent.getStringExtra(EXTRA_CALENDAREVENT_TITLE);
calendarEventSpec.description = intent.getStringExtra(EXTRA_CALENDAREVENT_DESCRIPTION);
calendarEventSpec.location = intent.getStringExtra(EXTRA_CALENDAREVENT_LOCATION);
mDeviceSupport.onAddCalendarEvent(calendarEventSpec);
break;
}
@ -580,13 +586,15 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
LOG.info("Setting broadcast receivers to: " + enable);
if (enable && initialized && coordinator != null && coordinator.supportsCalendarEvents()) {
if (mCalendarReceiver == null) {
IntentFilter calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
mCalendarReceiver = new CalendarReceiver(mGBDevice);
registerReceiver(mCalendarReceiver, calendarIntentFilter);
if (mCalendarReceiver == null && getPrefs().getBoolean("enable_calendar_sync", true)) {
if (!(GBApplication.isRunningMarshmallowOrLater() && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)) {
IntentFilter calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
mCalendarReceiver = new CalendarReceiver(mGBDevice);
registerReceiver(mCalendarReceiver, calendarIntentFilter);
}
}
if (mAlarmReceiver == null) {
mAlarmReceiver = new AlarmReceiver();

View File

@ -1,3 +1,19 @@
/* Copyright (C) 2017 Daniele Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service;
import android.app.ActivityManager;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Carsten Pfeiffer, JohnnySun
/* Copyright (C) 2016-2017 Carsten Pfeiffer, João Paulo Barraca, JohnnySun
This file is part of Gadgetbridge.

View File

@ -225,7 +225,7 @@ public final class BtLEQueue {
// alive (we do not close() it). Unfortunately we sometimes have problems
// reconnecting automatically, so we try to fix this by re-creating mBluetoothGatt.
// Not sure if this actually works without re-initializing the device...
if (status != 0) {
if (mBluetoothGatt != null) {
if (!wasInitialized || !maybeReconnect()) {
disconnect(); // ensure that we start over cleanly next time
}

View File

@ -51,7 +51,13 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
*/
public int heartRate;
public HPlusDataRecordDaySlot(byte[] data) {
private int age = 0;
/**
* Raw intensity calculated from calories
*/
public int intensity;
public HPlusDataRecordDaySlot(byte[] data, int age) {
super(data, TYPE_DAY_SLOT);
int a = (data[4] & 0xFF) * 256 + (data[5] & 0xFF);
@ -77,6 +83,8 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
slotTime.set(Calendar.SECOND, 0);
timestamp = (int) (slotTime.getTimeInMillis() / 1000L);
this.age = age;
}
public String toString(){
@ -101,6 +109,9 @@ public class HPlusDataRecordDaySlot extends HPlusDataRecord {
}
secondsInactive += other.secondsInactive;
intensity = (int) ((100*heartRate)/(208-0.7*age));
}
public boolean isValid() {

View File

@ -66,7 +66,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord {
*/
public int intensity;
public HPlusDataRecordRealtime(byte[] data) {
public HPlusDataRecordRealtime(byte[] data, int age) {
super(data, TYPE_REALTIME);
if (data.length < 15) {
@ -91,7 +91,7 @@ class HPlusDataRecordRealtime extends HPlusDataRecord {
heartRate = ActivitySample.NOT_MEASURED;
}
else {
intensity = (int) (100 * Math.max(0, Math.min((heartRate - 60) / 120.0, 1))); // TODO: Calculate a proper value
intensity = (int) ((100*heartRate)/(208-0.7*age));
activityKind = ActivityKind.TYPE_UNKNOWN;
}
}

View File

@ -24,6 +24,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.hplus;
import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -65,7 +66,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
private int DAY_SUMMARY_SYNC_PERIOD = 24 * 60 * 60;
private int DAY_SUMMARY_SYNC_RETRY_PERIOD = 30;
private int HELLO_PERIOD = 60;
private int HELLO_PERIOD = 60 * 2;
private boolean mQuit = false;
private HPlusSupport mHPlusSupport;
@ -122,9 +123,8 @@ class HPlusHandlerThread extends GBDeviceIoThread {
break;
}
if(!mHPlusSupport.getDevice().isConnected()){
if(gbDevice.getState() == GBDevice.State.NOT_CONNECTED){
quit();
break;
}
Calendar now = GregorianCalendar.getInstance();
@ -141,24 +141,26 @@ class HPlusHandlerThread extends GBDeviceIoThread {
requestDaySummaryData();
}
if (now.compareTo(mHelloTime) > 0) {
if(now.compareTo(mHelloTime) > 0){
sendHello();
}
now = GregorianCalendar.getInstance();
waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis())) - now.getTimeInMillis();
waitTime = Math.min(mGetDaySummaryTime.getTimeInMillis(), Math.min(mGetDaySlotsTime.getTimeInMillis(), Math.min(mHelloTime.getTimeInMillis(), mGetSleepTime.getTimeInMillis()))) - now.getTimeInMillis();
}
}
@Override
public void quit() {
LOG.info("HPlus: Quit Handler Thread");
mQuit = true;
synchronized (waitObject) {
waitObject.notify();
}
}
public void sync() {
LOG.info("HPlus: Starting data synchronization");
@ -176,7 +178,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
mDaySlotRecords.clear();
try {
if (!mHPlusSupport.isConnected())
if(!mHPlusSupport.isConnected())
mHPlusSupport.connect();
TransactionBuilder builder = new TransactionBuilder("startSyncDayStats");
@ -185,9 +187,9 @@ class HPlusHandlerThread extends GBDeviceIoThread {
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_VERSION});
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_CURR_DATA});
builder.queue(mHPlusSupport.getQueue());
} catch (Exception e) {
mHPlusSupport.performConnected(builder.getTransaction());
}catch(Exception e){
LOG.warn("HPlus: Synchronization exception: " + e);
}
synchronized (waitObject) {
@ -195,20 +197,21 @@ class HPlusHandlerThread extends GBDeviceIoThread {
}
}
public void sendHello() {
public void sendHello(){
try {
if (!mHPlusSupport.isConnected())
mHPlusSupport.connect();
TransactionBuilder builder = new TransactionBuilder("hello");
builder.write(mHPlusSupport.ctrlCharacteristic, HPlusConstants.CMD_ACTION_HELLO);
builder.queue(mHPlusSupport.getQueue());
} catch (Exception e) {
mHPlusSupport.performConnected(builder.getTransaction());
}catch(Exception e){
}
mHelloTime = GregorianCalendar.getInstance();
mHelloTime.add(Calendar.SECOND, HELLO_PERIOD);
synchronized (waitObject) {
waitObject.notify();
}
}
/**
* Process a message containing information regarding a day slot
@ -217,12 +220,12 @@ class HPlusHandlerThread extends GBDeviceIoThread {
* @param data the message from the device
* @return boolean indicating success or fail
*/
public boolean processIncomingDaySlotData(byte[] data) {
public boolean processIncomingDaySlotData(byte[] data, int age) {
HPlusDataRecordDaySlot record;
try{
record = new HPlusDataRecordDaySlot(data);
record = new HPlusDataRecordDaySlot(data, age);
} catch(IllegalArgumentException e){
LOG.info((e.getMessage()));
return false;
@ -290,7 +293,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
for (HPlusDataRecordDaySlot storedRecord : mDaySlotRecords) {
//Invalid records (no data) will be ignored
if (!storedRecord.isValid())
if(!storedRecord.isValid())
continue;
HPlusHealthActivitySample sample = createSample(dbHandler, storedRecord.timestamp);
@ -299,7 +302,7 @@ class HPlusHandlerThread extends GBDeviceIoThread {
sample.setSteps(storedRecord.steps);
sample.setHeartRate(storedRecord.heartRate);
sample.setRawKind(storedRecord.type);
sample.setRawIntensity(record.intensity);
sample.setProvider(provider);
samples.add(sample);
}
@ -380,11 +383,11 @@ class HPlusHandlerThread extends GBDeviceIoThread {
* @param data the message from the device
* @return boolean indicating success or fail
*/
public boolean processRealtimeStats(byte[] data) {
public boolean processRealtimeStats(byte[] data, int age) {
HPlusDataRecordRealtime record;
try{
record = new HPlusDataRecordRealtime(data);
record = new HPlusDataRecordRealtime(data, age);
} catch(IllegalArgumentException e){
LOG.info((e.getMessage()));
return false;
@ -493,11 +496,23 @@ class HPlusHandlerThread extends GBDeviceIoThread {
* @return boolean indicating success or fail
*/
public boolean processVersion(byte[] data) {
int major = data[2] & 0xFF;
int minor = data[1] & 0xFF;
int major, minor;
if(data.length >= 11){
major = data[10] & 0xFF;
minor = data[9] & 0xFF;
int hwMajor = data[2] & 0xFF;
int hwMinor = data[1] & 0xFF;
getDevice().setFirmwareVersion2(hwMajor + "." + hwMinor);
mHPlusSupport.setUnicodeSupport((data[3] != 0));
}else {
major = data[2] & 0xFF;
minor = data[1] & 0xFF;
}
getDevice().setFirmwareVersion(major + "." + minor);
getDevice().sendDeviceUpdateIntent(getContext());
return true;
@ -508,13 +523,10 @@ class HPlusHandlerThread extends GBDeviceIoThread {
*/
private void requestNextSleepData() {
try {
if (!mHPlusSupport.isConnected())
mHPlusSupport.connect();
TransactionBuilder builder = new TransactionBuilder("requestSleepStats");
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_SLEEP});
builder.queue(mHPlusSupport.getQueue());
} catch (Exception e) {
mHPlusSupport.performConnected(builder.getTransaction());
}catch(Exception e){
}
@ -565,13 +577,11 @@ class HPlusHandlerThread extends GBDeviceIoThread {
byte[] msg = new byte[]{HPlusConstants.CMD_GET_ACTIVE_DAY, hour, minute, nextHour, nextMinute};
try {
if (!mHPlusSupport.isConnected())
mHPlusSupport.connect();
TransactionBuilder builder = new TransactionBuilder("getNextDaySlot");
builder.write(mHPlusSupport.ctrlCharacteristic, msg);
builder.queue(mHPlusSupport.getQueue());
} catch (Exception e) {
mHPlusSupport.performConnected(builder.getTransaction());
}catch(Exception e){
}
}
@ -580,13 +590,10 @@ class HPlusHandlerThread extends GBDeviceIoThread {
*/
public void requestDaySummaryData(){
try {
if (!mHPlusSupport.isConnected())
mHPlusSupport.connect();
TransactionBuilder builder = new TransactionBuilder("startSyncDaySummary");
builder.write(mHPlusSupport.ctrlCharacteristic, new byte[]{HPlusConstants.CMD_GET_DAY_DATA});
builder.queue(mHPlusSupport.getQueue());
} catch (Exception e) {
mHPlusSupport.performConnected(builder.getTransaction());
}catch(Exception e){
}
mGetDaySummaryTime = GregorianCalendar.getInstance();
@ -617,4 +624,8 @@ class HPlusHandlerThread extends GBDeviceIoThread {
return sample;
}
public void setHPlusSupport(HPlusSupport HPlusSupport) {
LOG.info("Updating HPlusSupport object");
this.mHPlusSupport = HPlusSupport;
}
}

View File

@ -1,5 +1,5 @@
/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, ivanovlev, João
Paulo Barraca
/* Copyright (C) 2016-2017 Alberto, Andreas Shimokawa, Carsten Pfeiffer,
ivanovlev, João Paulo Barraca
This file is part of Gadgetbridge.
@ -38,6 +38,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
@ -60,7 +61,6 @@ 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.btle.profiles.deviceinfo.DeviceInfo;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.deviceinfo.DeviceInfoProfile;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -90,7 +90,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
public HPlusSupport(DeviceType type) {
super(LOG);
LOG.info("HPlusSupport Instance Created");
deviceType = type;
addSupportedService(HPlusConstants.UUID_SERVICE_HP);
@ -116,29 +116,34 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
measureCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE);
ctrlCharacteristic = getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_CONTROL);
getDevice().setFirmwareVersion("N/A");
getDevice().setFirmwareVersion2("N/A");
syncHelper = new HPlusHandlerThread(getDevice(), getContext(), this);
//Initialize device
sendUserInfo(builder); //Sync preferences
requestDeviceInfo(builder);
setInitialized(builder);
syncHelper.start();
builder.notify(getCharacteristic(HPlusConstants.UUID_CHARACTERISTIC_MEASURE), true);
builder.setGattCallback(this);
builder.notify(measureCharacteristic, true);
//Initialize device
sendUserInfo(builder); //Sync preferences
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
if(syncHelper == null) {
syncHelper = new HPlusHandlerThread(getDevice(), getContext(), this);
syncHelper.start();
}
syncHelper.sync();
getDevice().setFirmwareVersion("N/A");
getDevice().setFirmwareVersion2("N/A");
requestDeviceInfo(builder);
LOG.info("Initialization Done");
return builder;
}
@ -287,7 +292,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
private HPlusSupport setWeight(TransactionBuilder transaction) {
byte value = HPlusCoordinator.getUserWeight(getDevice().getAddress());
byte value = HPlusCoordinator.getUserWeight();
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_WEIGHT,
value
@ -297,7 +302,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
private HPlusSupport setHeight(TransactionBuilder transaction) {
byte value = HPlusCoordinator.getUserHeight(getDevice().getAddress());
byte value = HPlusCoordinator.getUserHeight();
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_HEIGHT,
value
@ -308,7 +313,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
private HPlusSupport setAge(TransactionBuilder transaction) {
byte value = HPlusCoordinator.getUserAge(getDevice().getAddress());
byte value = HPlusCoordinator.getUserAge();
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_AGE,
value
@ -318,7 +323,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
private HPlusSupport setGender(TransactionBuilder transaction) {
byte value = HPlusCoordinator.getUserGender(getDevice().getAddress());
byte value = HPlusCoordinator.getUserGender();
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_GENDER,
value
@ -329,7 +334,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
private HPlusSupport setGoal(TransactionBuilder transaction) {
int value = HPlusCoordinator.getGoal(getDevice().getAddress());
int value = HPlusCoordinator.getGoal();
transaction.write(ctrlCharacteristic, new byte[]{
HPlusConstants.CMD_SET_GOAL,
(byte) ((value / 256) & 0xff),
@ -404,11 +409,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
return this;
}
private void setInitialized(TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
}
@Override
public boolean useAutoConnect() {
return true;
@ -432,20 +432,22 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSetTime() {
try {
TransactionBuilder builder = performInitialized("time");
setCurrentDate(builder);
setCurrentTime(builder);
builder.queue(getQueue());
} catch (IOException e) {
performConnected(builder.getTransaction());
}catch(IOException e){
}
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
try {
TransactionBuilder builder = performInitialized("alarm");
@ -467,11 +469,10 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
setAlarm(builder, null);
builder.queue(getQueue());
performConnected(builder.getTransaction());
GB.toast(getContext(), getContext().getString(R.string.user_feedback_all_alarms_disabled), Toast.LENGTH_SHORT, GB.INFO);
} catch (Exception e) {
}
}catch(Exception e){}
}
@ -494,7 +495,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
@ -539,8 +539,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
@Override
public void onFetchActivityData() {
if (syncHelper != null)
syncHelper.sync();
if (syncHelper == null){
syncHelper = new HPlusHandlerThread(gbDevice, getContext(), this);
syncHelper.start();
}
syncHelper.sync();
}
@Override
@ -550,8 +555,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("Shutdown");
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SHUTDOWN, HPlusConstants.ARG_SHUTDOWN_EN});
builder.queue(getQueue());
} catch (Exception e) {
performConnected(builder.getTransaction());
}catch(Exception e){
}
}
@ -559,19 +564,18 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
@Override
public void onHeartRateTest() {
getQueue().clear();
try {
try{
TransactionBuilder builder = performInitialized("HeartRateTest");
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_HEARTRATE_STATE, HPlusConstants.ARG_HEARTRATE_MEASURE_ON}); //Set Real Time... ?
builder.queue(getQueue());
} catch (Exception e) {
performConnected(builder.getTransaction());
}catch(Exception e){
}
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
getQueue().clear();
try {
TransactionBuilder builder = performInitialized("realTimeHeartMeasurement");
byte state;
@ -582,8 +586,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
state = HPlusConstants.ARG_HEARTRATE_ALLDAY_OFF;
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_ALLDAY_HRM, state});
builder.queue(getQueue());
} catch (Exception e) {
performConnected(builder.getTransaction());
}catch(Exception e){
}
}
@ -594,7 +598,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
TransactionBuilder builder = performInitialized("findMe");
setFindMe(builder, start);
builder.queue(getQueue());
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Error toggling Find Me: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -603,8 +607,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
@Override
public void onSetConstantVibration(int intensity) {
getQueue().clear();
try {
TransactionBuilder builder = performInitialized("vibration");
@ -615,7 +617,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
msg[i + 1] = (byte) "GadgetBridge".charAt(i);
builder.write(ctrlCharacteristic, msg);
builder.queue(getQueue());
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Error setting Vibration: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
}
@ -658,17 +660,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
public void setUnicodeSupport(boolean support){
HPlusCoordinator.setUnicodeSupport(gbDevice.getAddress(), support);
}
private void showIncomingCall(String name, String rawNumber) {
try {
StringBuilder number = new StringBuilder();
//Clean up number as the device only accepts digits
for (char c : rawNumber.toCharArray()) {
if (Character.isDigit(c)) {
number.append(c);
}
}
TransactionBuilder builder = performInitialized("incomingCall");
@ -678,39 +676,50 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
//Show Call Icon
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_CALL, HPlusConstants.ARG_INCOMING_CALL});
byte[] msg = new byte[13];
if(name != null) {
byte[] msg = new byte[13];
//Show call name
for (int i = 0; i < msg.length; i++)
msg[i] = ' ';
//Show call name
byte[] nameBytes = encodeStringToDevice(name);
for (int i = 0; i < nameBytes.length && i < (msg.length - 1); i++)
msg[i + 1] = nameBytes[i];
for (int i = 0; i < msg.length; i++)
msg[i] = ' ';
msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME;
builder.write(ctrlCharacteristic, msg);
byte[] nameBytes = encodeStringToDevice(name);
for (int i = 0; i < nameBytes.length && i < (msg.length - 1); i++)
msg[i + 1] = nameBytes[i];
msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME_CN;
builder.write(ctrlCharacteristic, msg);
}
msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME;
builder.write(ctrlCharacteristic, msg);
if(rawNumber != null) {
StringBuilder number = new StringBuilder();
msg[0] = HPlusConstants.CMD_ACTION_DISPLAY_TEXT_NAME_CN;
builder.write(ctrlCharacteristic, msg);
//Clean up number as the device only accepts digits
for (char c : rawNumber.toCharArray()) {
if (Character.isDigit(c)) {
number.append(c);
}
}
builder.wait(200);
msg = msg.clone();
byte[] msg = new byte[13];
//Show call number
for (int i = 0; i < msg.length; i++)
msg[i] = ' ';
//Show call number
for (int i = 0; i < msg.length; i++)
msg[i] = ' ';
for (int i = 0; i < number.length() && i < (msg.length - 1); i++)
msg[i + 1] = (byte) number.charAt(i);
for (int i = 0; i < number.length() && i < (msg.length - 1); i++)
msg[i + 1] = (byte) number.charAt(i);
msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER;
msg[0] = HPlusConstants.CMD_SET_INCOMING_CALL_NUMBER;
builder.write(ctrlCharacteristic, msg);
builder.wait(200);
builder.write(ctrlCharacteristic, msg);
}
builder.queue(getQueue());
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Error showing incoming call: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
@ -718,7 +727,6 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
}
private void showText(String title, String body) {
try {
TransactionBuilder builder = performInitialized("notification");
@ -736,6 +744,8 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
int length = messageBytes.length / 17;
length = length > 5 ? 5 : length;
builder.write(ctrlCharacteristic, new byte[]{HPlusConstants.CMD_SET_INCOMING_MESSAGE, HPlusConstants.ARG_INCOMING_MESSAGE});
int remaining = Math.min(255, (messageBytes.length % 17 > 0) ? length + 1 : length);
@ -772,7 +782,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
msg[2] = (byte) remaining;
builder.write(ctrlCharacteristic, msg);
builder.queue(getQueue());
performConnected(builder.getTransaction());
} catch (IOException e) {
GB.toast(getContext(), "Error showing device Notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
@ -782,6 +792,7 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
private void close() {
if (syncHelper != null) {
syncHelper.quit();
syncHelper.interrupt();
syncHelper = null;
}
}
@ -803,13 +814,13 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
byte[] cs;
if (HPlusConstants.transliterateMap.containsKey(c)) {
cs = new byte[]{HPlusConstants.transliterateMap.get(c)};
cs = HPlusConstants.transliterateMap.get(c);
} else {
try {
if (HPlusCoordinator.getLanguage(this.gbDevice.getAddress()) == HPlusConstants.ARG_LANGUAGE_CN)
cs = c.toString().getBytes("GB2312");
if(HPlusCoordinator.getUnicodeSupport(this.gbDevice.getAddress()))
cs = c.toString().getBytes("Unicode");
else
cs = c.toString().getBytes("UTF-8");
cs = c.toString().getBytes("GB2312");
} catch (UnsupportedEncodingException e) {
//Fallback. Result string may be strange, but better than nothing
cs = c.toString().getBytes();
@ -836,10 +847,11 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
switch (data[0]) {
case HPlusConstants.DATA_VERSION:
case HPlusConstants.DATA_VERSION1:
return syncHelper.processVersion(data);
case HPlusConstants.DATA_STATS:
boolean result = syncHelper.processRealtimeStats(data);
boolean result = syncHelper.processRealtimeStats(data, HPlusCoordinator.getUserAge());
if (result) {
processExtraInfo (data);
}
@ -853,17 +865,19 @@ public class HPlusSupport extends AbstractBTLEDeviceSupport {
case HPlusConstants.DATA_DAY_SUMMARY:
case HPlusConstants.DATA_DAY_SUMMARY_ALT:
return syncHelper.processIncomingDaySlotData(data);
return syncHelper.processIncomingDaySlotData(data, HPlusCoordinator.getUserAge());
case HPlusConstants.DATA_UNKNOWN:
return true;
default:
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + data[0]);
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + Arrays.toString(data));
return true;
}
}
private void processExtraInfo (byte[] data) {
try {
HPlusDataRecordRealtime record = new HPlusDataRecordRealtime(data);
HPlusDataRecordRealtime record = new HPlusDataRecordRealtime(data, HPlusCoordinator.getUserAge());
handleBatteryInfo(record.battery);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Daniele Gobbetti
/* Copyright (C) 2016-2017 Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2016-2017 Andreas Shimokawa, Daniele Gobbetti
/* Copyright (C) 2016-2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti
This file is part of Gadgetbridge.

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2017 Daniele Gobbetti
/* Copyright (C) 2017 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.

View File

@ -30,6 +30,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SimpleTimeZone;
@ -514,6 +515,8 @@ public class PebbleProtocol extends GBDeviceProtocol {
public byte[] encodeAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
long id = calendarEventSpec.id != -1 ? calendarEventSpec.id : mRandom.nextLong();
int iconId;
ArrayList<Pair<Integer, Object>> attributes = new ArrayList<>();
attributes.add(new Pair<>(1, (Object) calendarEventSpec.title));
switch (calendarEventSpec.type) {
case CalendarEventSpec.TYPE_SUNRISE:
iconId = PebbleIconID.SUNRISE;
@ -523,9 +526,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
break;
default:
iconId = PebbleIconID.TIMELINE_CALENDAR;
attributes.add(new Pair<>(3, (Object) calendarEventSpec.description));
attributes.add(new Pair<>(11, (Object) calendarEventSpec.location));
}
return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) (calendarEventSpec.durationInSeconds / 60), iconId, calendarEventSpec.title, calendarEventSpec.description);
return encodeTimelinePin(new UUID(GB_UUID_MASK | calendarEventSpec.type, id), calendarEventSpec.timestamp, (short) (calendarEventSpec.durationInSeconds / 60), iconId, attributes);
}
@Override
@ -838,7 +843,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
return buf.array();
}
private byte[] encodeTimelinePin(UUID uuid, int timestamp, short duration, int icon_id, String title, String subtitle) {
private byte[] encodeTimelinePin(UUID uuid, int timestamp, short duration, int icon_id, List<Pair<Integer, Object>> attributes) {
final short TIMELINE_PIN_LENGTH = 46;
//FIXME: dont depend layout on icon :P
@ -847,13 +852,25 @@ public class PebbleProtocol extends GBDeviceProtocol {
layout_id = 0x02;
}
icon_id |= 0x80000000;
byte attributes_count = 2;
byte attributes_count = 1;
byte actions_count = 0;
int attributes_length = 10 + title.getBytes().length;
if (subtitle != null && !subtitle.isEmpty()) {
attributes_length += 3 + subtitle.getBytes().length;
attributes_count += 1;
int attributes_length = 7;
for (Pair<Integer, Object> pair : attributes) {
if (pair.first == null || pair.second == null)
continue;
attributes_count++;
if (pair.second instanceof Integer) {
attributes_length += 7;
} else if (pair.second instanceof Byte) {
attributes_length += 4;
} else if (pair.second instanceof String) {
attributes_length += ((String) pair.second).getBytes().length + 3;
} else if (pair.second instanceof byte[]) {
attributes_length += ((byte[]) pair.second).length + 3;
} else {
LOG.warn("unsupported type for timeline attributes: " + pair.second.getClass().toString());
}
}
int pin_length = TIMELINE_PIN_LENGTH + attributes_length;
@ -878,13 +895,24 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.put((byte) 4); // icon
buf.putShort((short) 4); // length of int
buf.putInt(icon_id);
buf.put((byte) 1); // title
buf.putShort((short) title.getBytes().length);
buf.put(title.getBytes());
if (subtitle != null && !subtitle.isEmpty()) {
buf.put((byte) 2); //subtitle
buf.putShort((short) subtitle.getBytes().length);
buf.put(subtitle.getBytes());
for (Pair<Integer, Object> pair : attributes) {
if (pair.first == null || pair.second == null)
continue;
buf.put(pair.first.byteValue());
if (pair.second instanceof Integer) {
buf.putShort((short) 4);
buf.putInt(((Integer) pair.second));
} else if (pair.second instanceof Byte) {
buf.putShort((short) 1);
buf.put((Byte) pair.second);
} else if (pair.second instanceof String) {
buf.putShort((short) ((String) pair.second).getBytes().length);
buf.put(((String) pair.second).getBytes());
} else if (pair.second instanceof byte[]) {
buf.putShort((short) ((byte[]) pair.second).length);
buf.put((byte[]) pair.second);
}
}
return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array());
@ -2345,13 +2373,30 @@ public class PebbleProtocol extends GBDeviceProtocol {
GBDeviceEventSendBytes sendBytes = new GBDeviceEventSendBytes();
if (command == 0x01) { //session setup
sendBytes.encodedBytes = null;
} else if (command == 0x02) { //dictation result
int replLenght = 7;
byte replStatus = 5; // 5 = disabled, change to 0 to send success
ByteBuffer repl = ByteBuffer.allocate(LENGTH_PREFIX + replLenght);
repl.order(ByteOrder.BIG_ENDIAN);
repl.putShort((short) replLenght);
repl.putShort(ENDPOINT_VOICECONTROL);
repl.put(command);
repl.putInt(flags);
repl.put(session_type);
repl.put(replStatus);
sendBytes.encodedBytes = repl.array();
} else if (command == 0x02) { //dictation result (possibly it is something we send, not something we receive)
sendBytes.encodedBytes = null;
}
return sendBytes;
}
private GBDeviceEvent decodeAudioStream(ByteBuffer buf) {
return null;
}
@Override
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
ByteBuffer buf = ByteBuffer.wrap(responseData);
@ -2611,11 +2656,13 @@ public class PebbleProtocol extends GBDeviceProtocol {
case ENDPOINT_APPLOGS:
decodeAppLogs(buf);
break;
// case ENDPOINT_VOICECONTROL:
// devEvts = new GBDeviceEvent[]{decodeVoiceControl(buf)};
// case ENDPOINT_AUDIOSTREAM:
// LOG.debug(GB.hexdump(responseData, 0, responseData.length));
// break;
case ENDPOINT_VOICECONTROL:
devEvts = new GBDeviceEvent[]{decodeVoiceControl(buf)};
break;
case ENDPOINT_AUDIOSTREAM:
devEvts = new GBDeviceEvent[]{decodeAudioStream(buf)};
// LOG.debug("AUDIOSTREAM DATA: " + GB.hexdump(responseData, 4, length));
break;
default:
break;
}

View File

@ -148,6 +148,7 @@ public class PebbleSupport extends AbstractSerialDeviceSupport {
}
}
if (reconnect()) {
super.onDeleteNotification(notificationSpec.id); //update notification hack
super.onNotification(notificationSpec);
}
}

View File

@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
public class FileUtils {
// Don't use slf4j here -- would be a bootstrapping problem
@ -209,9 +210,11 @@ public class FileUtils {
// the first directory is also the primary external storage, i.e. the same as Environment.getExternalFilesDir()
// TODO: check the mount state of *all* dirs when switching to later API level
if (i == 0 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
GB.log("ignoring unmounted external storage dir: " + dir, GB.INFO, null);
continue;
if (!GBEnvironment.env().isLocalTest()) { // don't do this with robolectric
if (i == 0 && !Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
GB.log("ignoring unmounted external storage dir: " + dir, GB.INFO, null);
continue;
}
}
result.add(dir); // add last
}

View File

@ -60,12 +60,8 @@ public class GB {
public static final String DISPLAY_MESSAGE_MESSAGE = "message";
public static final String DISPLAY_MESSAGE_DURATION = "duration";
public static final String DISPLAY_MESSAGE_SEVERITY = "severity";
public static GBEnvironment environment;
public static Notification createNotification(String text, boolean connected, Context context) {
if (env().isLocalTest()) {
return null;
}
Intent notificationIntent = new Intent(context, ControlCenterv2.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
@ -80,7 +76,7 @@ public class GB {
.setContentIntent(pendingIntent)
.setOngoing(true);
if (GBApplication.isRunningLollipopOrLater()) {
builder.setVisibility(Notification.VISIBILITY_PUBLIC);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
}
if (GBApplication.minimizeNotification()) {
builder.setPriority(Notification.PRIORITY_MIN);
@ -227,7 +223,7 @@ public class GB {
*/
public static void toast(final Context context, final String message, final int displayTime, final int severity, final Throwable ex) {
log(message, severity, ex); // log immediately, not delayed
if (env().isLocalTest()) {
if (GBEnvironment.env().isLocalTest()) {
return;
}
Looper mainLooper = Looper.getMainLooper();
@ -272,7 +268,7 @@ public class GB {
notificationIntent, 0);
NotificationCompat.Builder nb = new NotificationCompat.Builder(context)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(text)
.setContentIntent(pendingIntent)
@ -358,15 +354,15 @@ public class GB {
}
public static void updateBatteryNotification(String text, String bigText, Context context) {
if (env().isLocalTest()) {
if (GBEnvironment.env().isLocalTest()) {
return;
}
Notification notification = createBatteryNotification(text, bigText, context);
updateNotification(notification, NOTIFICATION_ID_LOW_BATTERY, context);
}
public static GBEnvironment env() {
return environment;
public static void removeBatteryNotification(Context context) {
removeNotification(NOTIFICATION_ID_LOW_BATTERY, context);
}
public static void assertThat(boolean condition, String errorMessage) {

View File

@ -20,7 +20,7 @@ import java.text.ParseException;
import java.util.Date;
public class GBPrefs {
public static final String PACKAGE_BLACKLIST = "package_blacklist";
public static final String AUTO_RECONNECT = "general_autocreconnect";
private static final String AUTO_START = "general_autostartonboot";
private static final boolean AUTO_START_DEFAULT = true;
@ -61,7 +61,7 @@ public class GBPrefs {
}
}
public int getUserSex() {
public int getUserGender() {
return 0;
}
}

View File

@ -1,3 +1,19 @@
/* Copyright (C) 2017 Alberto, Carsten Pfeiffer
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util;
@ -17,7 +33,7 @@ import org.xmlpull.v1.XmlSerializer;
import android.content.SharedPreferences;
import android.util.Xml;
import static nodomain.freeyourgadget.gadgetbridge.GBApplication.blacklist;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
public class ImportExportSharedPreferences {
@ -26,7 +42,7 @@ public class ImportExportSharedPreferences {
private static final String INTEGER = Integer.class.getSimpleName();
private static final String LONG = Long.class.getSimpleName();
private static final String STRING = String.class.getSimpleName();
private static final String HASTSET = HashSet.class.getSimpleName();
private static final String HASHSET = HashSet.class.getSimpleName();
private static final String NAME = "name";
private static final String PREFERENCES = "preferences";
@ -107,14 +123,14 @@ public class ImportExportSharedPreferences {
editor.putLong(key, Long.parseLong(text));
} else if (STRING.equals(name)) {
editor.putString(key, text);
} else if (HASTSET.equals(name)) {
if (key.equals("package_blacklist")) {
blacklist.clear();
} else if (HASHSET.equals(name)) {
if (key.equals(GBPrefs.PACKAGE_BLACKLIST)) {
Set<String> blacklist = new HashSet<>();
text=text.replace("[","").replace("]","");
for (int z=0;z<text.split(",").length;z++){
blacklist.add(text.split(",")[z].trim());
}
editor.putStringSet(key, blacklist);
GBApplication.setBlackList(blacklist);
}
} else if (!PREFERENCES.equals(name)) {
throw new Exception("Unkown type " + name);

View File

@ -79,6 +79,7 @@ public class UriHelper {
* Opens a stream to read the contents of the uri.
* Note: the caller has to close the stream after usage.
* Every invocation of this method will open a new stream.
* FIXME: make sure that every caller actually closes the returned stream!
* @throws FileNotFoundException
*/
@NonNull
@ -127,27 +128,31 @@ public class UriHelper {
if (cursor == null) {
throw new IOException("Unable to query metadata for: " + uri);
}
if (cursor.moveToFirst()) {
int name_index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
if (name_index == -1) {
throw new IOException("Unable to retrieve name for: " + uri);
}
int size_index = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE);
if (size_index == -1) {
throw new IOException("Unable to retrieve size for: " + uri);
}
try {
fileName = cursor.getString(name_index);
if (fileName == null) {
try {
if (cursor.moveToFirst()) {
int name_index = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
if (name_index == -1) {
throw new IOException("Unable to retrieve name for: " + uri);
}
fileSize = cursor.getLong(size_index);
if (fileSize < 0) {
int size_index = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE);
if (size_index == -1) {
throw new IOException("Unable to retrieve size for: " + uri);
}
} catch (Exception ex) {
throw new IOException("Unable to retrieve metadata for: " + uri + ": " + ex.getMessage());
try {
fileName = cursor.getString(name_index);
if (fileName == null) {
throw new IOException("Unable to retrieve name for: " + uri);
}
fileSize = cursor.getLong(size_index);
if (fileSize < 0) {
throw new IOException("Unable to retrieve size for: " + uri);
}
} catch (Exception ex) {
throw new IOException("Unable to retrieve metadata for: " + uri + ": " + ex.getMessage());
}
}
} finally {
cursor.close();
}
} else if (ContentResolver.SCHEME_FILE.equals(uriScheme)) {
file = new File(uri.getPath());

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 B

View File

@ -30,7 +30,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_gravity="bottom|end"
android:src="@drawable/ic_add_white"
app:srcCompat="@drawable/ic_add"
app:elevation="6dp"
app:pressedTranslationZ="12dp"
android:layout_marginBottom="30dp"

View File

@ -33,7 +33,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_gravity="bottom|end"
android:src="@drawable/ic_add_white"
app:srcCompat="@drawable/ic_add"
app:elevation="6dp"
app:pressedTranslationZ="12dp"
android:layout_marginBottom="30dp"

View File

@ -104,8 +104,11 @@
<string name="pref_summary_enable_outgoing_call">Falls dies deaktiviert wird, vibriert die Pebble 2/LE nicht bei ausgehenden Anrufen</string>
<string name="pref_title_enable_pebblekit">Erlaube Zugriff von anderen Android Apps</string>
<string name="pref_summary_enable_pebblekit">Experimentelle Unterstützung für Android Apps, die PebbleKit benutzen</string>
<string name="pref_header_pebble_timeline">Pebble Zeitstrang</string>
<string name="pref_title_sunrise_sunset">Sonnenauf- und -untergang </string>
<string name="pref_summary_sunrise_sunset">Sende Sonnenauf- und -untergangszeiten abhänging vom Standort auf die Pebble Timeline</string>
<string name="pref_title_enable_calendar_sync">Synchronisiere Kalender </string>
<string name="pref_summary_enable_calendar_sync">Sende Termine an den Zeitstrang</string>
<string name="pref_title_autoremove_notifications">Verworfene Benachrichtigungen automatisch entfernen</string>
<string name="pref_summary_autoremove_notifications">Benachrichtigungen werden automatisch von der Pebble entfernt, wenn sie auf dem Android-Gerät verworfen werden</string>
<string name="pref_title_pebble_privacy_mode">Privatsphäre-Modus</string>
@ -268,6 +271,8 @@
<string name="updatefirmwareoperation_update_complete_rebooting">Firmware-Installation erfolgreich beendet, Gerät wird neu gestartet…</string>
<string name="updatefirmwareoperation_write_failed">Schreiben der Firmware fehlgeschlagen</string>
<string name="chart_steps">Schritte</string>
<string name="calories">Kalorien </string>
<string name="distance">Distanz </string>
<string name="liveactivity_live_activity">Live Aktivität</string>
<string name="weeksteps_today_steps_description">Schritte heute, Ziel: %1$s</string>
<string name="pref_title_dont_ack_transfer">Transfer von Aktivitätsdaten nicht bestätigen</string>
@ -326,10 +331,12 @@
<string name="dbmanagementactivvity_cannot_access_export_path">Kann nicht auf den Exportpfad zugreifen. Bitte die Entwickler kontaktieren.</string>
<string name="dbmanagementactivity_exported_to">Exportiert nach: %1$s</string>
<string name="dbmanagementactivity_error_exporting_db">Fehler beim Exportieren der DB: %1$s</string>
<string name="dbmanagementactivity_error_exporting_shared">Fehler beim Exlortieren der Einstellungen: %1$s</string>
<string name="dbmanagementactivity_import_data_title">Daten importieren?</string>
<string name="dbmanagementactivity_overwrite_database_confirmation">Wirklich die aktuelle Datenbank überschreiben? Alle aktuellen Aktivitätsdaten (sofern vorhanden) gehen verloren!</string>
<string name="dbmanagementactivity_import_successful">Import erfolgreich.</string>
<string name="dbmanagementactivity_error_importing_db">Fehler beim Importieren der DB: %1$s</string>
<string name="dbmanagementactivity_error_importing_shared">Fehler beim Importieren der Einstellungen: %1$s</string>
<string name="dbmanagementactivity_delete_activity_data_title">Aktivitätsdaten löschen?</string>
<string name="dbmanagementactivity_really_delete_entire_db">Wirklich die komplette Datenbank löschen? Alle Aktivitätsdaten und Informationen über Deine Geräte gehen verloren!</string>
<string name="dbmanagementactivity_database_successfully_deleted">Daten erfolgreich gelöscht.</string>

View File

@ -104,8 +104,11 @@
<string name="pref_summary_enable_outgoing_call">Desactivar esto evitará que el Pebble 2/LE vibre en llamadas salientes</string>
<string name="pref_title_enable_pebblekit">Permitir el acceso a aplicaciones Android de terceros</string>
<string name="pref_summary_enable_pebblekit">Permitir el soporte experimental para aplicaciones Android que usan PebbleKit</string>
<string name="pref_header_pebble_timeline">Timeline Pebble</string>
<string name="pref_title_sunrise_sunset">Salida y puesta de Sol</string>
<string name="pref_summary_sunrise_sunset">Enviar las horas de salida y puesta de Sol basándose en la localización a la línea cronológica del Pebble</string>
<string name="pref_title_enable_calendar_sync">Sincronizar calendario</string>
<string name="pref_summary_enable_calendar_sync">Enviar los eventos del calendario a la timeline</string>
<string name="pref_title_autoremove_notifications">Eliminar automáticamente las notificaciones rechazadas</string>
<string name="pref_summary_autoremove_notifications">Las notificaciones se borran automáticamente de Pebble cuando son rechazadas en Android</string>
<string name="pref_title_pebble_privacy_mode">Modo privado</string>
@ -296,7 +299,7 @@
<string name="miband_prefs_device_time_offset_hours">Compensación de la hora del dispositivo en horas (para detectar el sueño de trabajadores a turnos)</string>
<string name="miband2_prefs_dateformat">Mi2: Formato de fecha</string>
<string name="dateformat_time">Tiempo</string>
<string name="dateformat_date_time"><![CDATA[Time & Date]]></string>
<string name="dateformat_date_time"><![CDATA[Fecha y hora]]></string>
<string name="mi2_prefs_activate_display_on_lift">Activar pantalla al levantar</string>
<string name="FetchActivityOperation_about_to_transfer_since">A punto de transferir datos desde %1$s</string>
<string name="waiting_for_reconnect">esperando reconexión</string>
@ -323,7 +326,7 @@
<string name="pref_summary_pebble_health_store_raw">Seleccionado, los datos archivados se guardan en bruto y están disponibles para ser interpretados más tarde. La base de datos será más grande. </string>
<string name="action_db_management">Administración de Bases de Datos</string>
<string name="title_activity_db_management">Administración de Bases de Datos</string>
<string name="activity_db_management_import_export_explanation">La base de datos usa la siguiente ubicación en su dispositivo. \nEsta ubicación está accesible para otras aplicaciones Android y para su ordenador. \nEncontrará sus bases de datos exportadas (o la que quiere importar) aquí:</string>
<string name="activity_db_management_import_export_explanation">La base de datos usa la siguiente ubicación en su dispositivo.\nEsta ubicación está accesible para otras aplicaciones Android y para su ordenador.\nEncontrará sus bases de datos exportadas (o la que quiere importar) aquí:</string>
<string name="activity_db_management_merge_old_title">Borrar la base de datos antigua</string>
<string name="dbmanagementactivvity_cannot_access_export_path">No se puede acceder a la ruta para exportar . Por favor, contacta con los desarrolladores.</string>
<string name="dbmanagementactivity_exported_to">Exportado a: %1$s</string>

View File

@ -104,8 +104,11 @@
<string name="pref_summary_enable_outgoing_call">Désactiver ceci empêchera la Pebble 2/LE de vibrer lors des appels sortants</string>
<string name="pref_title_enable_pebblekit">Permettre l\'accès aux applications tierces Android</string>
<string name="pref_summary_enable_pebblekit">Activer le support expérimental pour les applications Android utilisant PebbleKit</string>
<string name="pref_header_pebble_timeline">Timeline Pebble</string>
<string name="pref_title_sunrise_sunset">Lever et coucher de soleil</string>
<string name="pref_summary_sunrise_sunset">Envoyer les heures de lever et coucher du soleil dans lhistorique Pebble en fonction de l\'emplacement</string>
<string name="pref_title_enable_calendar_sync">Synchroniser le calendrier</string>
<string name="pref_summary_enable_calendar_sync">Envoyer les événements du calendrier sur la timeline</string>
<string name="pref_title_autoremove_notifications">Effacer automatiquement les notifications rejetées</string>
<string name="pref_summary_autoremove_notifications">Les notifications sont automatiquement effacées de la Pebble lorsqu\'elles sont rejetées sur l\'appareil Android</string>
<string name="pref_title_pebble_privacy_mode">Mode privé</string>
@ -296,7 +299,7 @@
<string name="miband_prefs_device_time_offset_hours">La compensation de temps en heure (pour travailleurs en rotation, par exemple)</string>
<string name="miband2_prefs_dateformat">Mi2 : Format de la date</string>
<string name="dateformat_time">Heure</string>
<string name="dateformat_date_time"><![CDATA[Time & Date]]></string>
<string name="dateformat_date_time"><![CDATA[Date et heure]]></string>
<string name="mi2_prefs_activate_display_on_lift">Allumer l\'écran lors d\'un mouvement</string>
<string name="FetchActivityOperation_about_to_transfer_since">Sur le point de transférer des données depuis %1$s</string>
<string name="waiting_for_reconnect">en attente de reconnexion</string>
@ -325,7 +328,7 @@ Temps de sommeil péféré en heures</string>
NOTE: la base de données sera bien évidement plus grande !</string>
<string name="action_db_management">Gestion de base de données</string>
<string name="title_activity_db_management">Gestion de base de données</string>
<string name="activity_db_management_import_export_explanation">Les opérations sur la base de donnée ont utilisé le chemin suivant sur votre appareil.\n Ce chemin n\'est pas accessible par d\'autres applications Android ou par votre ordinateur./nVous trouverez votre base de données (ou celle que vous souhaitez importer) ici:</string>
<string name="activity_db_management_import_export_explanation">Les opérations sur la base de donnée ont utilisé le chemin suivant sur votre appareil.\nCe chemin n\'est pas accessible par d\'autres applications Android ou par votre ordinateur.\nVous trouverez votre base de données (ou celle que vous souhaitez importer) ici:</string>
<string name="activity_db_management_merge_old_title">Effacer l\'ancienne base de données</string>
<string name="dbmanagementactivvity_cannot_access_export_path">Impossible d\'accéder au fichier d\'export. Merci de contacter les développeurs.</string>
<string name="dbmanagementactivity_exported_to">Exporter vers : %1$s</string>
@ -365,7 +368,7 @@ NOTE: la base de données sera bien évidement plus grande !</string>
<string name="miband2_prefs_timeformat">Mi2: format de l\'heure</string>
<string name="mi2_fw_installhandler_fw53_hint">Vous devez installer la version %1$s avant d\'installer ce micrologiciel!</string>
<string name="mi2_enable_text_notifications">Notifications textuelles</string>
<string name="mi2_enable_text_notifications_summary"><![CDATA[Needs firmware >= 1.0.1.28 and Mili_pro.ft* installed.]]></string>
<string name="mi2_enable_text_notifications_summary"><![CDATA[Requis: micrologiciel version 1.0.1.28 ou plus, fichier Mili_pro.ft* installé.]]></string>
<string name="off">off</string>
<string name="discovery_attempting_to_pair">Tentative d\'appairage avec %1$s</string>
<string name="discovery_bonding_failed_immediately">Le lien avec %1$s a échoué instantanément</string>

View File

@ -104,8 +104,11 @@
<string name="pref_summary_enable_outgoing_call">これを無効にすると、Pebble 2/LE は発信時に振動しなくなります</string>
<string name="pref_title_enable_pebblekit">第三者のアンドロイドアップにアクセス権利を与える</string>
<string name="pref_summary_enable_pebblekit">PebbleKitを使用してAndroidアプリ用の実験的なサポートを有効にします</string>
<string name="pref_header_pebble_timeline">Pebbleタイムライン</string>
<string name="pref_title_sunrise_sunset">日の出と日の入り</string>
<string name="pref_summary_sunrise_sunset">場所に基づいて、Pebble のタイムラインに日の出・日の入りの時間を送ります</string>
<string name="pref_title_enable_calendar_sync">カレンダーを同期</string>
<string name="pref_summary_enable_calendar_sync">カレンダーのイベントをタイムラインに送信します</string>
<string name="pref_title_autoremove_notifications">消去した通知を自動削除</string>
<string name="pref_summary_autoremove_notifications">通知は、Androidデバイスから削除されたときに、Pebbleから自動的に削除されます</string>
<string name="pref_title_pebble_privacy_mode">プライバシーモード</string>

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
<string name="app_name">Gadgetbridge</string>
<string name="title_activity_controlcenter">Gadgetbridge</string>
<string name="action_settings">Configurações</string>
<string name="action_debug">Depurar</string>
<string name="action_quit">Sair</string>
@ -13,12 +13,12 @@
<string name="controlcenter_delete_device">Apagar dispositivo</string>
<string name="controlcenter_delete_device_name">Apagar %1$s</string>
<string name="controlcenter_delete_device_dialogmessage">Isto irá apagar o dispositivo e apagar os dados associados!</string>
<string name="controlcenter_navigation_drawer_open">Abrir gaveta de navegação</string>
<string name="controlcenter_navigation_drawer_close">Fechar gaveta de navegação</string>
<string name="controlcenter_snackbar_need_longpress">Pressione o cartão longamente para desligar</string>
<string name="controlcenter_snackbar_disconnecting">A desligar</string>
<string name="controlcenter_snackbar_connecting">A ligar</string>
<string name="controlcenter_snackbar_requested_screenshot">Capturar o ecrã do dispositivo</string>
<string name="controlcenter_navigation_drawer_open">Abrir gaveta de navegação</string>
<string name="controlcenter_navigation_drawer_close">Fechar gaveta de navegação</string>
<string name="controlcenter_snackbar_need_longpress">Pressione o cartão longamente para desligar</string>
<string name="controlcenter_snackbar_disconnecting">A desligar</string>
<string name="controlcenter_snackbar_connecting">A ligar</string>
<string name="controlcenter_snackbar_requested_screenshot">Capturar o ecrã do dispositivo</string>
<string name="title_activity_debug">Depurar</string>
<!--Strings related to AppManager-->
<string name="title_activity_appmanager">Administrador de App</string>
@ -28,12 +28,12 @@
<string name="appmananger_app_delete">Apagar</string>
<string name="appmananger_app_delete_cache">Apagar e remover do cache</string>
<string name="appmananger_app_reinstall">Reinstalar</string>
<string name="appmanager_app_openinstore">Buscar na loja Pebble</string>
<string name="appmanager_app_openinstore">Buscar na loja Pebble</string>
<string name="appmanager_health_activate">Ativar</string>
<string name="appmanager_health_deactivate">Desativar</string>
<string name="appmanager_hrm_activate">Ativar HRM</string>
<string name="appmanager_hrm_deactivate">Desativar HRM</string>
<string name="appmanager_weather_activate">Ativas app de clima do sistema</string>
<string name="appmanager_hrm_activate">Ativar Mon. Ritmo Cardíaco</string>
<string name="appmanager_hrm_deactivate">Desativar Mon. Ritmo Cardíaco</string>
<string name="appmanager_weather_activate">Ativas app de clima do sistema</string>
<string name="appmanager_weather_deactivate">Desativar app de clima do sistema</string>
<string name="appmanager_weather_install_provider">Instalar notificações do app de clima</string>
<string name="app_configure">Configurar</string>
@ -43,10 +43,10 @@
<!--Strings related to FwAppInstaller-->
<string name="title_activity_fw_app_insaller">Instalador FW/App</string>
<string name="fw_upgrade_notice">Está prestes a instalar o firmware %s no lugar do atual na sua Mi Band.</string>
<string name="fw_multi_upgrade_notice">Estás prestes a instalar os firmwares %1$s e %2$s em vez dos que estão atualmente na sua Mi Band.</string>
<string name="fw_multi_upgrade_notice">Estás prestes a instalar os firmwares %1$s e %2$s em vez dos que estão atualmente na sua Mi Band.</string>
<string name="miband_firmware_known">O firmware foi testado e é compatível com o Gadgetbridge.</string>
<string name="miband_firmware_unknown_warning">Este firmware não foi testado e pode não ser compatível com o Gadgetbridge.\n\nÉ preferível que NÃO o instale na sua Mi Band!</string>
<string name="miband_firmware_suggest_whitelist">Se ainda assim quiser continuar e tudo continuar a funcionar normalmente, por favor diga aos programadores do Gadgetbridge para permitir o firmware versão: %s</string>
<string name="miband_firmware_unknown_warning">Este firmware não foi testado e pode não ser compatível com o Gadgetbridge.\n\nÉ preferível que NÃO o instale na sua Mi Band!</string>
<string name="miband_firmware_suggest_whitelist">Se ainda assim quiser continuar e tudo continuar a funcionar normalmente, por favor diga aos programadores do Gadgetbridge para permitir o firmware versão: %s</string>
<!--Strings related to Settings-->
<string name="title_activity_settings">Configurações</string>
<string name="pref_header_general">Configurações Gerais</string>
@ -57,7 +57,7 @@
<string name="pref_default">Padrão</string>
<string name="pref_header_datetime">Data e Hora</string>
<string name="pref_title_datetime_syctimeonconnect">Sincronizar hora</string>
<string name="pref_summary_datetime_syctimeonconnect">Sincronizar a hora para o dispositivo a quando da ligação e quando a hora ou fuso horário se alterarem no Android</string>
<string name="pref_summary_datetime_syctimeonconnect">Sincronizar a hora para o dispositivo a quando da ligação e quando a hora ou fuso horário se alterarem no Android</string>
<string name="pref_title_theme">Tema</string>
<string name="pref_theme_light">Claro</string>
<string name="pref_theme_dark">Escuro</string>
@ -73,10 +73,10 @@
<string name="pref_summary_notifications_pebblemsg">Suportar notificações de aplicações que enviam notificações pelo PebbleKit.</string>
<string name="pref_title_notifications_generic">Suportar notificações genéricas</string>
<string name="pref_title_whenscreenon">... e quando o ecrã estiver ligado</string>
<string name="pref_title_notification_filter">Não incomodar</string>
<string name="pref_summary_notification_filter">Ignorar notificações indesejadas enquanto estiver no modo Não Incomodar.</string>
<string name="pref_title_transliteration">Transliteração</string>
<string name="pref_summary_transliteration">Ativar apenas se o seu dispositivo não suportar a sua caracteres da sua língua (Atualmente apenas Cirílico)</string>
<string name="pref_title_notification_filter">Não incomodar</string>
<string name="pref_summary_notification_filter">Ignorar notificações indesejadas enquanto estiver no modo Não Incomodar.</string>
<string name="pref_title_transliteration">Transliteração</string>
<string name="pref_summary_transliteration">Ativar apenas se o seu dispositivo não suportar a sua caracteres da sua língua (Atualmente apenas Cirílico)</string>
<string name="always">sempre</string>
<string name="when_screen_off">quando o ecrã estiver desligado</string>
<string name="never">nunca</string>
@ -84,7 +84,7 @@
<string name="pref_title_call_privacy_mode">Modo de chamada privada</string>
<string name="pref_call_privacy_mode_off">Exibir nome e número</string>
<string name="pref_call_privacy_mode_name">Ocultar nome e exibir número</string>
<string name="pref_call_privacy_mode_number">Ocular o número e exibir o nome</string>
<string name="pref_call_privacy_mode_number">Ocular o número e exibir o nome</string>
<string name="pref_call_privacy_mode_complete">Ocultar nome e número</string>
<string name="pref_blacklist">Aplicações ignoradas</string>
<string name="pref_header_cannned_messages">Histórico de mensagens</string>
@ -100,17 +100,17 @@
<string name="pref_title_pebble_sync_health">Sincronizar com Pebble Health</string>
<string name="pref_title_pebble_sync_misfit">Sincronizar com Misfit</string>
<string name="pref_title_pebble_sync_morpheuz">Sincronizar com Morpheuz</string>
<string name="pref_title_enable_outgoing_call">Suportar chamadas efetuadas</string>
<string name="pref_summary_enable_outgoing_call">Desactivar isto também irá fazer com que o Pebble 2/LE não vibre durante as chamadas efetuadas</string>
<string name="pref_title_enable_pebblekit">Permitir acesso por Apps Android de terceiros.</string>
<string name="pref_title_enable_outgoing_call">Suportar chamadas efetuadas</string>
<string name="pref_summary_enable_outgoing_call">Desactivar isto também irá fazer com que o Pebble 2/LE não vibre durante as chamadas efetuadas</string>
<string name="pref_title_enable_pebblekit">Permitir acesso por Apps Android de terceiros.</string>
<string name="pref_summary_enable_pebblekit">Ativar suporte experimental a Apps Android que usem PebbleKit</string>
<string name="pref_title_sunrise_sunset">Despertar e pôr do sol</string>
<string name="pref_summary_sunrise_sunset">Enviar despertar e pôr do sol baseado na localização do Pebble</string>
<string name="pref_summary_sunrise_sunset">Enviar despertar e pôr do sol baseado na localização do Pebble</string>
<string name="pref_title_autoremove_notifications">Auto remover notificações rejeitadas</string>
<string name="pref_summary_autoremove_notifications">Notificações são automaticamente removidas quando rejeitadas no Android</string>
<string name="pref_title_pebble_privacy_mode">Modo de privacidade</string>
<string name="pref_pebble_privacy_mode_off">Notificações</string>
<string name="pref_pebble_privacy_mode_content">Deslocar texto de notificações que extravasar o ecrã</string>
<string name="pref_pebble_privacy_mode_content">Deslocar texto de notificações que extravasar o ecrã</string>
<string name="pref_pebble_privacy_mode_complete">Apenas mostrar ícone de notificações</string>
<string name="pref_header_location">Localização</string>
<string name="pref_title_location_aquire">Obter localização</string>
@ -124,88 +124,88 @@
<string name="pref_summary_pebble_forceprotocol">Esta opção força o uso do protocolo de notificação mais recente. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER!</string>
<string name="pref_title_pebble_forceuntested">Permitir recursos não certificados</string>
<string name="pref_summary_pebble_forceuntested">Permitir recursos não certificados. APENAS ATIVE SE SOUBER O QUE ESTÁ A FAZER!</string>
<string name="pref_title_pebble_forcele">Sempre preferir BLE</string>
<string name="pref_summary_pebble_forcele">Utilizar suporte para Pebble LE em todos os Pebbles em vez do clássico BT. Necessita que se emparelhe um \"Pebble LE\" depois de dispositivos não LE se terem ligado uma vez</string>
<string name="pref_title_pebble_mtu_limit">Pebble 2/LE GATT MTU limite</string>
<string name="pref_summary_pebble_mtu_limit">Se o seu Pebble2/Pebble LE não funciona como esperado, ative esta opção para limitar o tamanho das transferências (intervalo válido é 20-512)</string>
<string name="pref_title_pebble_enable_applogs">Activar Registo da Aplicação do Dispositivo</string>
<string name="pref_summary_pebble_enable_applogs">Causa que os registos das aplicações do relógio sejam guardados pelo Gadgetbridge (necessita de religação)</string>
<string name="pref_title_pebble_always_ack_pebblekit">Antecipar confirmações do PebbleKit</string>
<string name="pref_summary_pebble_always_ack_pebblekit">Irá fazer com que as mensagens enviadas para aplicações de terceiros sejam sempre imediatamente confirmadas</string>
<string name="pref_title_pebble_reconnect_attempts">Tentativas de Ligação</string>
<string name="pref_title_pebble_forcele">Sempre preferir BLE</string>
<string name="pref_summary_pebble_forcele">Utilizar suporte para Pebble LE em todos os Pebbles em vez do clássico BT. Necessita que se emparelhe um \"Pebble LE\" depois de dispositivos não LE se terem ligado uma vez</string>
<string name="pref_title_pebble_mtu_limit">Pebble 2/LE GATT MTU limite</string>
<string name="pref_summary_pebble_mtu_limit">Se o seu Pebble2/Pebble LE não funciona como esperado, ative esta opção para limitar o tamanho das transferências (intervalo válido é 20-512)</string>
<string name="pref_title_pebble_enable_applogs">Activar Registo da Aplicação do Dispositivo</string>
<string name="pref_summary_pebble_enable_applogs">Causa que os registos das aplicações do relógio sejam guardados pelo Gadgetbridge (necessita de religação)</string>
<string name="pref_title_pebble_always_ack_pebblekit">Antecipar confirmações do PebbleKit</string>
<string name="pref_summary_pebble_always_ack_pebblekit">Irá fazer com que as mensagens enviadas para aplicações de terceiros sejam sempre imediatamente confirmadas</string>
<string name="pref_title_pebble_reconnect_attempts">Tentativas de Ligação</string>
<string name="pref_title_unit_system">Unidades</string>
<string name="pref_title_timeformat">Formato da hora</string>
<string name="pref_title_screentime">Duração do ecrã</string>
<string name="prefs_title_all_day_heart_rate">Medição Contínua de Ritmo Cardíaco</string>
<string name="preferences_hplus_settings">Configurações HPlus/Makibes</string>
<string name="prefs_title_all_day_heart_rate">Medição Contínua de Ritmo Cardíaco</string>
<string name="preferences_hplus_settings">Configurações HPlus/Makibes</string>
<string name="not_connected">desligado</string>
<string name="connecting">a ligar</string>
<string name="connected">ligado</string>
<string name="unknown_state">estado desconhecido</string>
<string name="connectionstate_hw_fw">HW: %1$s FW: %2$s</string>
<string name="connectionstate_fw">FW: %1$s</string>
<string name="_unknown_">(desconhecido)</string>
<string name="connectionstate_hw_fw">HW: %1$s FW: %2$s</string>
<string name="connectionstate_fw">Versão FW: %1$s</string>
<string name="_unknown_">(desconhecido)</string>
<string name="test">Teste</string>
<string name="test_notification">Teste de notificação</string>
<string name="this_is_a_test_notification_from_gadgetbridge">Isto é uma notificação de teste do Gadgetbridge</string>
<string name="test_notification">Teste de notificação</string>
<string name="this_is_a_test_notification_from_gadgetbridge">Isto é uma notificação de teste do Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth não suportado</string>
<string name="bluetooth_is_disabled_">Bluetooth desligado</string>
<string name="tap_connected_device_for_app_mananger">Toque num dispositivo ligado para Gerir Aplicações</string>
<string name="tap_connected_device_for_activity">Toque num dispositivo ligado para ver a Atividade</string>
<string name="tap_connected_device_for_vibration">Toque num dispositivo ligado para o fazer Vibrar</string>
<string name="tap_a_device_to_connect">Toque num dispositivo para ligar</string>
<string name="cannot_connect_bt_address_invalid_">Não foi possível ligar. Endereço Bluetooth inválido?</string>
<string name="gadgetbridge_running">Gadgetbridge a executar</string>
<string name="installing_binary_d_d">A instalar o binário %1$d/%2$d</string>
<string name="tap_connected_device_for_app_mananger">Toque num dispositivo ligado para Gerir Aplicações</string>
<string name="tap_connected_device_for_activity">Toque num dispositivo ligado para ver a Atividade</string>
<string name="tap_connected_device_for_vibration">Toque num dispositivo ligado para o fazer Vibrar</string>
<string name="tap_a_device_to_connect">Toque num dispositivo para ligar</string>
<string name="cannot_connect_bt_address_invalid_">Não foi possível ligar. Endereço Bluetooth inválido?</string>
<string name="gadgetbridge_running">Gadgetbridge a executar</string>
<string name="installing_binary_d_d">A instalar o binário %1$d/%2$d</string>
<string name="installation_failed_">falha na instalação!</string>
<string name="installation_successful">instalação bem sucedida</string>
<string name="firmware_install_warning">ESTÁ A TENTAR INSTALAR UM FIRMWARE, CONTINUE À SUA RESPONSABILIDADE.\n\n\n Este firmware é para a Versão de HW: %s</string>
<string name="app_install_info">Está prestes a instalar a aplicação seguinte::\n\n\n%1$s Versão %2$s por %3$s\n</string>
<string name="n_a">N/D</string>
<string name="firmware_install_warning">ESTÁ A TENTAR INSTALAR UM FIRMWARE, CONTINUE À SUA RESPONSABILIDADE.\n\n\n Este firmware é para a Versão de HW: %s</string>
<string name="app_install_info">Está prestes a instalar a aplicação seguinte::\n\n\n%1$s Versão %2$s por %3$s\n</string>
<string name="n_a">N/D</string>
<string name="initialized">inicializado</string>
<string name="appversion_by_creator">%1$s por %2$s</string>
<string name="title_activity_discovery">Procurar Dispositivos</string>
<string name="appversion_by_creator">%1$s por %2$s</string>
<string name="title_activity_discovery">Procurar Dispositivos</string>
<string name="discovery_stop_scanning">Terminar a procura</string>
<string name="discovery_start_scanning">Iniciar a procura</string>
<string name="action_discover">Ligue o novo dispositivo</string>
<string name="device_with_rssi">%1$s (%2$s)</string>
<string name="device_with_rssi">%1$s (%2$s)</string>
<string name="title_activity_android_pairing">Emparelhar dispositivo</string>
<string name="android_pairing_hint">Use o diálogo do sistema Android para emparelhar com o dispositivo</string>
<string name="title_activity_mi_band_pairing">Emparelhar com a sua Mi Band</string>
<string name="android_pairing_hint">Use o diálogo do sistema Android para emparelhar com o dispositivo</string>
<string name="title_activity_mi_band_pairing">Emparelhar com a sua Mi Band</string>
<string name="pairing">A emparelhar com %s...</string>
<string name="pairing_creating_bond_with">A criar ligação com %1$s (%2$s)</string>
<string name="pairing_unable_to_pair_with">Não foi possível emparelhar com %1$s (%2$s)</string>
<string name="pairing_in_progress">Associação em progresso: %1$s (%2$s)</string>
<string name="pairing_already_bonded">Já associado com %1$s (%2$s), a ligar...</string>
<string name="message_cannot_pair_no_mac">Nenhum endereço MAC fornecido, não é possível emparelhar.</string>
<string name="preferences_category_device_specific_settings">Configurações Específicas do Dispositivo</string>
<string name="preferences_miband_settings">Configurações Mi Band</string>
<string name="pairing_creating_bond_with">A criar ligação com %1$s (%2$s)</string>
<string name="pairing_unable_to_pair_with">Não foi possível emparelhar com %1$s (%2$s)</string>
<string name="pairing_in_progress">Associação em progresso: %1$s (%2$s)</string>
<string name="pairing_already_bonded">Já associado com %1$s (%2$s), a ligar...</string>
<string name="message_cannot_pair_no_mac">Nenhum endereço MAC fornecido, não é possível emparelhar.</string>
<string name="preferences_category_device_specific_settings">Configurações Específicas do Dispositivo</string>
<string name="preferences_miband_settings">Configurações Mi Band</string>
<string name="male">masculino</string>
<string name="female">feminino</string>
<string name="other">outro</string>
<string name="left">esquerda</string>
<string name="right">direita</string>
<string name="miband_pairing_using_dummy_userdata">Sem dados de utilizador. Por enquanto serão usados dados fictícios</string>
<string name="miband_pairing_tap_hint">Quando a sia Mi Band vibrar e piscar, dê-lhe alguns toques seguidos.</string>
<string name="miband_pairing_using_dummy_userdata">Sem dados de utilizador. Por enquanto serão usados dados fictícios</string>
<string name="miband_pairing_tap_hint">Quando a sia Mi Band vibrar e piscar, dê-lhe alguns toques seguidos.</string>
<string name="appinstaller_install">Instalar</string>
<string name="discovery_connected_devices_hint">Torne o seu dispositivo visível. É improvável que os dispositivos atualmente ligados sejam apresentados. Ative a localização (i.e, GPS) no Android 6+. Desative também a Proteção de Privacidade para o Gadgetbridge, pois esta poderá bloquear e levar ao reinício do seu telefone. Se depois de alguns minutos não for encontrado qualquer dispositivo, tente outra vez após reiniciar o telefone.</string>
<string name="discovery_connected_devices_hint">Torne o seu dispositivo visível. É improvável que os dispositivos atualmente ligados sejam apresentados. Ative a localização (i.e, GPS) no Android 6+. Desative também a Proteção de Privacidade para o Gadgetbridge, pois esta poderá bloquear e levar ao reinício do seu telefone. Se depois de alguns minutos não for encontrado qualquer dispositivo, tente outra vez após reiniciar o telefone.</string>
<string name="discovery_note">Nota:</string>
<string name="candidate_item_device_image">Imagem do Dispositivo</string>
<string name="candidate_item_device_image">Imagem do Dispositivo</string>
<string name="miband_prefs_alias">Nome/Apelido</string>
<string name="pref_header_vibration_count">Quantidade de vibrações</string>
<string name="title_activity_sleepmonitor">Monitor de sono</string>
<string name="pref_write_logfiles">Escrever arquivos de registo</string>
<string name="initializing">Inicializando</string>
<string name="busy_task_fetch_activity_data">A Descarregar Dados de Atividade</string>
<string name="sleep_activity_date_range">De %1$s até %2$s</string>
<string name="miband_prefs_wearside">De que lado a usa?</string>
<string name="pref_screen_vibration_profile">Perfil de Vibração</string>
<string name="vibration_profile_staccato">Destacado</string>
<string name="busy_task_fetch_activity_data">A Descarregar Dados de Atividade</string>
<string name="sleep_activity_date_range">De %1$s até %2$s</string>
<string name="miband_prefs_wearside">De que lado a usa?</string>
<string name="pref_screen_vibration_profile">Perfil de Vibração</string>
<string name="vibration_profile_staccato">Destacado</string>
<string name="vibration_profile_short">Pequeno</string>
<string name="vibration_profile_medium">Médio</string>
<string name="vibration_profile_long">Longo</string>
<string name="vibration_profile_waterdrop">Gota de Água</string>
<string name="vibration_profile_ring">Toque</string>
<string name="vibration_profile_waterdrop">Gota de Água</string>
<string name="vibration_profile_ring">Toque</string>
<string name="vibration_profile_alarm_clock">Alarme</string>
<string name="miband_prefs_vibration">Vibração</string>
<string name="vibration_try">Tente</string>
@ -217,8 +217,8 @@
<string name="pref_screen_notification_profile_generic_chat">Conversas</string>
<string name="pref_screen_notification_profile_generic_navigation">Navegação</string>
<string name="pref_screen_notification_profile_generic_social">Rede social</string>
<string name="control_center_find_lost_device">Encontrar dispositivo perdido</string>
<string name="control_center_cancel_to_stop_vibration">Cancele para parar a vibração.</string>
<string name="control_center_find_lost_device">Encontrar dispositivo perdido</string>
<string name="control_center_cancel_to_stop_vibration">Cancele para parar a vibração.</string>
<string name="title_activity_charts">A sua atividade</string>
<string name="title_activity_set_alarm">Configurar Alarmes</string>
<string name="controlcenter_start_configure_alarms">Configurar alarmes</string>
@ -234,52 +234,54 @@
<string name="user_feedback_miband_set_alarms_failed">Existiu um erro ao definir o alarme, tente novamente!</string>
<string name="user_feedback_miband_set_alarms_ok">Alarme enviado para o dispositivo!</string>
<string name="chart_no_data_synchronize">Sem data. Sincronizar com o dispositivo?</string>
<string name="user_feedback_miband_activity_data_transfer">Prestes a transferir %1$s de dados desde %2$s</string>
<string name="user_feedback_miband_activity_data_transfer">Prestes a transferir %1$s de dados desde %2$s</string>
<string name="miband_prefs_fitness_goal">Objetivo de passos por dia</string>
<string name="dbaccess_error_executing">Erro ao executar \'%1$s\'</string>
<string name="dbaccess_error_executing">Erro ao executar \'%1$s\'</string>
<string name="controlcenter_start_activitymonitor">A Sua Atividade (ALPHA)</string>
<string name="cannot_connect">Não foi possível ligar: %1$s</string>
<string name="cannot_connect">Não foi possível ligar: %1$s</string>
<string name="installer_activity_unable_to_find_handler">Não foi possível encontrar um manipulador para instalar o arquivo.</string>
<string name="pbw_install_handler_unable_to_install">Não foi possível instalar o ficheiro: %1$s</string>
<string name="pbw_install_handler_hw_revision_mismatch">Não foi possível instalar o firmware fornecido: ele não corresponde à versão de hardware do Pebble</string>
<string name="installer_activity_wait_while_determining_status">Aguarde enquanto se determina o estado da instalação...</string>
<string name="pbw_install_handler_unable_to_install">Não foi possível instalar o ficheiro: %1$s</string>
<string name="pbw_install_handler_hw_revision_mismatch">Não foi possível instalar o firmware fornecido: ele não corresponde à versão de hardware do Pebble</string>
<string name="installer_activity_wait_while_determining_status">Aguarde enquanto se determina o estado da instalação...</string>
<string name="notif_battery_low_title">Dispositivo com bateria baixa!</string>
<string name="notif_battery_low_percent">%1$s bateria restante: %2$s%%</string>
<string name="notif_battery_low_bigtext_last_charge_time">Última carga: %s \n</string>
<string name="notif_battery_low_bigtext_number_of_charges">Número de cargas: %s</string>
<string name="notif_battery_low_percent">%1$s bateria restante: %2$s%%</string>
<string name="notif_battery_low_bigtext_last_charge_time">Última carga: %s \n</string>
<string name="notif_battery_low_bigtext_number_of_charges">Número de cargas: %s</string>
<string name="sleepchart_your_sleep">O Seu Sono</string>
<string name="weeksleepchart_sleep_a_week">Sono durante a semana</string>
<string name="weeksleepchart_today_sleep_description">Sono hoje, objetivo: %1$s</string>
<string name="weeksleepchart_sleep_a_week">Sono na semana</string>
<string name="weeksleepchart_today_sleep_description">Sono hoje, objetivo: %1$s</string>
<string name="weekstepschart_steps_a_week">Passos na semana</string>
<string name="activity_sleepchart_activity_and_sleep">A Sua Atividade e Sono</string>
<string name="updating_firmware">Atualizando Firmware...</string>
<string name="fwapp_install_device_not_ready">Arquivo não pode ser instalado, o dispositivo não está pronto.</string>
<string name="miband_installhandler_miband_firmware">Firmware da Mi Band %1$s</string>
<string name="miband_installhandler_miband_firmware">Firmware da Mi Band %1$s</string>
<string name="miband_fwinstaller_compatible_version">Versão compatível</string>
<string name="miband_fwinstaller_untested_version">Versão não testada!</string>
<string name="fwappinstaller_connection_state">Ligação do Dispositivo: %1$s</string>
<string name="pbw_installhandler_pebble_firmware">Firmware da Pebble: %1$s</string>
<string name="pbwinstallhandler_correct_hw_revision">Revisão de hardware correta</string>
<string name="pbwinstallhandler_incorrect_hw_revision">Revisão de hardware inválida!</string>
<string name="pbwinstallhandler_app_item">%1$s (%2$s)</string>
<string name="updatefirmwareoperation_updateproblem_do_not_reboot">Ocorreu um erro com a transferência do firmware. NÃO REINICIE a sua Mi Band!</string>
<string name="fwappinstaller_connection_state">Ligação do Dispositivo: %1$s</string>
<string name="pbw_installhandler_pebble_firmware">Firmware da Pebble: %1$s</string>
<string name="pbwinstallhandler_correct_hw_revision">Revisão de hardware correta</string>
<string name="pbwinstallhandler_incorrect_hw_revision">Revisão de hardware inválida!</string>
<string name="pbwinstallhandler_app_item">%1$s (%2$s)</string>
<string name="updatefirmwareoperation_updateproblem_do_not_reboot">Ocorreu um erro com a transferência do firmware. NÃO REINICIE a sua Mi Band!</string>
<string name="updatefirmwareoperation_metadata_updateproblem">Problemas ao transferir os metadados do firmware</string>
<string name="updatefirmwareoperation_update_complete">Instalação do Firmware completa</string>
<string name="updatefirmwareoperation_update_complete_rebooting">Instalação do Firmware completa, reiniciando o dispositivo...</string>
<string name="updatefirmwareoperation_write_failed">Falha ao instalar o Firmware</string>
<string name="chart_steps">Passos</string>
<string name="liveactivity_live_activity">Atividade em tempo real</string>
<string name="weeksteps_today_steps_description">Passos hoje, objetivo: %1$s</string>
<string name="pref_title_dont_ack_transfer">Não confirmar a transferência de dados de atividade</string>
<string name="pref_summary_dont_ack_transfers">Se os dados de atividade não forem confirmados pela banda, eles não serão apagados. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
<string name="pref_summary_keep_data_on_device">Irá manter os dados na Mi Band mesmo após a sincronização. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
<string name="pref_title_low_latency_fw_update">Usar modo de baixa latência para atualizações de FW</string>
<string name="pref_summary_low_latency_fw_update">Isto pode ajudar em dispositivos onde as atualizações de firmware falhem</string>
<string name="calories">Calorias</string>
<string name="distance">Distância</string>
<string name="liveactivity_live_activity">Atividade em tempo real</string>
<string name="weeksteps_today_steps_description">Passos hoje, objetivo: %1$s</string>
<string name="pref_title_dont_ack_transfer">Não confirmar a transferência de dados de atividade</string>
<string name="pref_summary_dont_ack_transfers">Se os dados de atividade não forem confirmados pela banda, eles não serão apagados. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
<string name="pref_summary_keep_data_on_device">Irá manter os dados na Mi Band mesmo após a sincronização. Útil se o GB é utilizado em simultâneo com outras aplicações.</string>
<string name="pref_title_low_latency_fw_update">Usar modo de baixa latência para atualizações de FW</string>
<string name="pref_summary_low_latency_fw_update">Isto pode ajudar em dispositivos onde as atualizações de firmware falhem</string>
<string name="live_activity_steps_history">Histórico de passos</string>
<string name="live_activity_current_steps_per_minute">Passos/min atuais</string>
<string name="live_activity_total_steps">Total de passos</string>
<string name="live_activity_steps_per_minute_history">Histórico de passos por minuto</string>
<string name="live_activity_start_your_activity">Iniciar sua atividade</string>
<string name="live_activity_start_your_activity">Inicie a sua atividade</string>
<string name="abstract_chart_fragment_kind_activity">Atividade</string>
<string name="abstract_chart_fragment_kind_light_sleep">Sono leve</string>
<string name="abstract_chart_fragment_kind_deep_sleep">Sono profundo</string>
@ -290,13 +292,13 @@
<string name="miband_fwinstaller_incompatible_version">Firmware incompatível</string>
<string name="fwinstaller_firmware_not_compatible_to_device">Este firmware não é compatível com seu dispositivo</string>
<string name="miband_prefs_reserve_alarm_calendar">Alarmes reservados para eventos próximos</string>
<string name="miband_prefs_hr_sleep_detection">Utilizar sensor de Ritmo Cardíaco para melhorar a deteção de sono</string>
<string name="miband_prefs_device_time_offset_hours">Compensação da hora do dispositivo em horas (para detetar o sono de trabalhadores por turnos)</string>
<string name="miband2_prefs_dateformat">Mi2: Formato da Data</string>
<string name="miband_prefs_hr_sleep_detection">Utilizar sensor de Ritmo Cardíaco para melhorar a deteção de sono</string>
<string name="miband_prefs_device_time_offset_hours">Compensação da hora do dispositivo em horas (para detetar o sono de trabalhadores por turnos)</string>
<string name="miband2_prefs_dateformat">Mi2: Formato da Data</string>
<string name="dateformat_time">Hora</string>
<string name="dateformat_date_time"><![CDATA[Hora & Data]]></string>
<string name="mi2_prefs_activate_display_on_lift">Ativar ecrã do dispositivo quando o levantar</string>
<string name="FetchActivityOperation_about_to_transfer_since">Prestes a transferir dados desde %1$s</string>
<string name="dateformat_date_time"><![CDATA[Hora & Data]]></string>
<string name="mi2_prefs_activate_display_on_lift">Ativar ecrã do dispositivo quando o levantar</string>
<string name="FetchActivityOperation_about_to_transfer_since">Prestes a transferir dados desde %1$s</string>
<string name="waiting_for_reconnect">aguarde para tornar a ligar</string>
<string name="activity_prefs_about_you">Sobre você</string>
<string name="activity_prefs_year_birth">Ano de nascimento</string>
@ -305,39 +307,39 @@
<string name="activity_prefs_weight_kg">Peso em kg</string>
<string name="authenticating">a autenticar</string>
<string name="authentication_required">autenticação necessária</string>
<string name="appwidget_text">Zzz</string>
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Adicionar widget</string>
<string name="activity_prefs_sleep_duration">Preferir duração de sono em horas</string>
<string name="appwidget_alarms_set">Alarme definido para as %1$02d:%2$02d</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Erro ao criar o diretório para os ficheiros de registo: %1$s</string>
<string name="DEVINFO_HR_VER">RH:</string>
<string name="activity_prefs_sleep_duration">Preferir duração de sono em horas</string>
<string name="appwidget_alarms_set">Alarme definido para as %1$02d:%2$02d</string>
<string name="device_hw">HW: %1$s</string>
<string name="device_fw">Versão FW: %1$s</string>
<string name="error_creating_directory_for_logfiles">Erro ao criar o diretório para os ficheiros de registo: %1$s</string>
<string name="DEVINFO_HR_VER">Versão HW:</string>
<string name="updatefirmwareoperation_update_in_progress">Atualização de Firmware em curso</string>
<string name="updatefirmwareoperation_firmware_not_sent">Firmware não enviado</string>
<string name="charts_legend_heartrate">Ritmo Cardíaco</string>
<string name="live_activity_heart_rate">Ritmo Cardíaco</string>
<string name="pref_title_pebble_health_store_raw">Armazenar registo não processado na base de dados.</string>
<string name="pref_summary_pebble_health_store_raw">Se ativado os dados são guardados no seu formato original, estando mais tarde disponíveis para interpretação. Nota: neste caso a base de dados será maior!</string>
<string name="action_db_management">Gestão da Base de Dados</string>
<string name="title_activity_db_management">Gestão da Base de Dados</string>
<string name="activity_db_management_import_export_explanation">As operações sobre a base de dados utilizam o seguinte caminho no seu dispositivo. \nEste caminho está acessível a outras aplicações Android e ao seu computador. \nEspere encontrar a sua base de dados exportada (ou a base de dados que pretende importar) nessa localização:</string>
<string name="activity_db_management_merge_old_title">Apagar Base de Dados Antiga</string>
<string name="dbmanagementactivvity_cannot_access_export_path">Não foi possível aceder ao caminho de exportação. Por favor contacte os programadores.</string>
<string name="dbmanagementactivity_exported_to">Exportado para: %1$s</string>
<string name="dbmanagementactivity_error_exporting_db">Erro ao exportar a Base de Dados: %1$s</string>
<string name="charts_legend_heartrate">Ritmo Cardíaco</string>
<string name="live_activity_heart_rate">Ritmo Cardíaco</string>
<string name="pref_title_pebble_health_store_raw">Armazenar registo não processado na base de dados.</string>
<string name="pref_summary_pebble_health_store_raw">Se ativado os dados são guardados no seu formato original, estando mais tarde disponíveis para interpretação. Nota: neste caso a base de dados será maior!</string>
<string name="action_db_management">Gestão da Base de Dados</string>
<string name="title_activity_db_management">Gestão da Base de Dados</string>
<string name="activity_db_management_import_export_explanation">As operações sobre a base de dados utilizam o seguinte caminho no seu dispositivo. \nEste caminho está acessível a outras aplicações Android e ao seu computador. \nEspere encontrar a sua base de dados exportada (ou a base de dados que pretende importar) nessa localização:</string>
<string name="activity_db_management_merge_old_title">Apagar Base de Dados Antiga</string>
<string name="dbmanagementactivvity_cannot_access_export_path">Não foi possível aceder ao caminho de exportação. Por favor contacte os programadores.</string>
<string name="dbmanagementactivity_exported_to">Exportado para: %1$s</string>
<string name="dbmanagementactivity_error_exporting_db">Erro ao exportar a Base de Dados: %1$s</string>
<string name="dbmanagementactivity_import_data_title">Importar dados?</string>
<string name="dbmanagementactivity_overwrite_database_confirmation">Realmente deseja sobrescrever a base de dados atual? Todos os dados de atividade existentes irão perder-se. </string>
<string name="dbmanagementactivity_import_successful">Importação com sucesso.</string>
<string name="dbmanagementactivity_error_importing_db">Erro ao importar a Base de Dados: %1$s</string>
<string name="dbmanagementactivity_delete_activity_data_title">Apagar os dados de atividade?</string>
<string name="dbmanagementactivity_really_delete_entire_db">Apagar realmente toda a base de dados? Todos os seus dados de atividade e informação sobre os seus dispositivos irá perder-se.</string>
<string name="dbmanagementactivity_database_successfully_deleted">Dados apagados com sucesso.</string>
<string name="dbmanagementactivity_db_deletion_failed">Não foi possível apagar a base de dados.</string>
<string name="dbmanagementactivity_delete_old_activity_db">Apagar os dados de atividade antigos?</string>
<string name="dbmanagementactivity_delete_old_activitydb_confirmation">Apagar realmente a base de dados antiga? Dados de atividade não importados irão perder-se.</string>
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">Atividade antiga foi apagada com sucesso.</string>
<string name="dbmanagementactivity_old_activity_db_deletion_failed">Não foi possível apagar a atividade antiga da base de dados.</string>
<string name="dbmanagementactivity_overwrite_database_confirmation">Realmente deseja sobrescrever a base de dados atual? Todos os dados de atividade existentes irão perder-se. </string>
<string name="dbmanagementactivity_import_successful">Importação com sucesso.</string>
<string name="dbmanagementactivity_error_importing_db">Erro ao importar a Base de Dados: %1$s</string>
<string name="dbmanagementactivity_delete_activity_data_title">Apagar os dados de atividade?</string>
<string name="dbmanagementactivity_really_delete_entire_db">Apagar realmente toda a base de dados? Todos os seus dados de atividade e informação sobre os seus dispositivos irá perder-se.</string>
<string name="dbmanagementactivity_database_successfully_deleted">Dados apagados com sucesso.</string>
<string name="dbmanagementactivity_db_deletion_failed">Não foi possível apagar a base de dados.</string>
<string name="dbmanagementactivity_delete_old_activity_db">Apagar os dados de atividade antigos?</string>
<string name="dbmanagementactivity_delete_old_activitydb_confirmation">Apagar realmente a base de dados antiga? Dados de atividade não importados irão perder-se.</string>
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">Atividade antiga foi apagada com sucesso.</string>
<string name="dbmanagementactivity_old_activity_db_deletion_failed">Não foi possível apagar a atividade antiga da base de dados.</string>
<string name="dbmanagementactivity_overwrite">Escrever por cima</string>
<string name="Cancel">Cancelar</string>
<string name="Delete">Apagar</string>
@ -345,20 +347,25 @@
<string name="title_activity_vibration">Vibração</string>
<!--Strings related to Pebble Pairing Activity-->
<string name="title_activity_pebble_pairing">A emparelhar Pebble</string>
<string name="pebble_pairing_hint">É esperado que veja uma notificação de emparelhamento no seu dispositivo Android. Se isso não acontecer, aceda às notificações e aceite o pedido de emparelhamento. Depois aceite igualmente o pedido de emparelhamento no seu Pebble</string>
<string name="weather_notification_label">Garanta que este tema se encontra ativado na Aplicação de Meteorologia para obter informação meteorológica atualizada no seu Pebble.\n\nNão é necessária qualquer configuração aqui.\n\nPode ativar a aplicação nativa de meteorologia do seu Pebble através da gestão de aplicações.\n\nTemas de relógio irão apresentar a informação meteorológica automaticamente.</string>
<string name="pref_title_setup_bt_pairing">Ativar o emparelhamento Bluetooth</string>
<string name="pref_summary_setup_bt_pairing">Desative isto caso tenha problemas na ligação</string>
<string name="pebble_pairing_hint">É esperado que veja uma notificação de emparelhamento no seu dispositivo Android. Se isso não acontecer, aceda às notificações e aceite o pedido de emparelhamento. Depois aceite igualmente o pedido de emparelhamento no seu Pebble</string>
<string name="weather_notification_label">Garanta que este tema se encontra ativado na Aplicação de Meteorologia para obter informação meteorológica atualizada no seu Pebble.\n\nNão é necessária qualquer configuração aqui.\n\nPode ativar a aplicação nativa de meteorologia do seu Pebble através da gestão de aplicações.\n\nTemas de relógio irão apresentar a informação meteorológica automaticamente.</string>
<string name="pref_title_setup_bt_pairing">Ativar o emparelhamento Bluetooth</string>
<string name="pref_summary_setup_bt_pairing">Desative isto caso tenha problemas na ligação</string>
<string name="unit_metric">Métrico</string>
<string name="unit_imperial">Imperial</string>
<string name="timeformat_24h">24H</string>
<string name="timeformat_am_pm">Manhã/Tarde</string>
<string name="timeformat_24h">24H</string>
<string name="timeformat_am_pm">Manhã/Tarde</string>
<string name="pref_screen_notification_profile_alarm_clock">Alarme</string>
<string name="StringUtils_sender">(%1$s)</string>
<string name="find_device_you_found_it">Encontrado!</string>
<string name="miband2_prefs_timeformat">Mi2: Formato da hora</string>
<string name="mi2_fw_installhandler_fw53_hint">Deve instalar a versão %1$s antes de instalar este firmware!</string>
<string name="mi2_enable_text_notifications">Notificações de texto</string>
<string name="mi2_enable_text_notifications_summary"><![CDATA[Necessita de firmware >= 1.0.1.28 e Mili_pro.ft* instalado.]]></string>
<string name="off">desligado</string>
<string name="StringUtils_sender">(%1$s)</string>
<string name="find_device_you_found_it">Encontrado!</string>
<string name="miband2_prefs_timeformat">Mi2: Formato da hora</string>
<string name="mi2_fw_installhandler_fw53_hint">Deve instalar a versão %1$s antes de instalar este firmware!</string>
<string name="mi2_enable_text_notifications">Notificações de texto</string>
<string name="mi2_enable_text_notifications_summary"><![CDATA[Necessita de firmware >= 1.0.1.28 e Mili_pro.ft* instalado.]]></string>
<string name="off">desligado</string>
<string name="discovery_attempting_to_pair">A tentar emparelhar com %1$s</string>
<string name="discovery_enable_bluetooth">Ative o Bluetooth para encontrar os dispositivos.</string>
<string name="discovery_pair_title">Emparelhar com %1$s?</string>
<string name="discovery_yes_pair">Emparelhar</string>
<string name="discovery_dont_pair">Não emparelhar</string>
</resources>

View File

@ -10,14 +10,32 @@
<string name="controlcenter_find_device">Найти устройство...</string>
<string name="controlcenter_take_screenshot">Сделать снимок экрана</string>
<string name="controlcenter_disconnect">Отключиться</string>
<string name="controlcenter_delete_device">Удалить устройство</string>
<string name="controlcenter_delete_device_name">Удалить %1$s</string>
<string name="controlcenter_delete_device_dialogmessage">Устройство и вся связанная с ним информация будут удалены!</string>
<string name="controlcenter_navigation_drawer_open">Открыть панель навигации</string>
<string name="controlcenter_navigation_drawer_close">Закрыть панель навигации</string>
<string name="controlcenter_snackbar_need_longpress">Выполните долгое нажатие на карту для разъединения.</string>
<string name="controlcenter_snackbar_disconnecting">Разъединение</string>
<string name="controlcenter_snackbar_connecting">Соединение</string>
<string name="controlcenter_snackbar_requested_screenshot">Сделать снимок устройства</string>
<string name="title_activity_debug">Отладка</string>
<!--Strings related to AppManager-->
<string name="title_activity_appmanager">App Manager</string>
<string name="appmanager_cached_watchapps_watchfaces">Приложения в памяти</string>
<string name="appmanager_installed_watchapps">Установленные приложения</string>
<string name="appmanager_installed_watchfaces">Установленный циферблаты</string>
<string name="appmananger_app_delete">Удалить</string>
<string name="appmananger_app_delete_cache">Удалить и очистить кеш</string>
<string name="appmananger_app_reinstall">Переустановка</string>
<string name="appmanager_app_openinstore">Искать в Pebble Appstore</string>
<string name="appmanager_health_activate">Активировать</string>
<string name="appmanager_health_deactivate">Деактивировать</string>
<string name="appmanager_hrm_activate">Включить монитор сердечного ритма</string>
<string name="appmanager_hrm_deactivate">Выключить монитор сердечного ритма</string>
<string name="appmanager_weather_activate">Включить системное приложение прогноза погоды</string>
<string name="appmanager_weather_deactivate">Выключить системное приложение прогноза погоды</string>
<string name="appmanager_weather_install_provider">Установить уведомления для прогноза погоды</string>
<string name="app_configure">Настроить</string>
<string name="app_move_to_top">Переместить наверх</string>
<!--Strings related to AppBlacklist-->
@ -25,6 +43,7 @@
<!--Strings related to FwAppInstaller-->
<string name="title_activity_fw_app_insaller">Установщик прошивки/приложений</string>
<string name="fw_upgrade_notice">Вы собираетесь установить прошивку %s заместо текущей на вашем Mi Band.</string>
<string name="fw_multi_upgrade_notice">Вы собираетесь установить прошивки %1$s и %2$s вместо текущей на вашем Mi Band.</string>
<string name="miband_firmware_known">Эта прошивка была проверена и совместима с Gadgetbridge.</string>
<string name="miband_firmware_unknown_warning">Эта прошивка не протестирована и может быть несовместима с Gadgetbridge.\n\nНе рекомендуется устанавливать её на ваш Mi Band!</string>
<string name="miband_firmware_suggest_whitelist">Если вы желаете продолжить и впоследствии всё функционирует нормально, пожалуйста, сообщите разработчикам Gadgetbridge, чтобы они пометили как совместимую версию прошивки: %s</string>
@ -32,6 +51,7 @@
<string name="title_activity_settings">Настройки</string>
<string name="pref_header_general">Общие настройки</string>
<string name="pref_title_general_autoconnectonbluetooth">Подключение к устройству при активации Bluetooth</string>
<string name="pref_title_general_autostartonboot">Запускать автоматически</string>
<string name="pref_title_general_autocreonnect">Переподключаться автоматически</string>
<string name="pref_title_audo_player">Предпочтительный музыкальный плеер</string>
<string name="pref_default">По-умолчанию</string>
@ -42,34 +62,82 @@
<string name="pref_theme_light">Светлая</string>
<string name="pref_theme_dark">Темная</string>
<string name="pref_title_language">Язык</string>
<string name="pref_title_minimize_priority">Прятать уведомления от Gadgetbridge</string>
<string name="pref_summary_minimize_priority_off">Показывать значок в строке состояния и уведомления на экране блокировки</string>
<string name="pref_summary_minimize_priority_on">Не показывать значок в строке состояния и уведомления на экране блокировки</string>
<string name="pref_header_notifications">Уведомления</string>
<string name="pref_title_notifications_repetitions">Повторы</string>
<string name="pref_title_notifications_call">Вызовы</string>
<string name="pref_title_notifications_sms">СМС-сообщения</string>
<string name="pref_title_notifications_pebblemsg">Сообщения Pebble</string>
<string name="pref_summary_notifications_pebblemsg">Поддержка приложений, которые отправляют уведомления на Pebble через PebbleKit.</string>
<string name="pref_title_notifications_generic">Поддержка обычных уведомлений</string>
<string name="pref_title_whenscreenon">… даже когда экран включён</string>
<string name="pref_title_notification_filter">Не беспокоить</string>
<string name="pref_summary_notification_filter">Предотвращать отправку нежелательных уведомлений в режиме \"Не беспокоить\"</string>
<string name="pref_title_transliteration">Транслитерация</string>
<string name="pref_summary_transliteration">Используйте, если устройство не может работать с вашим языком</string>
<string name="pref_summary_transliteration">Включите эту функцию, если ваше устройство не имеет поддержки шрифта на вашем языке (в настоящее время только на Кириллице)</string>
<string name="always">всегда</string>
<string name="when_screen_off">когда экран выключен</string>
<string name="never">никогда</string>
<string name="pref_header_privacy">Конфиденциальность</string>
<string name="pref_title_call_privacy_mode">Вызов режима конфиденциальности</string>
<string name="pref_call_privacy_mode_off">Отображать имя и номер</string>
<string name="pref_call_privacy_mode_name">Скрывать имя, но отображать номер</string>
<string name="pref_call_privacy_mode_number">Скрывать номер, но отображать имя</string>
<string name="pref_call_privacy_mode_complete">Скрывать имя и номер</string>
<string name="pref_blacklist">Нежелательные приложения</string>
<string name="pref_header_cannned_messages">Сохранённые сообщения</string>
<string name="pref_title_canned_replies">Ответы</string>
<string name="pref_title_canned_reply_suffix">Общий суффикс</string>
<string name="pref_title_canned_messages_dismisscall">Пропущенные вызовы</string>
<string name="pref_title_canned_messages_set">Обновить на Pebble</string>
<string name="pref_header_development">Настройки для разработчиков</string>
<string name="pref_title_development_miaddr">Адрес Mi Band</string>
<string name="pref_title_pebble_settings">Настройки Pebble</string>
<string name="pref_header_activitytrackers">Отслеживание активности</string>
<string name="pref_title_pebble_activitytracker">Предпочитаемый трекер активности</string>
<string name="pref_title_pebble_sync_health">Синхронизация с Pebble Health</string>
<string name="pref_title_pebble_sync_misfit">Синхронизация с Misfit</string>
<string name="pref_title_pebble_sync_morpheuz">Синхронизация с Morpheuz</string>
<string name="pref_title_enable_outgoing_call">Поддержка исходящих вызовов</string>
<string name="pref_summary_enable_outgoing_call">При отключении данного пункта, Pebble 2/LE также не будет вибрировать при исходящих вызовах</string>
<string name="pref_title_enable_pebblekit">Разрешить доступ приложениям третьих лиц</string>
<string name="pref_summary_enable_pebblekit">Добавить экспериментальную поддержку приложений Android, использующих PebbleKit</string>
<string name="pref_title_sunrise_sunset">Восход и закат солнца</string>
<string name="pref_summary_sunrise_sunset">Показывать время восхода и захода солнца в зависимости от местоположения на временной шкале pebble</string>
<string name="pref_title_autoremove_notifications">Автоматическое удаление отклонённых уведомлений</string>
<string name="pref_summary_autoremove_notifications">Уведомления автоматически будут удалены из Pebble, при удалении с устройства Android</string>
<string name="pref_title_pebble_privacy_mode">Режим конфиденциальности</string>
<string name="pref_pebble_privacy_mode_off">Обычные уведомления</string>
<string name="pref_pebble_privacy_mode_content">Перенос текста уведомления, выходящим за границы экрана</string>
<string name="pref_pebble_privacy_mode_complete">Показать только значок уведомления</string>
<string name="pref_header_location">Местоположение</string>
<string name="pref_title_location_aquire">Получить данные о местоположении</string>
<string name="pref_title_location_latitude">Широта</string>
<string name="pref_title_location_longitude">Долгота</string>
<string name="pref_title_location_keep_uptodate">Обновлять местоположение</string>
<string name="pref_summary_location_keep_uptodate">Попробуйте получить текущее местоположение во время выполнения, используйте сохраненное местоположение в качестве резервного</string>
<string name="toast_enable_networklocationprovider">Пожалуйста, включите сетевое расположение</string>
<string name="toast_aqurired_networklocation">Месторасположение определено</string>
<string name="pref_title_pebble_forceprotocol">Принудительный протокол уведомлений</string>
<string name="pref_summary_pebble_forceprotocol">Эта настройка заставляет принудительно использовать самый новый протокол уведомлений, зависящий от версии прошивки. ВКЛЮЧАЙТЕ, ТОЛЬКО ЕСЛИ ЗНАЕТЕ НА ЧТО ВЫ ИДЁТЕ.</string>
<string name="pref_title_pebble_forceuntested">Включить непроверенные функции</string>
<string name="pref_summary_pebble_forceuntested">Включить функции, которые не протестированы. ВКЛЮЧАЙТЕ НА СВОЙ СТРАХ И РИСК!</string>
<string name="pref_title_pebble_forcele">Всегда отдавать предпочтение BLE</string>
<string name="pref_summary_pebble_forcele">Использовать экспериментальную поддержку Pebble LE для всех Pebble вместо BT classic, требует сопряжения «Pebble LE» после того, как был подключен не LE</string>
<string name="pref_title_pebble_mtu_limit">Ограничение GATT MTU для Pebble 2/LE</string>
<string name="pref_summary_pebble_mtu_limit">Если ваш Pebble 2/Pebble LE работает некорректно, попробуйте при помощи данной настройки ограничить MTU (допустимый диапазон от 20 до 512)</string>
<string name="pref_title_pebble_enable_applogs">Включить ведение логов приложений</string>
<string name="pref_summary_pebble_enable_applogs">Логи приложений будут вестись в Gadgetbridge (требуется переподключение)</string>
<string name="pref_title_pebble_always_ack_pebblekit">Преждевременное ACK PebbleKit</string>
<string name="pref_summary_pebble_always_ack_pebblekit">Сообщения, отправляемые внешним сторонним приложениям, будут всегда и немедленно разрешены</string>
<string name="pref_title_pebble_reconnect_attempts">Попытки переподключения</string>
<string name="pref_title_unit_system">Единицы</string>
<string name="pref_title_timeformat">Формат времени</string>
<string name="pref_title_screentime">Продолжительность работы экрана</string>
<string name="prefs_title_all_day_heart_rate">Измерение сердечного ритма в течение всего дня</string>
<string name="preferences_hplus_settings">Настройки HPlus/Makibes</string>
<string name="not_connected">нет соединения</string>
<string name="connecting">соединение</string>
<string name="connected">соединено</string>
@ -82,6 +150,10 @@
<string name="this_is_a_test_notification_from_gadgetbridge">Это тестовое уведомление от Gadgetbridge</string>
<string name="bluetooth_is_not_supported_">Bluetooth не поддерживается.</string>
<string name="bluetooth_is_disabled_">Bluetooth отключён.</string>
<string name="tap_connected_device_for_app_mananger">Коснитесь подключённого устройства для вызова Менеджера приложений</string>
<string name="tap_connected_device_for_activity">Коснитесь подключённого устройства для показа Активностей</string>
<string name="tap_connected_device_for_vibration">Коснитесь подключённого устройства для Вибрации</string>
<string name="tap_a_device_to_connect">Коснитесь подключённого устройства для соединения</string>
<string name="cannot_connect_bt_address_invalid_">Не удалось соединиться. Неверен адрес BT?</string>
<string name="gadgetbridge_running">Gadgetbridge запущен</string>
<string name="installing_binary_d_d">установки бинарного файла %1$d/%2$d</string>
@ -100,6 +172,11 @@
<string name="title_activity_android_pairing">Сопряжение устройств</string>
<string name="android_pairing_hint">Для сопряжения устройств используйте диалог Android.</string>
<string name="title_activity_mi_band_pairing">Сопряжение вашего Mi Band</string>
<string name="pairing">Сопряжение с %s…</string>
<string name="pairing_creating_bond_with">Привязываюсь к %1$s (%2$s)</string>
<string name="pairing_unable_to_pair_with">Невозможно выполнить сопряжение с %1$s (%2$s)</string>
<string name="pairing_in_progress">Выполняется привязка: %1$s (%2$s)</string>
<string name="pairing_already_bonded">Уже привязан к %1$s (%2$s), выполняется соединение...</string>
<string name="message_cannot_pair_no_mac">MAC-адрес не был передан, сопряжение не удалось.</string>
<string name="preferences_category_device_specific_settings">Настройки устройства</string>
<string name="preferences_miband_settings">Настройки Mi Band</string>
@ -111,6 +188,7 @@
<string name="miband_pairing_using_dummy_userdata">Не предоставлено действительных данных пользователя. Используются данные по-умолчанию.</string>
<string name="miband_pairing_tap_hint">Когда ваш Mi Band вибрирует и мигает, постучите по нему несколько раз.</string>
<string name="appinstaller_install">Установить</string>
<string name="discovery_connected_devices_hint">Сделайте свое устройство видимым. В настоящее время подключенные устройства, скорее всего, не будут обнаружены. Активируйте местоположение (например, GPS) на Android 6+. Отключите Privacy Guard для Gadgetbridge, так как это может привести к сбою и перезагрузке вашего телефона. Если через несколько минут устройство не обнаружено, повторите попытку после перезагрузки мобильного устройства.</string>
<string name="discovery_note">Заметка:</string>
<string name="candidate_item_device_image">Изображение устройства</string>
<string name="miband_prefs_alias">Имя/псевдоним</string>
@ -130,10 +208,15 @@
<string name="vibration_profile_ring">Звонок</string>
<string name="vibration_profile_alarm_clock">Будильник</string>
<string name="miband_prefs_vibration">Вибро</string>
<string name="vibration_try">Попробовать</string>
<string name="pref_screen_notification_profile_sms">SMS-уведомление</string>
<string name="pref_header_vibration_settings">Настройки вибро</string>
<string name="pref_screen_notification_profile_generic">Общие уведомления</string>
<string name="pref_screen_notification_profile_email">Уведомление по электронной почте</string>
<string name="pref_screen_notification_profile_incoming_call">Уведомления о входящем звонке</string>
<string name="pref_screen_notification_profile_generic_chat">Чат</string>
<string name="pref_screen_notification_profile_generic_navigation">Навигация</string>
<string name="pref_screen_notification_profile_generic_social">Социальные сети</string>
<string name="control_center_find_lost_device">Найти потерянное устройство</string>
<string name="control_center_cancel_to_stop_vibration">Отмените, чтобы остановить вибро</string>
<string name="title_activity_charts">Ваша активность</string>
@ -165,6 +248,8 @@
<string name="notif_battery_low_bigtext_last_charge_time">Последний раз устройство заряжалось: %s \n</string>
<string name="notif_battery_low_bigtext_number_of_charges">Количество циклов перезарядки: %s</string>
<string name="sleepchart_your_sleep">Ваш сон</string>
<string name="weeksleepchart_sleep_a_week">Сон за неделю</string>
<string name="weeksleepchart_today_sleep_description">Сон сегодня, цель: %1$s</string>
<string name="weekstepschart_steps_a_week">Шагов в неделю</string>
<string name="activity_sleepchart_activity_and_sleep">Ваша активность и сон</string>
<string name="updating_firmware">Обновление прошивки...</string>
@ -183,11 +268,14 @@
<string name="updatefirmwareoperation_update_complete_rebooting">Установка прошивки завершена, устройство перезагружается...</string>
<string name="updatefirmwareoperation_write_failed">Запись прошивки завершилась неудачей</string>
<string name="chart_steps">Шаги</string>
<string name="calories">Калории</string>
<string name="distance">Расстояние</string>
<string name="liveactivity_live_activity">Жизненная активность</string>
<string name="weeksteps_today_steps_description">Шагов сегодня, цель: %1$s</string>
<string name="pref_title_dont_ack_transfer">Не передавать данные об активности</string>
<string name="pref_summary_dont_ack_transfers">Если данные об активности не будут переданы на устройство, оно не будет очищено. Полезно, если GB используется с другими приложениями.</string>
<string name="pref_summary_keep_data_on_device">Хранить данные о деятельности на Mi Band, даже после синхронизации. Полезно, если Mi Band используется совместно с другими приложениями.</string>
<string name="pref_title_low_latency_fw_update">Использовать режим с низкими задержками для обновления прошивки</string>
<string name="pref_summary_low_latency_fw_update">Это может помочь в случае неудачного обновления прошивки</string>
<string name="live_activity_steps_history">История шагов</string>
<string name="live_activity_current_steps_per_minute">Текущие шаги в минуту</string>
@ -206,20 +294,84 @@
<string name="miband_prefs_reserve_alarm_calendar">Резервные сигналы для предстоящих событий</string>
<string name="miband_prefs_hr_sleep_detection">Использовать датчик сердцебиения для улучшения мониторинга сна</string>
<string name="miband_prefs_device_time_offset_hours">Смещение времени в часах (для тех, кто работает по ночам)</string>
<string name="miband2_prefs_dateformat">Mi2: Формат даты</string>
<string name="dateformat_time">Время</string>
<string name="dateformat_date_time"><![CDATA[Время и Дата]]></string>
<string name="mi2_prefs_activate_display_on_lift">Активировать экран при подъёме</string>
<string name="FetchActivityOperation_about_to_transfer_since">Готов к передачи данных с %1$s</string>
<string name="waiting_for_reconnect">Ожидание переподключения</string>
<string name="activity_prefs_about_you">Ваши данные</string>
<string name="activity_prefs_year_birth">Год рождения</string>
<string name="activity_prefs_gender">Пол</string>
<string name="activity_prefs_height_cm">Рост в см</string>
<string name="activity_prefs_weight_kg">Вес в кг</string>
<string name="authenticating">Авторизация</string>
<string name="authentication_required">Требуется авторизация</string>
<string name="appwidget_text">Zzz</string>
<string name="add_widget">Добавить виджет</string>
<string name="activity_prefs_sleep_duration">Желаемая продолжительность сна</string>
<string name="appwidget_alarms_set">Будильник был установлен на %1$02d:%2$02d</string>
<string name="device_hw">Версия устройства: %1$s</string>
<string name="device_fw"> Версия ПО: %1$s</string>
<string name="error_creating_directory_for_logfiles">Ошибка создания каталога для лог-файлов: %1$s</string>
<string name="DEVINFO_HR_VER">HR:</string>
<string name="updatefirmwareoperation_update_in_progress">Обновление прошивки в процессе</string>
<string name="updatefirmwareoperation_firmware_not_sent">Прошивка не отправлена</string>
<string name="charts_legend_heartrate">Пульс</string>
<string name="live_activity_heart_rate">Пульс</string>
<string name="pref_title_pebble_health_store_raw">Хранить необработанные записи в базе данных</string>
<string name="pref_summary_pebble_health_store_raw">Если флажок установлен, данные сохраняются «как есть» и доступны для последующей интерпретации. NB: база данных будет занимать больше места в этом случае!</string>
<string name="action_db_management">Управление базой данных</string>
<string name="title_activity_db_management">Управление базой данных</string>
<string name="activity_db_management_import_export_explanation">Операции с базой данных используют на вашем устройстве следующий путь. \n Этот путь доступен для других приложений Android и вашего компьютера. \n Вы можете найти вашу экспортированную базу данных (или разместить базу данных, которую вы хотите импортировать) здесь:</string>
<string name="activity_db_management_merge_old_title">Удаление устаревшей базой данных</string>
<string name="dbmanagementactivvity_cannot_access_export_path">Невозможно получить доступ к пути экспорта. Обратитесь пожалуйста к разработчикам.</string>
<string name="dbmanagementactivity_exported_to">Экспортировано в: %1$s</string>
<string name="dbmanagementactivity_error_exporting_db">Ошибка экспорта базы данных: %1$s</string>
<string name="dbmanagementactivity_error_exporting_shared">Ошибка экспорта настроек: %1$s</string>
<string name="dbmanagementactivity_import_data_title">Импортировать данные?</string>
<string name="dbmanagementactivity_overwrite_database_confirmation">Действительно перезаписать текущую базу данных? Все ваши текущие данные по вашей активности (если они есть) будут потеряны.</string>
<string name="dbmanagementactivity_import_successful">Импорт успешно завершён.</string>
<string name="dbmanagementactivity_error_importing_db">Ошибка импорта БД: %1$s</string>
<string name="dbmanagementactivity_error_importing_shared">Ошибка импорта настроек: %1$s</string>
<string name="dbmanagementactivity_delete_activity_data_title">Удалить данные по вашей активности?</string>
<string name="dbmanagementactivity_really_delete_entire_db">Действительно удалить всю базу данных? Все данные о вашей активности и информация о ваших устройствах будут потеряны.</string>
<string name="dbmanagementactivity_database_successfully_deleted">Данные успешно удалены.</string>
<string name="dbmanagementactivity_db_deletion_failed">Не удалось удалить базу данных.</string>
<string name="dbmanagementactivity_delete_old_activity_db">Удалить данные по вашей активности?</string>
<string name="dbmanagementactivity_delete_old_activitydb_confirmation">Вы действительно хотите удалить устаревшие данные по вашей активности? Все данные о вашей активности, которые не были импортированы, будут потеряны.</string>
<string name="dbmanagementactivity_old_activity_db_successfully_deleted">Устаревшие данные по вашей активности успешно удалены.</string>
<string name="dbmanagementactivity_old_activity_db_deletion_failed">Не удалось удалить устаревшие данные по вашей активности.</string>
<string name="dbmanagementactivity_overwrite">Перезаписать</string>
<string name="Cancel">Отмена</string>
<string name="Delete">Удалить</string>
<!--Strings related to Vibration Activity-->
<string name="title_activity_vibration">Вибрация</string>
<!--Strings related to Pebble Pairing Activity-->
<string name="title_activity_pebble_pairing">Pebble Сопряжение</string>
<string name="pebble_pairing_hint">На вашем Android устройстве должно появиться всплывающее окно с предложением сопряжения устройств. Если этого не произошло, загляните в ящик уведомлений и примите запрос на сопряжение. После этого примите запрос сопряжения на вашем Pebble</string>
<string name="weather_notification_label">Убедитесь, что оболочка выбрана в приложении «Уведомление о погоде», чтобы получать информацию о погоде на вашем Pebble.\n\nНикакой настройки здесь не требуется.\n\nВы можете включить приложение погоды вашего Pebble из управления приложениями.\n\nПоддерживаемые циферблаты покажут погоду автоматически.</string>
<string name="pref_title_setup_bt_pairing">Включить сопряжение по Bluetooth</string>
<string name="pref_summary_setup_bt_pairing">Выключите данный пункт, если у вас возникли проблемы с подключением</string>
<string name="unit_metric">Метрическая система</string>
<string name="unit_imperial">Приоритетный</string>
<string name="timeformat_24h">24ч</string>
<string name="timeformat_am_pm">12ч</string>
<string name="pref_screen_notification_profile_alarm_clock">Будильник</string>
<string name="StringUtils_sender">(%1$s)</string>
<string name="find_device_you_found_it">Вы нашли!</string>
<string name="miband2_prefs_timeformat">Mi2: Формат Времени</string>
<string name="mi2_fw_installhandler_fw53_hint">Вам необходимо установить версию %1$s до того как установить данную прошивку!</string>
<string name="mi2_enable_text_notifications">Текстовые уведомления</string>
<string name="mi2_enable_text_notifications_summary"><![CDATA[Требуется прошивка >= 1.0.1.28 и установленный Mili_pro.ft* .]]></string>
<string name="off">Выкл.</string>
<string name="discovery_attempting_to_pair">Попытка сопряжения с %1$s</string>
<string name="discovery_bonding_failed_immediately">Не удалось выполнить привязку к %1$s</string>
<string name="discovery_trying_to_connect_to">Попытка соединения с: %1$s</string>
<string name="discovery_enable_bluetooth">Включить Bluetooth для обнаружения устройств.</string>
<string name="discovery_successfully_bonded">Привязка к %1$s успешно выполнена.</string>
<string name="discovery_pair_title">Выполнить сопряжение с %1$s ?</string>
<string name="discovery_pair_question">Выберите \"Сопряжение\" для сопряжения ваших устройств, если этого сделать не получилось, попробуйте снова без сопряжения.</string>
<string name="discovery_yes_pair">Сопряжение</string>
<string name="discovery_dont_pair">Не выполнять сопряжение</string>
</resources>

View File

@ -128,8 +128,11 @@
<string name="pref_title_enable_pebblekit">Allow 3rd Party Android App Access</string>
<string name="pref_summary_enable_pebblekit">Enable experimental support for Android Apps using PebbleKit</string>
<string name="pref_header_pebble_timeline">Pebble Timeline</string>
<string name="pref_title_sunrise_sunset">Sunrise and Sunset</string>
<string name="pref_summary_sunrise_sunset">Send sunrise and sunset times based on the location to the pebble timeline</string>
<string name="pref_title_enable_calendar_sync">Sync Calendar</string>
<string name="pref_summary_enable_calendar_sync">Send calendar events to the timeline</string>
<string name="pref_title_autoremove_notifications">Autoremove dismissed Notifications</string>
<string name="pref_summary_autoremove_notifications">Notifications are automatically removed from the Pebble when dismissed from the Android device</string>

View File

@ -1,23 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
<release
version="0.18.5"
versioncode="92">
<change>Applied some material design guidelines to Charts and (pebble) app management
</change>
<release version="0.19.2" versioncode="95">
<change>Pebble: Fix recurring calendar events only appearing once per week</change>
<change>HPlus: Fix crash when receiving calls without phone number</change>
<change>HPlus: Detect unicode support on Zeband Plus</change>
<change>No longer quit Gadgetbridge when bluetooth gets turned off</change>
</release>
<release version="0.19.1" versioncode="94">
<change>Fix crash at startup</change>
<change>Improve reconnection to device</change>
<change>Improve transliteration</change>
</release>
<release version="0.19.0" versioncode="93">
<change>Pebble: allow calendar sync with Timeline (Title, Location, Description)</change>
<change>Pebble: display calendar icon for reminders from AOSP Calendar</change>
<change>HPlus: try to fix latin characters showing as random chinese text</change>
<change>Improve reconnection with BLE devices</change>
<change>Improve generic notification reliability by trying to restart notification listener when stale/crashed</change>
<change>Other small bugfixes</change>
</release>
<release version="0.18.5" versioncode="92">
<change>Applied some material design guidelines to Charts and (pebble) app management</change>
<change>Changed colours: deep sleep is now dark blue, light sleep is now light blue</change>
<change>Support for exporting and importing of preferences in addition to the database
</change>
<change>Support for exporting and importing of preferences in addition to the database</change>
<change>Visual improvements of the pie charts</change>
<change>Add filter by name in the App blacklist activity</change>
<change>Pebble: improve compatibility with watch app configuration pages</change>
<change>Pebble: display battery percentage (will only update once an hour)</change>
<change>HPlus: users can now decide whether they want to pair the device or not, hopefully
fixing some connection problems (#642)
</change>
<change>HPlus: users can now decide whether they want to pair the device or not, hopefully fixing some connection problems (#642)</change>
<change>HPlus: display battery state and warn on low battery</change>
</release>
<release version="0.18.4" versioncode="91">
<change>Mi Band 2: Display realtime steps in Live Activity</change>
<change>Mi Band: Attempt to recognize Mi Band model with hwVersion = 8</change>

View File

@ -189,10 +189,6 @@
android:key="pebble_reconnect_attempts"
android:maxLength="4"
android:title="@string/pref_title_pebble_reconnect_attempts" />
<CheckBoxPreference
android:title="@string/pref_title_sunrise_sunset"
android:summary="@string/pref_summary_sunrise_sunset"
android:key="send_sunrise_sunset" />
<CheckBoxPreference
android:defaultValue="false"
android:key="autoremove_notifications"
@ -206,6 +202,17 @@
android:defaultValue="@string/p_pebble_privacy_mode_off"
android:summary="%s" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_header_pebble_timeline">
<CheckBoxPreference
android:defaultValue="true"
android:key="enable_calendar_sync"
android:summary="@string/pref_summary_enable_calendar_sync"
android:title="@string/pref_title_enable_calendar_sync" />
<CheckBoxPreference
android:key="send_sunrise_sunset"
android:summary="@string/pref_summary_sunrise_sunset"
android:title="@string/pref_title_sunrise_sunset" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_header_activitytrackers">
<ListPreference
android:defaultValue="4"

View File

@ -5,7 +5,7 @@ import android.content.Context;
import android.content.Intent;
import org.robolectric.Robolectric;
import org.robolectric.util.ServiceController;
import org.robolectric.android.controller.ServiceController;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceService;

View File

@ -0,0 +1,57 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao;
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
public class CalendarEventTest extends TestBase {
private static final long BEGIN = 1;
private static final long END = 2;
private static final long ID_1 = 100;
private static final long ID_2 = 101;
private static final String CALNAME_1 = "cal1";
@Test
public void testHashCode() {
CalendarEvents.CalendarEvent c1 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, "something", null, null, CALNAME_1, false);
CalendarEvents.CalendarEvent c2 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, false);
CalendarEvents.CalendarEvent c3 = new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, null, "something", CALNAME_1, false);
assertEquals(c1.hashCode(), c1.hashCode());
assertNotEquals(c1.hashCode(), c2.hashCode());
assertNotEquals(c2.hashCode(), c3.hashCode());
}
@Test
public void testSync() {
List<CalendarEvents.CalendarEvent> eventList = new ArrayList<>();
eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, false));
GBDevice dummyGBDevice = createDummyGDevice("00:00:01:00:03");
dummyGBDevice.setState(GBDevice.State.INITIALIZED);
// Device device = DBHelper.getDevice(dummyGBDevice, daoSession);
CalendarReceiver testCR = new CalendarReceiver(dummyGBDevice);
testCR.syncCalendar(eventList);
eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_2, null, "something", null, CALNAME_1, false));
testCR.syncCalendar(eventList);
CalendarSyncStateDao calendarSyncStateDao = daoSession.getCalendarSyncStateDao();
assertEquals(2, calendarSyncStateDao.count());
}
}

View File

@ -6,8 +6,8 @@ import org.junit.After;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
@ -25,16 +25,12 @@ public class LoggingTest extends TestBase {
public LoggingTest() throws Exception {
}
private Logging logging = new Logging() {
@Override
protected String createLogDirectory() throws IOException {
return logFilesDir.getAbsolutePath();
}
};
private Logging logging = GBApplication.getLogging();
@Override
@After
public void tearDown() {
public void tearDown() throws Exception {
super.tearDown();
assertTrue(FileUtils.deleteRecursively(getLogFilesDir()));
}

View File

@ -1,7 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import org.junit.After;
import org.junit.Before;
@ -16,9 +15,9 @@ import java.io.File;
import ch.qos.logback.classic.util.ContextInitializer;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
@ -46,9 +45,10 @@ public abstract class TestBase {
// Make sure logging is set up for all testcases, so that we can debug problems
@BeforeClass
public static void setupSuite() throws Exception {
GBEnvironment.setupEnvironment(GBEnvironment.createLocalTestEnvironment());
// print everything going to android.util.Log to System.out
System.setProperty("robolectric.logging", "stdout");
// ShadowLog.stream = System.out;
// properties might be preconfigured in build.gradle because of test ordering problems
String logDir = System.getProperty(Logging.PROP_LOGFILES_DIR);
@ -69,20 +69,19 @@ public abstract class TestBase {
@Before
public void setUp() throws Exception {
app = (GBApplication) RuntimeEnvironment.application;
assertNotNull(app);
assertNotNull(getContext());
// doesn't work with Robolectric yet
// dbHandler = GBApplication.acquireDB();
// daoSession = dbHandler.getDaoSession();
DaoMaster.DevOpenHelper openHelper = new DaoMaster.DevOpenHelper(app, null, null);
SQLiteDatabase db = openHelper.getWritableDatabase();
daoSession = new DaoMaster(db).newSession();
app.setupDatabase();
dbHandler = GBApplication.acquireDB();
daoSession = dbHandler.getDaoSession();
assertNotNull(daoSession);
}
@After
public void tearDown() throws Exception {
// GBApplication.releaseDB();
dbHandler.closeDb();
GBApplication.releaseDB();
}
protected GBDevice createDummyGDevice(String macAddress) {

View File

@ -32,4 +32,6 @@ public class Tryout extends TestBase {
LOG.info("Calender: " + DateTimeUtils.formatDateTime(calendar.getTime()));
Logging.logBytes(LOG, bytes);
}
}