Compare commits

...

378 Commits

Author SHA1 Message Date
Dennis Guse 9074fa1b6c Release v3.7.0 2020-04-28 18:36:08 +02:00
transifex-integration[bot] 5721a615a2 Translate '/src/main/res/values/strings.xml' in 'de' (#199)
* Translate /src/main/res/values/strings.xml in de

translation completed for the source file '/src/main/res/values/strings.xml'
on the 'de' language.

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2020-04-28 18:34:28 +02:00
Dennis Guse fadcf1cd41 Pace/speed setting: changed label and renamed keys.
Also removed existing translations.
2020-04-28 08:23:18 +02:00
Dennis Guse a50b1ac78e Cleanup using Android Studio code analyzer. 2020-04-28 08:07:24 +02:00
Dennis Guse 9a7d4fdb36 TrackListActivity: removed start/stop GPS toast message.
Nowadays, we already have a notification in place.
2020-04-28 07:03:39 +02:00
Román Martínez 9fb51a689d Merge pull request #197 from OpenTracksApp/rgmf-issue#151
Merge issue#151.
2020-04-27 20:30:12 +02:00
Román 65bda35001 Preferred rate settings adapted to new code changes. 2020-04-27 20:25:16 +02:00
Dennis Guse 443ded86a5 Continuous integration: only test on API 29.
On Github Actions API 21 fails while it works locally.
2020-04-27 20:12:13 +02:00
Román 83b5ebb02c Added new option in Preferred rate setting: By Default.
With By Default option it will use pace or speed depending on activity type.
Fixes #151.
2020-04-27 20:02:55 +02:00
Román Martínez 240d8f6b6c Images/folders are deleted when tracks/waypoints are deleted.
This fixes #189.
2020-04-27 18:18:02 +02:00
Dennis Guse 39c5422341 Merge pull request #193 from OpenTracksApp/bluetooth
Bluetooth LE: add cycling cadence and speed sensor(s).
2020-04-27 18:14:39 +02:00
Dennis Guse 4893a2b743 Added cycling speed sensor.
Fixed speed and cadence computation.
Fixed that a sensor change in settings resulted in a disconnect for all connected sensors.
2020-04-27 07:17:46 +02:00
Dennis Guse 3408ea97f4 Refactoring: move TrackPointDataSet directly into TrackPoint. 2020-04-27 07:17:46 +02:00
Dennis Guse ddb17f61cb Bluetooth LE: implemented cycling cadence and speed sensors.
Cadence is working while speed still requires the wheel size.
2020-04-27 07:17:46 +02:00
Dennis Guse bbe182cf82 Removed handler between BluetoothConnectionManager and -RemoteSensorManager.
Toast messages should rather be sent from activities.
2020-04-27 07:17:46 +02:00
Dennis Guse b7b7bdf19b Workaround for GPS week rollover (happening every 19.7 years).
Fixes #155.

https://en.wikipedia.org/wiki/GPS_Week_Number_Rollover
2020-04-27 07:08:29 +02:00
Román Martínez 5681938a9e Merge pull request #188 from OpenTracksApp/import_export#180
Improve import/export
2020-04-23 08:21:55 +02:00
Román 74835dcef5 KMZ import/export: code improved. 2020-04-22 13:19:32 +02:00
Román ed0dc05366 KMZ exporting: marker images use waypoint id as a name. 2020-04-21 19:01:35 +02:00
Román 653eb8320a KMZ importing: look for images anywhere in KMZ file and only images referenced remain in external storage. 2020-04-21 19:01:35 +02:00
Román 54b9c4370b KML description: max/min elevation will not show if have invalid values (unknow). 2020-04-21 19:01:35 +02:00
Román fa9cb3903c KMZ exporting: checks waypoints images exists before parse kml. 2020-04-21 19:01:30 +02:00
Román 0f1d8500d1 KMZ importing: firstly copies images then parses kml (checks kml's href link to image is correct). 2020-04-21 19:00:03 +02:00
Dennis Guse 20d7fd2c03 Merge pull request #187 from OpenTracksApp/dev
Refactored TrackRecordingService.
2020-04-15 23:11:38 +02:00
Dennis Guse 1d838344dc TrackRecordingService: test inserting locations. 2020-04-15 22:37:31 +02:00
Dennis Guse fd826199e2 Tests for TrackRecordingService (pause/resume/restart). 2020-04-14 22:13:14 +02:00
Dennis Guse f485d90ef2 ContentProviderUtils.bulkInsertTrackPoint(): removed length parameter. 2020-04-14 19:42:17 +02:00
Dennis Guse ec39e67a09 Removed Waypoint default constructor used by testing.
This avoids a null check for Waypoint.getLocation().
2020-04-14 07:12:56 +02:00
Dennis Guse 1a86db0a8b Cleanup. 2020-04-13 20:23:40 +02:00
Dennis Guse 6a1fada22f TrackRecordingService: cleanup. 2020-04-13 20:23:22 +02:00
Dennis Guse 2898e06246 TrackRecordingService: move service restart handling into method. 2020-04-13 20:23:22 +02:00
Dennis Guse 0d87c65b49 TrackRecordingService: cleanup. 2020-04-13 20:23:22 +02:00
Dennis Guse 9b97cdfcd1 TrackRecordingService: cleanup.
if+return rather than if+else.
2020-04-13 20:23:22 +02:00
Dennis Guse 95feae846a Create helper functions for TrackPoints in TrackPointUtils. 2020-04-13 20:23:22 +02:00
Dennis Guse b09eeaede3 Release v3.6.2 2020-04-13 12:56:59 +02:00
Dennis Guse 35dec1634c Bugfix: charts were showing only the invalid TrackPoints.
Introduced in fbc902e830
2020-04-13 12:48:52 +02:00
Dennis Guse e49a425106 Release v3.6.1 2020-04-12 18:46:49 +02:00
Román adbad04867 URIs for waypoint's photos are correct. Fixes #183. 2020-04-12 18:27:20 +02:00
Dennis Guse 850bd2f887 Update Android gradle plugin. 2020-04-11 23:16:38 +02:00
Dennis Guse d240b457e2 Release v3.6.0 2020-04-10 14:55:04 +02:00
Dennis Guse 6d800caab1 While importing photos, check url carefully.
(cherry picked from commit 309c96e4789bd7623b84720a8c8f2bf297c95d59)
2020-04-10 14:42:11 +02:00
Román 7bf153db39 Avoid NullPointer in creating photoUrl when importing 2020-04-10 14:42:11 +02:00
Román 1b69f29751 Importing creates content:// for attached pictures. Also, exporting without points bug fixed and images href contains relative link. Fixes #166 and #176. 2020-04-10 14:42:11 +02:00
Dennis Guse dfc3837739 Settings: PrefsFragment could not be re-created. Fixes #179. 2020-04-08 08:10:46 +02:00
Dennis Guse 30e847866e Removed TravisCI it does not work as desired. 2020-04-07 21:01:54 +02:00
Dennis Guse fbc902e830 Cleanup using AndroidStudio code analyzer.
Moved to iterator for loops for ChartView and ChartFragment.
2020-04-05 21:56:26 +02:00
Dennis Guse 208f1d71ba Refactor ChartView/ChartFragment to use ChartPoint. 2020-04-05 21:56:26 +02:00
Dennis Guse 38a15a96b3 Cleanup. 2020-04-05 21:56:26 +02:00
Dennis Guse 46f8753746 Removed TrackDataType and simplified TrackDataManager. 2020-04-05 21:56:26 +02:00
Dennis Guse 70bc8f33b1 Do not expose TrackDataType to fragments. 2020-04-05 21:56:26 +02:00
Dennis Guse 6cd27ab096 Merged DataSourceManager into TrackDataHub. 2020-04-05 21:56:26 +02:00
Dennis Guse 5581b404f7 DataSourceManager now always registers for all changes. 2020-04-05 21:56:26 +02:00
Dennis Guse 2ef74eb908 Automated code cleanup using AndroidStudio. 2020-04-05 21:56:26 +02:00
Dennis Guse 16c654141f Renamed TripStatistics to TrackStatistics. 2020-04-05 21:56:26 +02:00
Dennis Guse dfc785f2fd Simplified internal data processing (class structure). 2020-04-05 21:56:26 +02:00
Dennis Guse f621cdf032 Removed grade internals completely. 2020-04-05 21:56:26 +02:00
Román 8b04d9c4a6 creates a segment like pause/continue. 2020-04-05 14:25:29 +02:00
Dennis Guse 364b4702e3 Waypoint requires a non-null location.
Remove setLocation() from Waypoint and TrackPoint.
2020-03-30 19:52:06 +02:00
Dennis Guse a98c16de28 Removed TrackPointFactory.
New TrackPoints are now always created and old ones not reused.
2020-03-30 19:52:06 +02:00
Dennis Guse 881bf1982a Removed TrackWriterTrackPointFactory.
Replaced with local variable for TrackPoint swapping.
2020-03-30 19:52:06 +02:00
Dennis Guse f340622977 Automated code analysis improvements. 2020-03-30 19:52:06 +02:00
Dennis Guse 194379295d TrackPoint has Location as delegate instead of inheritance. 2020-03-30 19:52:06 +02:00
Dennis Guse 901cffee97 Use TrackPoint instead of Location (and omit casting). 2020-03-30 19:52:06 +02:00
Román d93c2ec5f3 TrackDetailActivity starts an actual recording and a stopped recording can be resumed. Fixes #103 and #11. 2020-03-30 19:25:22 +02:00
Dennis Guse 306522863a Release v3.5.0 2020-03-21 23:18:57 +01:00
Dennis Guse e54d85e04d ChartFragment: unregister from preference changes on pause. 2020-03-21 20:54:49 +01:00
Dennis Guse cb30ad27c3 Removed that tracks store count of the locations. 2020-03-20 19:44:31 +01:00
Dennis Guse 8685c9cc88 Merge pull request #162 from rgmf/issue#89
Share pictures from marker.
2020-03-18 22:08:42 +01:00
Román a6f03d0f23 Share pictures from marker.
Fixes #89.
2020-03-18 19:33:57 +01:00
Dennis Guse e3c812b155 TrackDeleteActivity now has a transparant background. 2020-03-18 07:43:21 +01:00
Dennis Guse f149de9ce3 Merge pull request #167 from rgmf/issue#163
Add android:key in PreferenceCategory with expandable.
2020-03-17 21:54:08 +01:00
Dennis Guse 3447dd2569 Merge pull request #165 from OpenTracksApp/dbCleanup
Database cleanup and optimization
2020-03-17 21:44:28 +01:00
Dennis Guse b2b8c7fd54 Database upgrade (incl. tests): removed unused columns and added indices.
Fixes #108.
2020-03-17 21:39:10 +01:00
Román 486bcd0aec Add android:key in PreferenceCategory with expandable.
Fixes #163.
2020-03-17 10:57:04 +01:00
Dennis Guse c8a33201f5 Moved annoucement text generation into util class. 2020-03-16 21:43:23 +01:00
Dennis Guse 4a37c2cc7e Cleanup. 2020-03-16 21:43:23 +01:00
Dennis Guse d23db5bafd Charts use preferred units and rate.
Fixes #150.
2020-03-16 21:43:23 +01:00
Dennis Guse ac528f9a10 Use trackname for sharing files.
Fixes #130.
2020-03-16 21:43:23 +01:00
Dennis Guse 256eadd49d Added some french translation provided by @edern-appere.
Fixes #147.
2020-03-16 21:43:23 +01:00
Dennis Guse 64e58934a5 Settings: fix label for elevation (removed "Grade" as it is not shown anymore). 2020-03-16 21:43:23 +01:00
Dennis Guse 92dfadfdba Remove elevation min/max from UI as GPS-derived data often lead to invalid data (especially initial fix phase).
Fixes #148.
2020-03-16 21:43:23 +01:00
Román cbce70143b All marker management is now in MarkerEditActivity (including take photo and added gallery image to the marker).
Issue 122.
2020-03-11 19:59:15 +01:00
Román f4b9e3159e New screenshots for F-Droid/PlayStore. Fixes #132. 2020-03-11 19:40:57 +01:00
Dennis Guse 928781391d Release v3.4.0 2020-02-05 23:41:55 +01:00
Dennis Guse 05cd672464 Updated help text. 2020-02-05 23:41:50 +01:00
Dennis Guse d3109bbc37 Cleanup. 2020-02-05 23:08:17 +01:00
Dennis Guse eabdb7c721 Unify order of show on map and share menu entries. 2020-02-04 19:52:05 +01:00
Peter Storch ad990e8cd7 Forward multiple tracks to dashboard 2020-02-04 00:07:17 +01:00
Dennis Guse db3eef0104 Test: date comparison tends to be flaky. 2020-02-03 23:27:19 +01:00
Dennis Guse c4f1ba7962 Upgrade material design dependency.
Requires including androidx.documentfile directly.
2020-02-03 23:27:19 +01:00
Dennis Guse 7451fbd413 CI: enable testing via Github Actions. 2020-02-03 23:27:19 +01:00
Dennis Guse b6c55de55a CI: enable testing TravisCI on again. 2020-02-03 23:27:19 +01:00
Dennis Guse dd3ee6d99e Bugfix: match internal ContentURI correctly.
TrackPoint by TrackId was not working (and not used).
2020-02-03 21:32:06 +01:00
Dennis Guse bbb6a7c22c Bugfix: keep TrackRecordingService in foreground if paused. 2020-02-03 20:45:14 +01:00
Dennis Guse dc1b50a7bf Cleanup: move PAUSE_LATITUDE and RESUME_LATITUDE to TrackPointsColumn. 2020-02-03 20:45:14 +01:00
Dennis Guse 21bfb88e0b Bugfix: initialize fragments from bundle rather via constructor.
Introduced in 8b11e1d611
This fixes #127.
2020-02-03 19:51:04 +01:00
Dennis Guse aaab8adce9 ExportAsyncTask: always get a wakelock. 2020-02-03 19:51:04 +01:00
Dennis Guse df502f835c Release v3.3.2 2020-01-11 20:33:47 +01:00
Dennis Guse dd6a4cd153 Export: files are now overwritten. 2020-01-11 20:29:20 +01:00
Dennis Guse 76656997ef Sharing: check if a compatible app is installed rather than trying it. 2020-01-11 20:27:43 +01:00
Dennis Guse 32070a0bd6 Use java.time instead of custom code for parsing (requires API 26+).
This is a functional change on API26+ as a timezone identifier is required.
2020-01-11 19:31:58 +01:00
Dennis Guse b9420d4dde Removed LocationManagerConnector. 2020-01-11 19:31:58 +01:00
Dennis Guse 1b810e1294 TrackRecordingService: do not try to register location listener every minute. 2020-01-11 19:31:58 +01:00
Dennis Guse f16d59bff6 Cleanup. 2020-01-11 19:31:58 +01:00
Dennis Guse b5bf3aadc5 Refactored wake lock release. 2020-01-11 19:31:58 +01:00
Dennis Guse f6a27a1522 Documentation: add activities overview. 2020-01-11 19:31:58 +01:00
Dennis Guse 83758fcd72 Always cancel TrackRecordingService's notification. 2020-01-11 19:31:58 +01:00
Dennis Guse 070917bc0b Extracted TrackRecordingService's Binder. 2020-01-11 19:31:58 +01:00
Dennis Guse 543a04dfdd Cleanup. 2020-01-11 19:31:58 +01:00
Dennis Guse b4d5eb0eec Refactoring: SharedPreferences are not provided via DataSource anymore. 2020-01-11 19:31:58 +01:00
Dennis Guse 9f84cc4952 Use ContentUris.parseId(). 2020-01-11 19:31:58 +01:00
Dennis Guse 09fe35f1ca Cleanup. 2020-01-11 19:31:58 +01:00
Dennis Guse 27e50b2dd9 Moved ContentProvider into separate package. 2020-01-11 19:31:58 +01:00
Dennis Guse 971460848b Extracted LocationIterator from ContentProviderUtils.
Also refactored LocationFactory to TrackPointFactory.
2020-01-11 19:31:58 +01:00
Dennis Guse 8bc2937bb4 Extracted LocationIterator from ContentProviderUtils. 2020-01-11 19:31:58 +01:00
Dennis Guse 0e9b68c2a1 Cleanup. 2020-01-11 19:31:58 +01:00
Nathan Hearnsberger ba298c2afb Additional language support for export_with/without_photos (DE, ES). 2020-01-11 19:27:53 +01:00
Nathan Hearnsberger 79f5ed4dc3 Added KMZ without photos, photo details to export picker. 2020-01-11 19:27:53 +01:00
Dennis Guse bbb2f874ee Release v3.3.1 2020-01-03 18:11:57 +01:00
Dennis Guse 9af3759b07 Bugfix: app name (release) not shown.
Introduced in 6cc7271a41.
2020-01-03 17:54:15 +01:00
Dennis Guse 1f6fe6bf0c Android 10: on uninstall ask the user if the app data should be kept. 2020-01-03 17:50:07 +01:00
Dennis Guse 7b291e85d7 Renamed DeleteActivity to TrackDeleteActivity. 2020-01-03 17:04:51 +01:00
Dennis Guse 0f72066bc0 Add IntentChooser for dashboards. 2020-01-02 19:56:24 +01:00
Dennis Guse 70e0e7b216 Add README for data API. 2020-01-02 19:54:19 +01:00
Peter Storch 7e50bb1b6d Merge pull request #114 from pstorch/79_osm_plugin
integrate dashboard plugin
2020-01-02 17:56:07 +01:00
Peter Storch a3eb107068 integrate dashboard plugin mechanism
To be able to integrate the OSMDashboard https://github.com/OpenTracksApp/OSMDashboard,
which addresses #79. Also other Dashboards will be possible.
2020-01-02 17:45:01 +01:00
Dennis Guse 6133ef121a Release v3.3.0 2020-01-01 14:46:28 +01:00
Dennis Guse c6d24e9534 Pictures: removed MediaScanner. 2020-01-01 14:44:23 +01:00
Dennis Guse 441d3f721f Debug version: use a black logo. 2020-01-01 14:39:16 +01:00
Dennis Guse 3c212e4bcc Bugfix: for taking pictures; provide access to external/pictures. 2020-01-01 14:28:58 +01:00
Dennis Guse 50eb71bc1f Cleanup. 2020-01-01 14:16:02 +01:00
Dennis Guse 0806821b5f Fixed import/export message to show Storage Access Framework selected path. 2019-12-29 23:33:10 +01:00
Dennis Guse 36d0f17953 Removed french Congo translation (identical to values-fr). 2019-12-29 21:57:39 +01:00
Dennis Guse 700adb7972 Export dialog: removed "from folder" text. 2019-12-29 21:56:48 +01:00
Dennis Guse a156369f8b Import dialog: removed "from folder" text. 2019-12-29 21:32:01 +01:00
Dennis Guse c0f6dc511b Removed values-ar-rXB/strings.xml as it was for testing only. 2019-12-29 21:28:36 +01:00
Dennis Guse 63e1461eba Removed values-en-rXA/strings.xml as it was for testing only. 2019-12-29 20:59:30 +01:00
Dennis Guse 804e5f10c8 Removed values-bn/strings.xml as translations were invalid. 2019-12-29 20:53:37 +01:00
Dennis Guse 6cc7271a41 Build: debug version uses different icon and appLabel. 2019-12-29 20:46:46 +01:00
Dennis Guse f66194189b Removed translation description from values/strings.xml 2019-12-29 20:15:37 +01:00
Dennis Guse 5d97d6d492 Use Android 7.0 localization: merge latin american and present en-rGB as default for en. 2019-12-29 19:13:26 +01:00
Dennis Guse f3a308e669 BREAKING CHANGE: pictures are now stored in getExternalFilesDir(); no migration takes place! 2019-12-29 15:43:06 +01:00
Dennis Guse a230504925 Export & import via Storage Access Framework. 2019-12-29 15:41:28 +01:00
Dennis Guse 90e19e282c Upgraded tests to Junit4. 2019-12-29 15:31:53 +01:00
Dennis Guse b585bfe44c Moved to Github organization: OpenTracksApp. 2019-12-27 23:54:29 +01:00
Dennis Guse 475e04eeb4 Add PlantUML model for data structures. 2019-12-27 18:07:29 +01:00
Dennis Guse f89b2b0efb Removed TripStatists min/max latitude/longitude.
This also removed: search does not account for closer tracks (to current position).
2019-12-27 18:01:11 +01:00
Dennis Guse b94f3c176b Include unit in variable names. 2019-12-27 18:01:10 +01:00
Dennis Guse d1d729d287 Removed unused ContentProvider.getFirstTrackPointId(). 2019-12-27 18:01:07 +01:00
Dennis Guse e23bbcc405 Removed unnecessary Track.startId and Track.stopId. 2019-12-27 18:01:05 +01:00
Dennis Guse 4129985b0f Renamed SensorDataSetLocation to TrackPoint (similar to SQLite table). 2019-12-27 18:00:51 +01:00
Dennis Guse 5cb7b623d9 Removed TripStatistics from Waypoints. 2019-12-26 21:15:42 +01:00
Dennis Guse 3c52efadd6 Removed unused Waypoint.TripStatistics. 2019-12-26 21:15:41 +01:00
Dennis Guse 73b4977051 Removed unused Waypoint.startId and Waypoint.stopId. 2019-12-26 21:15:41 +01:00
Dennis Guse 422c2c02ce Assume GPS provides always time (removed workaround). 2019-12-26 21:15:41 +01:00
Dennis Guse d692329975 Settings: hide advanced recording settings.
And use switches rather than checkboxes.
2019-12-25 23:22:20 +01:00
Dennis Guse 931574bc11 Release v3.2.11 2019-12-25 10:19:22 +01:00
Dennis Guse caddbd8501 MarkerDetailActivity: removed leftover statistic functionality. 2019-12-23 21:44:22 +01:00
Dennis Guse 8ece2c8612 Removed WaypointCreationRequest. 2019-12-23 21:39:37 +01:00
Dennis Guse 3a219321d1 Removed country-specific translations that are identical. 2019-12-23 07:43:05 +01:00
Dennis Guse 35cb924a17 StatsFragment: show elevation without decimal digits. 2019-12-22 20:39:43 +01:00
Dennis Guse b67e5bbd20 StatsFragment: left align current elevation. 2019-12-22 20:33:23 +01:00
Dennis Guse 1db275230e Bugfix: crash on opening heart rate sensor dialog (settings). 2019-12-22 19:09:01 +01:00
Dennis Guse 5ac72092fb Test: export and import tracks via KML and GPX; now includes waypoints.
TripStatistics is not tested.
2019-12-22 18:59:23 +01:00
Dennis Guse bdd9ded1e8 Test: export and import tracks via KML and GPX.
KML now exports track icon (name; not the url).
2019-12-22 18:59:23 +01:00
Dennis Guse 759aeb6ed6 Removed DescriptionGenerator interface. 2019-12-22 18:59:23 +01:00
Dennis Guse 12023d51ae Move data classes into separate package. 2019-12-22 18:59:23 +01:00
Dennis Guse c426785b90 Cleanup. 2019-12-22 18:59:23 +01:00
Dennis Guse 09d960c7a1 First waypoint of a track does not store the statistics of the track. 2019-12-22 18:59:23 +01:00
Dennis Guse f37fe4a503 Removed WaypointType. 2019-12-22 18:59:23 +01:00
Dennis Guse 013e56bef0 Replaced TabHost by Material design TabsLayout. 2019-12-22 18:59:23 +01:00
Dennis Guse d6835ba766 TrackRecordingService does not save the context. 2019-12-22 18:59:23 +01:00
Dennis Guse d7890cbc81 Shortened fr-FR short description.
Thanks to Edern.
2019-12-17 20:35:41 +01:00
Dennis Guse 2b6886c534 Update release procedure. 2019-12-16 22:41:16 +01:00
Dennis Guse e77590a125 Release v3.2.10 2019-12-15 22:11:19 +01:00
Dennis Guse ca330b2d80 Cleanup. 2019-12-15 21:28:03 +01:00
Dennis Guse 20f1524701 Cleanup. 2019-12-15 17:09:44 +01:00
Dennis Guse 96b2a15643 Bugfix: import and export on Android 10 requires requesting (privacy-unfriendly) legacy behavior. 2019-12-15 16:50:13 +01:00
Dennis Guse ad59392e02 Fix travisCI. 2019-12-14 23:58:43 +01:00
Dennis Guse 29f92b887c Fixed TrackRecordingService.insertWaypoint() to allow deprecated behavior. 2019-12-14 23:55:04 +01:00
Dennis Guse 46328211c2 Added promotion sticker. 2019-12-14 23:55:04 +01:00
Dennis Guse 3760130d1f Github: add donation. 2019-12-14 23:42:07 +01:00
Dennis Guse 1094827fac TrackListActivity: fixed landscape layout. 2019-12-11 23:05:31 +01:00
Dennis Guse 36fcb44af5 TripRecordingService: removed unused trip statistics marker updater. 2019-12-11 23:05:31 +01:00
Dennis Guse a683fbac4b Device reboot: do not restart a not successfully ended track recording. 2019-12-11 23:05:31 +01:00
Dennis Guse 64ad9019d1 ChartView: is not configurable anymore. 2019-12-11 23:05:31 +01:00
Dennis Guse 1e80d5c8a8 ChartView: draw waypoints (again).
Functionality was (accidentally) removed in 5a264c5c53
2019-12-11 23:05:31 +01:00
Matthias Lisin ac765b1ee8 Revert duration hint to MAY_DUCK.
Pass OnAudioFocusChangeListener to abandonAudioFocus
2019-12-10 12:58:57 +01:00
Dennis Guse 850c4f0a9a TravisCI: disabled non-functioning installation of Android emulator. 2019-12-10 06:53:22 +01:00
Dennis Guse 4c32999752 Add Google Play link. 2019-12-10 06:50:58 +01:00
Dennis Guse 76823b167a TrackListActivity: removed delete all from action menu. 2019-12-08 13:12:15 +01:00
Dennis Guse 9994cfd260 Bugfix: NPE. 2019-12-07 13:37:06 +01:00
Dennis Guse 1750236474 Added automation for Google Play publication (using fastlane). 2019-12-06 22:26:40 +01:00
Dennis Guse fed6e31f9e Cleanup. 2019-12-06 08:00:20 +01:00
Dennis Guse 9bafc7d4a1 Release v3.2.9 2019-12-04 19:57:27 +01:00
Dennis Guse be58b48d20 Bugfix: StatsFragment (onResume and track was recording) overwrote in the total time UI until next UI update. 2019-12-03 22:21:45 +01:00
Dennis Guse 303abac980 Removed READ_PHONE_STATE permission and use AudioManager.MODE_IN_CALL instead. 2019-12-03 22:01:01 +01:00
Dennis Guse d1a459efc2 Get audio focus onStart. 2019-12-03 20:28:22 +01:00
Dennis Guse 96069e6f09 Cleanup. 2019-12-03 20:24:57 +01:00
Dennis Guse 301a721329 Settings: localize no heart rate sensor selected. 2019-12-03 19:53:14 +01:00
Dennis Guse e8c6801a32 Refactor StatsFragment. 2019-12-03 19:19:19 +01:00
Dennis Guse 355f3ef614 Added "screen always on"-feature to TrackDetailActivity.
And the "show on lockscreen"-feature is only used while recording.
2019-12-03 16:57:56 +01:00
Dennis Guse eaa6083586 Bugfix: Heart rate was not shown in stats. 2019-12-03 07:31:29 +01:00
Dennis Guse a0c5d02c51 Fixed and removed device features (AndroidManifest). 2019-12-03 07:31:29 +01:00
Dennis Guse 76f9f5fc82 MarkerDetailFragment now shows the blue pin as default image. 2019-12-03 07:31:29 +01:00
Dennis Guse 8e2598e93b Removed split markers. 2019-12-03 07:31:24 +01:00
Dennis Guse c0672c41df Bugfix: crash if heart rate sensor was null (i.e., preference not initialized). 2019-11-29 23:18:49 +01:00
Dennis Guse 4c6601fbc5 Release v3.2.8 2019-11-29 22:58:22 +01:00
edern-appere b99c87d1bd Added French translation for Fdroid. 2019-11-29 22:46:43 +01:00
Dennis Guse 66ffbfe951 Fixed ChartFragment tests.
Initialization of looper was missing.
2019-11-29 22:43:51 +01:00
Dennis Guse 8cb01a1027 Bugfix: sharing a track was not adding the file(s).
While migrating to enable sharing multiple tracks a configuration option was missed.
2019-11-29 22:39:36 +01:00
Dennis Guse 193e5eccbb Cleanup. 2019-11-29 22:39:36 +01:00
Dennis Guse bf7fc28bb0 Bugfix: picture name suffixes were malformed on export and thus could not be re-imported. 2019-11-29 22:39:31 +01:00
Dennis Guse e35cbc343a Add pull request template. 2019-11-29 07:54:39 +01:00
Dennis Guse 1c7b053c8c Fixed MarkerDetailActivity.
The orientation of the layout was not set and thus adding the toolbar broke the UI.
2019-11-28 18:09:11 +01:00
Dennis Guse 88cdcd4b47 Use GestureDetector rather than doing it on our own. 2019-11-27 21:42:05 +01:00
Dennis Guse 1432fed8f9 Removed zoom controls for ChartFragment. 2019-11-27 21:41:53 +01:00
Dennis Guse a14307b9a4 Moved Chart* into own package. 2019-11-27 20:19:12 +01:00
Dennis Guse 3bdce5aa08 Cleanup: use autocloseable. 2019-11-25 23:09:08 +01:00
Dennis Guse d7526f93b8 Cleanup: removed ContentProviderUtils interface. 2019-11-25 23:02:48 +01:00
Dennis Guse 8932578b90 Release v3.2.7 2019-11-25 22:03:40 +01:00
Dennis Guse 631ac9881d Wrap help into PreferenceCategory. 2019-11-25 22:03:02 +01:00
Dennis Guse 78334ed797 Fixed Spanish project description (short); was still German. 2019-11-24 21:12:07 +01:00
Dennis Guse b06da89654 Show chart for distance as well as time.
Also removed configuration option.
2019-11-24 19:55:00 +01:00
Dennis Guse d6c2e78993 Cleanup. 2019-11-24 18:48:31 +01:00
Dennis Guse 67d1ac551d Show GPS accuracy while recording in the notification of the TrackRecordingService and notify if accuracy goes beyond configured threshold. 2019-11-24 18:44:04 +01:00
Dennis Guse dcb6be717d Cleanup. 2019-11-23 22:46:39 +01:00
Dennis Guse f2c04eec1a Add help activity.
Can be accessed from Settings.
2019-11-23 21:43:07 +01:00
Dennis Guse a08311cf43 Temporary bugfix: disable landscape mode for TrackListActivity. 2019-11-22 07:44:01 +01:00
Dennis Guse 1e28f3aa86 Add TTSUtils.
(required by previous commit)
2019-11-22 07:43:19 +01:00
Dennis Guse 426af41f2c Move TTS stream definition into one place. 2019-11-20 19:27:41 +01:00
Dennis Guse fd08a219f9 Add Spanish store description. 2019-11-20 19:13:21 +01:00
Dennis Guse 25b6aec0e9 Release v3.2.6 2019-11-19 22:09:10 +01:00
Dennis Guse 3e40209d15 Refactoring. 2019-11-19 08:00:12 +01:00
Dennis Guse 54ff94f905 Settings: renamed Bluetooth sensor to "Heart rate". 2019-11-18 17:16:50 +01:00
Dennis Guse 7a4faf6b0d Fixed SettingsActivity. 2019-11-18 17:13:42 +01:00
Dennis Guse 8922b00a5f README: add screenshots. 2019-11-17 18:45:29 +01:00
Dennis Guse 702efa8693 Removed shortcut to Android's Bluetooth settings. 2019-11-17 18:25:01 +01:00
Dennis Guse e7a8c897a3 Settings: scan for Bluetooth sensors rather than using bonded devices.
Bonded devices do no provide information what services (e.g., heart rate) they support.
2019-11-17 18:24:02 +01:00
Dennis Guse c59d1d87bd Bluetooth: move getting adapter to BluetoothUtils. 2019-11-17 18:23:08 +01:00
Dennis Guse a706efb8b3 Bugfix. 2019-11-14 18:30:57 +01:00
Dennis Guse e391e83a00 Build: show version and tag in about page. 2019-11-14 18:04:50 +01:00
Dennis Guse 2940bf20d8 Settings: remove all defaults from code. 2019-11-12 21:33:04 +01:00
Dennis Guse 081db5a58b Settings: remove default setting for default activity. 2019-11-12 21:33:04 +01:00
Dennis Guse b34805272f Bugfix: not connecting to heart rate sensor - need to start BluetoothRemoteSensorManager.
Introduced in bc5ab0d7e5
2019-11-09 00:02:20 +01:00
Dennis Guse 98443dba99 Cleanup. 2019-11-08 22:45:34 +01:00
Dennis Guse dd6e733d8c Release v3.2.5 2019-11-08 22:31:49 +01:00
Dennis Guse 92d3439c3b Re-enable SummaryProvider for default_activity. 2019-11-08 07:58:30 +01:00
Dennis Guse bd155cd600 Use SummaryProvider.
Fixed #46.
2019-11-08 07:36:45 +01:00
Dennis Guse e14e53c0ae Fix hack to update summary of statsRate if statsUnit change. 2019-11-07 23:32:28 +01:00
Dennis Guse 77a6a63174 Re-initialize PreferenceFragment, when sharedpreferences were reset. 2019-11-07 22:24:15 +01:00
Dennis Guse 40f2463f09 Fail safety: if settings cannot loaded (invalid data in sharedpreferences), reset it and try again. 2019-11-07 19:15:17 +01:00
Dennis Guse e5d1048c8d Bugfix reset settings: really clear sharedPreferences before settings default values. 2019-11-07 19:15:17 +01:00
Dennis Guse bf3b529fb2 Add link to Transifex for localization.
Fixes #27.
2019-11-07 19:15:17 +01:00
Dennis Guse d0fde46027 Android does not allow referencing integer in string-arrays: store everything as string and parse it later (also for tests). 2019-11-06 18:40:58 +01:00
Dennis Guse 0f348dff10 Android does not allow referencing integer in string-arrays: store everything as string and parse it later. 2019-11-06 18:16:40 +01:00
Dennis Guse d3f5080e73 Fixed issues introduced in 353c958ae0 and 2a60aa8f59. 2019-11-06 07:42:15 +01:00
Dennis Guse a453c8ec1e TrackDetailActivity: disable activity type changer in view mode.
Fix #3.
2019-11-05 22:45:29 +01:00
Dennis Guse 2a60aa8f59 Settings: removed some default definitions from code into xml. 2019-11-05 22:02:23 +01:00
Dennis Guse 41e7870509 Settings: removed some default definitions from code into xml. 2019-11-05 21:13:51 +01:00
Dennis Guse 57f66b77ab Reorganized settings (sharedpreferences and keys); xml only. 2019-11-05 21:13:51 +01:00
Dennis Guse 53572406e9 Mark foreground service to use location and connection to other devices, so it can run properly on Android 10+ when app is in the background.
Might fix #61.
2019-11-05 21:13:51 +01:00
Dennis Guse a288255ab1 Added issue and feature templates. 2019-11-05 19:33:57 +01:00
Dennis Guse 353c958ae0 Moved accessing sharedPreferences into PreferencesUtils. 2019-11-03 01:44:46 +01:00
Dennis Guse bc5ab0d7e5 Removed unnecessary RemoteSensorManagerFactory.
TrackRecordingService is only using BluetoothRemoteSensorManager and thus factory pattern is not required.
2019-11-03 00:21:51 +01:00
Dennis Guse 9d915b5640 Cleanup. 2019-11-03 00:13:14 +01:00
Dennis Guse 3aaee270eb Release v3.2.4 2019-11-01 23:54:34 +01:00
Dennis Guse d53a7156e2 Bugfix: removed custom dialogs to set voice/split frequency preferences from the context menu directly.
Fixed #49.
2019-11-01 23:38:07 +01:00
Dennis Guse 3af24ea3d0 Release v3.2.3 2019-10-31 22:07:21 +01:00
Dennis Guse 5483212b06 Fixed StringUtils.formatDecimal to work with decimals of 0. 2019-10-30 23:02:02 +01:00
Dennis Guse 419279fe5e Cleanup. 2019-10-30 22:36:22 +01:00
Dennis Guse 501e899b27 StatsFragment: need to reconnect to TrackRecordingService in onResume as it is unbound in onStop.
Fixed #50.
2019-10-30 22:36:16 +01:00
Dennis Guse 3bb9edb009 Connect to new sensor if settings are changed.
Fixed #52.
2019-10-30 07:48:29 +01:00
Dennis Guse e52b1188a3 Cleanup. 2019-10-29 23:05:17 +01:00
Dennis Guse 9e877e4762 Removed stale verify. 2019-10-29 22:39:51 +01:00
Dennis Guse 5711757110 If sharing multiple tracks, share them as separate files. 2019-10-29 17:43:23 +01:00
Dennis Guse e1c33dad43 Move to Android ShareSheet. 2019-10-29 17:34:21 +01:00
Dennis Guse 4b2400774c TravisCI: start emulator, which is required for testing. 2019-10-28 23:25:07 +01:00
Dennis Guse afd4a59622 CI: enabled tests.
Fixed #6.
2019-10-27 16:50:28 +01:00
Dennis Guse bdce437470 Removed AnnoucementPeriodTaskTest as these were broken beyond repair. 2019-10-27 16:46:56 +01:00
Dennis Guse 33479cda11 Fixed TrackRecordingService tests: start service properly if needed. 2019-10-27 15:58:50 +01:00
Dennis Guse 385cee3dc7 Fixed Date parsing test (account for
rounding up microseconds).
2019-10-27 13:45:43 +01:00
Dennis Guse d0bbd7655b Ignore KML waypoints that do not have a type. 2019-10-27 13:40:06 +01:00
Dennis Guse a83b8f62fe Repaired Track importer tests. 2019-10-27 13:24:20 +01:00
Dennis Guse 0cd9176703 Heart rate must be larger than zero to be shown while tracking. 2019-10-26 14:35:03 +02:00
Dennis Guse d8e526923d Removed RemoteSensorManager as abstraction was not used anymore. 2019-10-26 14:26:16 +02:00
Dennis Guse 3a6f417662 Added disconnect toast message. 2019-10-26 14:23:38 +02:00
Dennis Guse 7d807a7f11 Refactor Bluetooth LE stack to use autoReconnect.
Removed additional timerTask to reconnect (was not working anymore).

Fixed #41.
2019-10-26 14:11:55 +02:00
Dennis Guse e9d5718495 Cleanup. 2019-10-23 18:47:32 +02:00
Dennis Guse 0dd7f74a11 Removed temporary sensor manager (was not used anymore). 2019-10-22 21:15:11 +02:00
Dennis Guse 9bd4ba489c Release: v3.2.2 2019-10-21 23:00:13 +02:00
Dennis Guse dd55b50dad Re-added actionbar (actually toolbar) to SettingsActivity. 2019-10-21 22:54:23 +02:00
Dennis Guse 1719d21a5b Bugfix: export now really includes sensor data (as double) and import is done as float. 2019-10-21 22:34:33 +02:00
Dennis Guse 75ef8d99d4 SensorDataSet: data unavailable is now NaN. 2019-10-21 22:34:28 +02:00
Dennis Guse ae06fd1680 Bugfix: export sensor data if available and allowed. 2019-10-20 18:39:31 +02:00
Dennis Guse 2b03f7dea8 Use LinearLayout for SearchActivity (show toolbar correctly). 2019-10-20 17:05:09 +02:00
Dennis Guse 7591b012b4 Extracted ShareActionProvider setup code into a separate class. 2019-10-20 16:55:28 +02:00
Dennis Guse 0e089f49f3 Re-added application logo to material design toolbar. 2019-10-19 12:27:11 +02:00
Dennis Guse 5ad1856b13 Removed context menu from ListActivities. 2019-10-17 21:32:55 +02:00
Dennis Guse 965dbbe829 Removed custom action bar style. 2019-10-17 21:26:31 +02:00
Dennis Guse 262c9886df Removed aggregated stats activity. 2019-10-17 21:22:07 +02:00
Dennis Guse ac5a9ea65d Use Androidx compat's toolbar instead of actionbars.
ListView's multiselect still uses actionbar.
2019-10-17 21:19:14 +02:00
Dennis Guse ed9b4c8f3d Add workaround for Compat's ShareActionProvider, which is not available when inflating in a AbsListView.MultiChoiceModeListener. 2019-10-17 18:33:45 +02:00
Dennis Guse 209aa6a8a5 Hide share action from SearchActivity as it is not (yet) functional. 2019-10-17 07:58:11 +02:00
Dennis Guse 81ee6ac1fa Do not use custom style for AlertDialogs, so PreferenceDialogs and AlertDialogs look the same. 2019-10-17 06:38:05 +02:00
Dennis Guse 024dd603cb Migration to Androidx PreferenceCompat. 2019-10-17 06:00:47 +02:00
Dennis Guse 50654c6baf Release v3.2.1 2019-10-15 18:36:40 +02:00
Dennis Guse 8c9bf60fc6 Update Settings if the units (metric/imperial) are changed. 2019-10-15 17:29:59 +02:00
Dennis Guse a83eb0b983 Added de-DE description. 2019-10-13 12:19:27 +02:00
Dennis Guse ab3cbfea0f Metadata: adjust to fastlane layout. 2019-10-13 12:19:27 +02:00
Dennis Guse c93e5ca1a6 Settings: moved to Androidx Compat and use one screen only. 2019-10-12 18:00:51 +02:00
Dennis Guse c2841092da Settings: moved setup into helper. 2019-09-19 22:30:28 +02:00
Dennis Guse 8b11e1d611 DialogFragments: do not save state. 2019-09-19 22:30:28 +02:00
Dennis Guse 6375a851b2 Moved ChooseActivityTypeDialog to be a real DialogFragment.
Note: This breaks RecordingsSettingsActivity.
2019-09-19 22:28:04 +02:00
Dennis Guse f21200a7ec UnitConversation: always use double for division.
Fixed wrong computation (00:00) of total time.
2019-09-19 19:29:49 +02:00
Dennis Guse 5752cebdec Released 3.2.0 2019-09-19 19:14:34 +02:00
Dennis Guse fa6329c157 Fdroid: downgraded to Gradle 5.5 2019-09-19 19:08:04 +02:00
Dennis Guse 98a9884798 Fdroid: added link in README and moved metadata to fastlane folder. 2019-09-19 19:04:17 +02:00
Dennis Guse 1f7f615411 Import/Export: first option is KMZ containing all data. 2019-09-16 23:43:30 +02:00
Dennis Guse a79345ccf9 Grant ACCESS_FINE_LOCATION for TrackRecordingServiceTest. 2019-09-16 22:47:37 +02:00
Dennis Guse 732e0e76f9 Removed custom styling of title divider of used dialogs (just too much code). 2019-09-16 22:37:37 +02:00
Dennis Guse 63d29f2097 Cleanup. 2019-09-16 19:44:34 +02:00
Dennis Guse 9dd2f7d1c1 KML/KMZ export and share: add configuration to export with trackdetail without the rest.
Fixed some issues in TRACK_ONLY mode as too much data was exposed.
2019-09-15 09:47:48 +02:00
Dennis Guse afd25363b3 For export and import: KMZ first as it contains all data incl. photos. 2019-09-15 09:22:10 +02:00
Dennis Guse 2eb5a42099 When it should be KMZ, make it KMZ. 2019-09-15 08:45:11 +02:00
Dennis Guse 9c753034b4 Update title in MarkerDetailActivity when viewpager changes. 2019-09-15 00:31:53 +02:00
Dennis Guse 95b7c701a2 Cleanup: do not assume first waypoint contains (always) TripStatistics. 2019-09-15 00:14:15 +02:00
Dennis Guse 56685a9dec Bugfix: export of images via KMZ is now working. 2019-09-14 23:35:28 +02:00
Dennis Guse 45b13ea813 Removed tab icons as they were not shown anyhow. 2019-09-14 22:43:26 +02:00
Dennis Guse b0feed84bb Removed voice frequency menu icon as it not shown anyhow. 2019-09-14 22:34:58 +02:00
Dennis Guse 0e2da83e1f Removed snowboarding and skiing due to lack of icons. 2019-09-14 22:20:29 +02:00
Dennis Guse fa364a150d Styling for progress bar via tint. 2019-09-14 21:50:10 +02:00
Dennis Guse 6ec78d77a9 Changed gps menu item icon to represent actual state of the gps (and not the action to be executed). 2019-09-14 13:25:23 +02:00
Dennis Guse 8853bb5408 Replaced drawables with vectors (SVG) for TrackController.
- ic_button_pause
- ic_button_record
- ic_button_stop
2019-09-14 02:58:05 +02:00
Dennis Guse e4d6355875 Replaced drawables with vectors (SVG) for TrackListActivity.
- ic_pause
- ic_record
- ic_arrow_long
2019-09-14 01:34:41 +02:00
Dennis Guse 1e08d72cc9 Removed resources of indetermined progressbar (unused). 2019-09-14 00:22:35 +02:00
Dennis Guse ea82d2df94 Fixed tabhost resource id. 2019-09-13 23:55:39 +02:00
Dennis Guse 71c0114f8f Fixed lint error. 2019-09-13 23:14:14 +02:00
Dennis Guse a3955c7c37 Made generic activity type selectable.
Decreased icon size.
2019-09-13 23:00:12 +02:00
Dennis Guse e610b70590 Use logo (white) for generic track (replaces similar a png with gradient). 2019-09-13 22:55:25 +02:00
Dennis Guse 7f4d47d7b5 Foreground service notification now uses the logo directly (vector). 2019-09-13 22:38:39 +02:00
Dennis Guse fd27ec11a2 Replaced chartViews current position marker with logo. 2019-09-12 23:52:04 +02:00
Dennis Guse 2aa036f3aa Material design icons for dialogs. 2019-09-12 23:44:21 +02:00
Dennis Guse 2c49a4f3f1 Introduced material design icons. 2019-09-12 23:24:19 +02:00
Dennis Guse 10e4a52947 Fixed stats_content. 2019-09-12 20:39:13 +02:00
Dennis Guse 9f3936e180 Removed unused icons. 2019-09-12 20:17:29 +02:00
Dennis Guse a3a76ce0c3 Disabled lint for AppCompatResource. 2019-09-12 20:15:03 +02:00
Dennis Guse 545db52f94 Cleanup. 2019-09-12 18:33:45 +02:00
Dennis Guse bfa02a97ee Upgrade appcompat library. 2019-09-12 17:57:33 +02:00
Dennis Guse 3a2eeaa204 Fixed lint warnings in stats_content layout. 2019-09-12 17:57:33 +02:00
Dennis Guse 42b3a5dfc9 Refactored stats_content layout to contain all content (not use include). 2019-09-12 17:57:28 +02:00
Dennis Guse b5cb47a0c7 Refactored TrackRecordingServiceConnection: made static methods object methods. 2019-09-10 22:45:20 +02:00
Dennis Guse 443e069a13 Refactored TrackRecordingServiceConnection.
- implemented interfaces directly
- moved static methods from TrackRecordingServiceConnectionUtil
2019-09-10 22:37:54 +02:00
Dennis Guse 9c432c3110 Removed unused menu from SearchListActivity. 2019-09-10 21:20:41 +02:00
Dennis Guse 79934d51ac Revert "Moved from onTouch to onClick listeners."
This reverts commit 025dee68
2019-09-10 21:20:41 +02:00
Dennis Guse 496cf9145b Removed usage of Broadcast (were used by Widget). 2019-09-10 21:20:40 +02:00
Dennis Guse 264bb3746f Removed ControlRecordingService.
It was implemented to enable other apps to start and stop track recordings.
2019-09-10 21:19:11 +02:00
Dennis Guse 7b1ccf4507 Cleanup AndroidManifest. 2019-09-10 21:18:55 +02:00
Dennis Guse a326265dba Fixed searchview, which did not trigger SearchListActivity. 2019-09-10 21:18:25 +02:00
Dennis Guse 0864bc8104 Cleaned up ContentProviderUtils's factory pattern. 2019-09-10 21:18:03 +02:00
Dennis Guse f5656470a9 Removed Parcelable from all data classes as it not needed anymore. 2019-09-10 21:17:45 +02:00
Dennis Guse 5bc773f1aa Removed modifiedtime from Track - was likely a Google Drive leftover. 2019-09-10 21:17:18 +02:00
Dennis Guse 6bfd8c8db8 Cleanup: Use Pair instead of String[2]. 2019-09-10 21:16:58 +02:00
Dennis Guse df2865456e Removed obsolete workaround for DateUtils.formatElapsedTime(). 2019-09-10 21:16:38 +02:00
Dennis Guse fc6d5f1f23 Removed Widget. 2019-09-10 21:16:16 +02:00
Dennis Guse 96dab8c81b Build: enabled abort on lint errors. 2019-09-10 21:16:00 +02:00
Dennis Guse 1d13d51f0c Cleanup. 2019-09-10 21:12:56 +02:00
Dennis Guse 87d92d25f9 Cleanup: mainly code formatting and made LocationIterator implement autoclosable. 2019-09-08 12:39:40 +02:00
Dennis Guse 025dee6821 Moved from onTouch to onClick listeners. 2019-09-08 12:39:40 +02:00
Dennis Guse 796d086615 Cleanup.
Cleanup.
2019-09-08 12:39:37 +02:00
Dennis Guse bb1ef08474 Splitted TrackRecordingServiceTest into two files and fixed issue about calling Looper.prepare() only once for all tests. 2019-09-05 22:44:27 +02:00
Dennis Guse cc770d8bb4 Fixed (some) AnnoucementPeriodicTask[Factory] tests. 2019-09-05 07:41:16 +02:00
Dennis Guse 8a56c262cb Cleanup. 2019-09-04 22:09:22 +02:00
Dennis Guse c0a9958b5b Fixed (some) TrackRecordingServiceTests. 2019-09-04 21:33:30 +02:00
Dennis Guse 7351cd3377 TrackDataHubTest is beyond repair and thus removed. 2019-09-04 20:41:39 +02:00
Dennis Guse 7b3d5848b9 Fixed ControlRecordingService: only unbind if connected. 2019-09-04 17:43:24 +02:00
Dennis Guse 0f311e062e Fixed ContentProviderUtils tests.
Account for using a real (non-empty) database and thus non-predictable object ids.
2019-09-03 21:32:35 +02:00
Dennis Guse 57930a8d86 Added changelog for 2991/v3.1.0 2019-09-03 21:32:35 +02:00
626 changed files with 17208 additions and 33988 deletions
+4
View File
@@ -0,0 +1,4 @@
# These are supported funding model platforms
liberapay: dennis.guse
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
+26
View File
@@ -0,0 +1,26 @@
---
name: Bug report
about: Report issues of OpenTracks features
title: ''
labels: potential bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
If applicable:
* add screenshots
* add logcat output
**Technical information**
- Device: [e.g. Nexus 7 (2013)]
- OS: [e.g. LineageOS 15.1]
- Version [e.g. v3.2.4]
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for OpenTracks
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+10
View File
@@ -0,0 +1,10 @@
# Thanks for your contribution.
**Describe the pull request**
A clear and concise description of what the pull request changes/adds.
**Link to the the issue**
(If available): The link to the issue that this pull request solves.
**License agreement**
By opening this pull request, you are providing your contribution under the _Apache License 2.0_ (see [LICENSE.md](LICENSE.md)).
+21
View File
@@ -0,0 +1,21 @@
#https://github.com/marketplace/actions/android-emulator-runner
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: macos-latest
strategy:
matrix:
api-level: [29]
target: [default]
steps:
- name: checkout
uses: actions/checkout@v2
- name: run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: x86_64
script: ./gradlew connectedCheck
+6 -7
View File
@@ -45,19 +45,16 @@ captures/
# IntelliJ IDEA
*.iml
*.iws
/out/
# User-specific configurations
.idea/.name
.idea/assetWizardSettings.xml
.idea/caches/
.idea/compiler.xml
.idea/copyright/profiles_settings.xml
.idea/dataSources.ids
.idea/datasources.xml
.idea/dictionaries
.idea/dynamic.xml
.idea/encodings.xml
.idea/jsLibraryMappings.xml
@@ -67,14 +64,16 @@ captures/
.idea/scopes/scope_settings.xml
.idea/shelf/
.idea/sqlDataSources.xml
.idea/tasks.xml
.idea/vcs.xml
.idea/uiDesigner.xml
.idea/workspace.xml
# Keystore files
# Uncomment the following line if you do not want to check your keystore files in.
#*.jks
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Fastlane
Appfile
releasePlayStore/*
Gemfile.lock
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RenderSettings">
<option name="showDecorations" value="true" />
</component>
</project>
-12
View File
@@ -1,12 +0,0 @@
language: android
dist: trusty
android:
components:
- build-tools-28.0.3
- android-29
script:
- ./gradlew build
#TODO: When all tests are passing, CI should also execute them.
#- ./gradlew connectedCheck
-12
View File
@@ -1,12 +0,0 @@
# OpenTrack: Developer information
## Create releases for FDroid
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
2. Decide on _version name_ (semantic versioning)
3. Manually update _version code_ and _version name_ in `AndroidManifest.xml` (used by FDroid)
4. Create changelog (`_version code_.txt`)
5. Create commit with all changes
6. Push commits
7. Tag newly create commit with _version name_
8. Push the tagged to public repository
+3
View File
@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane"
+43 -9
View File
@@ -1,21 +1,43 @@
# OpenTracks: a sport tracker
![OpenTracks logo](LOGO.svg)
# <img src="drawable-svg/LOGO.svg" alt="OpenTracks logo" height="40"></img> OpenTracks: a sport tracker
_OpenTracks_ is a sport tracking application that completely respects your privacy.
<a href="https://liberapay.com/dennis.guse/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
<table>
<tr>
<th>Free</th>
<th>Paid</th>
<th>Donations</th>
</tr>
<tr>
<td>
<a href="https://f-droid.org/packages/de.dennisguse.opentracks">
<img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
</a>
</td>
<td>
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
</a>
</td>
<td>
<a href="https://liberapay.com/dennis.guse/donate">
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
</a>
</td>
</tr>
</table>
## Features:
* __Tracking:__ track your sport and outdoor activities
* __Photos and Markers:__ mark interesting locations while tracking
* __Heart rate:__ supports bluetooth LE sensors
* __Export data:__ Export tracks either as KML or GPX
* __Voice annoucements__
* __Photos and Markers:__ mark interesting locations while tracking (also with photos)
* __Heart rate:__ supports Bluetooth LE sensors
* __Cycling (speed and cadence)__: supports Bluetooth LE sensors
* __Export data:__ export tracks either as KMZ (incl. photos), KML or GPX
* __No Internet access:__ Internet is not used
* __Voice announcements__
* __No advertising__
* __Privacy:__
* _contains in-app analytics_
* _does not contain any in-app analytics_
* _does not have Google Play Services_
* _has no cloud integration_
@@ -26,6 +48,18 @@ _Please note:_
_Tracks_ does not contain a _map_ (as this would required Internet access).
To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)) needs to be installed.
## Custom Dashboards
As of v3.3.1 OpenTracks enables to use custom dashboards.
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
## Screenshots
<div>
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_1.png">
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_2.png">
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_3.png">
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_4.png">
</div>
## Project history
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/)).
+30
View File
@@ -0,0 +1,30 @@
# OpenTracks Data API
## Reason
OpenTracks by itself only provides basic features for data visualization and also lacks functionality for analytics.
By itself OpenTracks provides a basic dashboard that can show summary of a track (while recording as well as afterwards).
Although this might be (mostly) sufficient, it has some limitations.
This include limited configurability (e.g., ordering of shown elements) and the lack of Internet access.
In fact, the later is considered a feature but severely limits the implementation presenting a map (at least in an easy way).
_NOTE:_ for showing a track on a map a workaround was implemented in OpenTracks.
Data could be exported as KMZ and presented in 3rd-party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
For the time being, the OpenTracks Data API can be used to implement mainly custom dashboards.
## Implementation
OpenTracks by default does not expose any data to 3rd-party applications.
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s of one or more selected tracks.
For each, one URI is provided.
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
Limitations:
* only tracks selected by the user are exposed,
* `Waypoints` are not exposed,
* access is only granted temporarily and automatically revoked (see `FLAG_GRANT_READ_URI_PERMISSION`),
* no write access is possible, and
* neither track recordings can be started, paused, or stopped.
All required information can be found in `IntentDashboardUtils.java`.
A _reference implementation_ is the [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard).
+37
View File
@@ -0,0 +1,37 @@
# OpenTracks: Developer information
## Craft new releases
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
2. Decide on _version name_ (semantic versioning)
3. Manually update _version code_ and _version name_ in `build.gradle`
4. Create changelog (`_version code_.txt`)
5. Create commit with all changes
6. Tag newly create commit with _version name_ (e.g., v3.2.1)
7. Push commits and tags to public repository
8. Add changelog to tag (Github releases)
## F-Droid
Application id: de.dennisguse.opentracks
It if it properly tagged F-Droid takes care of building the app and publishing.
Usually, it takes two days before the update is published (build, signed, and published).
The recent build activity can be found [here](https://f-droid.org/wiki/index.php?title=Special:RecentChanges&days=7&from=&hidebots=0&hideanons=1&hideliu=1&limit=500)
## Google Play Store
Application id: de.dennisguse.opentracks.playstore
Requirement: `gem install fastlane`
1. Build signed apk
(requires keystore)
2. Upload signed apk to [Google Play Console](https://play.google.com/apps/publish)
3. Update store meta data (e.g., description and screenshots)
`fastlane supply --version_code VERSION_CODE --skip_upload_apk`
(use the version code from the manifest)
(requires API key)
## Translations (localization)
Translations of the OpenTracks are handled in Transifex: [https://www.transifex.com/opentracks](https://www.transifex.com/opentracks/).
+30 -13
View File
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath 'com.android.tools.build:gradle:3.6.2'
}
}
@@ -35,10 +35,14 @@ def getVersionName = { ->
try {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'describe', '--tags', '--dirty'
commandLine 'git', 'describe', '--tags'
standardOutput = stdout
}
return stdout.toString().trim()
exec {
commandLine 'git', 'log', '--format="%h"', '-n 1'
standardOutput = stdout
}
return stdout.toString().replace('\n', '-').replace('"', '').trim()
}
catch (ignored) {
return null
@@ -50,11 +54,19 @@ android {
defaultConfig {
applicationId "de.dennisguse.opentracks"
//Useful for debugging.
//versionCode getVersionCode()
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_color_24dp",
applicationName: "@string/app_name"
]
versionCode 3369
//versionCode getVersionCode() //Useful for debugging.
versionName "v3.7.0"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
minSdkVersion 21
targetSdkVersion 29
@@ -65,25 +77,31 @@ android {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_black_24dp",
applicationName: "OpenTracks (Debug)"
]
}
release {
minifyEnabled false
}
releasePlayStore {
applicationIdSuffix ".playstore"
versionNameSuffix "-PlayStore"
}
}
applicationVariants.all { variant ->
variant.resValue "string", "applicationId", variant.applicationId
}
//TODO: Should be removed (when LINT does not throw any errors).
lintOptions {
abortOnError false
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
@@ -95,6 +113,5 @@ dependencies {
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:3.0.0'
implementation 'com.google.android.material:material:1.1.0'
}
+40
View File
@@ -0,0 +1,40 @@
@startuml
title
State of OpenTracks
end title
legend right
SettingsActivity can be reached
from every activity showing a menu.
endlegend
[*] --> TrackListActivity
TrackListActivity --> TrackDetailActivity : Start recording
TrackListActivity --> SearchListActivity
TrackListActivity --> MarkerListActivity : Show all markers
TrackListActivity --> TrackDetailActivity : Show track
TrackListActivity --> TrackDeleteActivity
SearchListActivity --> MarkerDetailActivity : Search result
SearchListActivity --> TrackDetailActivity : Search result
TrackDetailActivity --> TrackDeleteActivity
TrackDetailActivity --> TrackEditActivity
TrackDetailActivity --> MarkerListActivity
TrackDetailActivity --> MarkerEditActivity : Add marker
TrackDetailActivity --> CameraIntent : Add marker (with photo)
TrackDetailActivity --> ShareFileIntent
TrackDetailActivity --> DashboardIntent : Database cursor
TrackDetailActivity --> TrackOnMapIntent : KMZ
MarkerListActivity --> MarkerDetailActivity
MarkerDetailActivity --> MarkerEditActivity
MarkerDetailActivity --> CoordinateOnMapIntent : URI
SettingsActivity --> AboutActivity
SettingsActivity --> HelpActivity
SettingsActivity --> ExportActivity
SettingsActivity --> ImportActivity
@enduml
+52
View File
@@ -0,0 +1,52 @@
@startuml
class Track {
-id
-name
-description
-category
-numberOfPoints
-icon
}
Track o-- TrackPoint
Track o-- Waypoint
Track "1" - "1" TripStatistics
class TripStatistics {
- startTime
- stopTime
- totalDistance
- totalTime
- movingTime
- maxSpeed
- totalElevationGain
}
class TrackPoint extends android.location.Location {
- id
- trackId
- longitude
- latitude
- time
- altitude
- accuracy
- speed
- bearing
- sensor_heartrate
- sensor_cadence
- sensor_power
}
class Waypoint {
- id
- trackId
- name
- description
- category
- icon
- length
- duration
- location
- tripStatistics
- photoUrl
}
@enduml
View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

+41
View File
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="188"
id="svg2"
version="1.1"
viewBox="0 0 78 188"
width="78"
xmlns="http://www.w3.org/2000/svg">
<defs id="defs6" />
<g id="layer1" />
<g
style="display:inline"
id="layer2" />
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
d="m 1,150 38,37 39,-37 H 50 V 97 L 29,81 v 69 z"
id="path31161" />
<path
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
d="M 29,51 V 70 L 50,86 V 67 Z"
id="path31169" />
<path
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
d="M 29,23 V 40 L 50,56 V 39 Z"
id="path31192" />
<path
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
d="M 29,0 V 14 L 50,29 V 16 Z"
id="path31194" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+71
View File
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="ic_button_pause.svg">
<defs id="defs6" />
<g
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<circle
style="fill:none;fill-opacity:1;stroke:#a31a1f;stroke-width:5.03185225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path12"
r="59.984074" />
<rect
style="fill:#efefef;fill-opacity:0.87843137;stroke:none;stroke-width:4.82273912;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
height="17.940611"
id="rect837"
width="5"
x="54"
y="54.059391" />
<rect
style="fill:#efefef;fill-opacity:0.87843137;stroke:none;stroke-width:4.82273865;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
height="17.940611"
id="rect837-3"
width="5"
x="66"
y="54" />
</g>
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
bordercolor="#666666"
borderopacity="1"
gridtolerance="10"
guidetolerance="10"
id="namedview4"
objecttolerance="10"
pagecolor="#ffffff"
showgrid="false"
inkscape:current-layer="layer1"
inkscape:cx="75.030293"
inkscape:cy="72.881196"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-height="1152"
inkscape:window-maximized="0"
inkscape:window-width="2048"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:zoom="5.3400704" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<defs id="defs6" />
<g id="layer1">
<circle
style="fill:none;fill-opacity:1;stroke:#a31a1f;stroke-width:5.03185225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path12"
r="59.984074" />
<circle
style="fill:#414141;fill-opacity:0.4;stroke:none;stroke-width:6.9172821;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path835"
r="50" />
<rect
style="fill:#efefef;fill-opacity:0.87843137;stroke:none;stroke-width:4.82273912;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
height="17.940611"
id="rect837"
width="5"
x="54"
y="54.059391" />
<rect
style="fill:#efefef;fill-opacity:0.87843137;stroke:none;stroke-width:4.82273865;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
height="17.940611"
id="rect837-3"
width="5"
x="66"
y="54" />
</g>
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+41
View File
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<defs id="defs6" />
<g id="layer1">
<circle
style="fill:none;fill-opacity:1;stroke:#a31a1f;stroke-width:5.03185225;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path12"
r="59.984074" />
<circle
style="fill:#9a191d;fill-opacity:1;stroke:none;stroke-width:6.9172821;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path835"
r="50" />
<circle
style="fill:#ffffff;fill-opacity:1;stroke:#7e3b3d;stroke-width:1.62162137;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="61.500004"
id="path831"
r="9.189189" />
</g>
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<circle
cx="62.5"
cy="62.5"
fill="none"
r="59.984"
stroke="#a31a1f"
stroke-width="5.0319" />
<circle
cx="62.5"
cy="-62.5"
fill="url(#linearGradient934)"
r="50"
transform="rotate(90)" />
<circle
cx="62.5"
cy="61.5"
fill="#fff"
r="9.1892"
stroke="#7e3b3d"
stroke-width="1.6216" />
<defs>
<linearGradient
gradientUnits="userSpaceOnUse"
id="linearGradient934"
x1="12.5"
x2="112.5"
y1="-62.5"
y2="-62.5">
<stop
offset="0"
stop-color="#721212" />
<stop
offset="1"
stop-color="#a21a1f" />
</linearGradient>
</defs>
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
id="svg2"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<defs id="defs6">
<linearGradient
id="linearGradient884"
osb:paint="solid">
<stop
style="stop-color:#969696;stop-opacity:1;"
id="stop882"
offset="0" />
</linearGradient>
</defs>
<g id="layer1">
<circle
style="fill:#484848;fill-opacity:1;stroke:none;stroke-width:6.9172821;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.5"
cy="62.5"
id="path835"
r="50" />
<ellipse
style="fill:none;fill-opacity:1;stroke:#555555;stroke-width:5.03194237;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
cx="62.502243"
cy="63.029659"
id="path12"
rx="59.986229"
ry="59.984074"
transform="matrix(0.99996409,-0.00847427,0,1,0,0)" />
</g>
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#414141;stroke-width:1.04023623;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
height="17.459764"
id="rect844"
width="17.459761"
x="53.020119"
y="54.020119" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<circle
cx="62.5"
cy="62.5"
fill="#2a2a2a"
r="50" />
<ellipse
cx="62.502"
cy="63.03"
fill="none"
rx="59.986"
ry="59.984"
stroke="#2a2a2a"
stroke-width="5.0319"
transform="matrix(.99996 -.0084743 0 1 0 0)" />
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
fill="#555"
height="15"
width="15"
x="54"
y="56" />
</svg>

After

Width:  |  Height:  |  Size: 1022 B

+59
View File
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="125"
version="1.1"
viewBox="0 0 125 125"
width="125"
xmlns="http://www.w3.org/2000/svg">
<circle
cx="62.5"
cy="-62.5"
fill="url(#linearGradient1053)"
r="50"
transform="rotate(90)" />
<defs>
<linearGradient
gradientTransform="translate(0,-125)"
gradientUnits="userSpaceOnUse"
id="linearGradient1053"
x1="12.5"
x2="112.5"
y1="62.5"
y2="62.5">
<stop
offset="0"
stop-color="#404040" />
<stop
offset="1"
stop-color="#555" />
</linearGradient>
</defs>
<ellipse
cx="62.502"
cy="63.03"
fill="none"
rx="59.986"
ry="59.984"
stroke="#555"
stroke-width="5.0319"
transform="matrix(.99996 -.0084743 0 1 0 0)" />
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
fill="#dbdbdb"
height="17.46"
stroke="#414141"
stroke-width="1.0402"
width="17.46"
x="53.02"
y="54.02" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
height="48"
id="svg2"
version="1.1"
viewBox="0 0 48 48"
width="48"
xmlns="http://www.w3.org/2000/svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="ic_track_paused.svg">
<defs id="defs6" />
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<rect
style="fill:#a31a1f;fill-opacity:1;stroke-width:1.01154423"
height="20"
id="rect12"
width="5"
x="15"
y="14" />
<rect
style="fill:#a31a1f;fill-opacity:1;stroke-width:1.01154423"
height="20"
id="rect12-3"
width="5"
x="27"
y="14" />
<sodipodi:namedview
bordercolor="#666666"
borderopacity="1"
gridtolerance="10"
guidetolerance="10"
id="namedview4"
objecttolerance="10"
pagecolor="#ffffff"
showgrid="true"
inkscape:current-layer="svg2"
inkscape:cx="21.262539"
inkscape:cy="30.645051"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-height="1152"
inkscape:window-maximized="0"
inkscape:window-width="2048"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:zoom="9.8333333">
<inkscape:grid
id="grid16"
type="xygrid" />
</sodipodi:namedview>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
height="48"
id="svg2"
version="1.1"
viewBox="0 0 48 48"
width="48"
xmlns="http://www.w3.org/2000/svg">
<defs id="defs6" />
<g
style="display:inline"
id="layer1">
<path
style="display:inline;fill:#a31a1f;fill-opacity:1;stroke-width:0.03607465"
d="m 22.92317,31.9906 c -0.06948,-0.0081 -0.277422,-0.03235 -0.46209,-0.05384 -2.147889,-0.249888 -4.249982,-1.419727 -5.59965,-3.116269 -0.961167,-1.208194 -1.554689,-2.572853 -1.795388,-4.128058 -0.08907,-0.575498 -0.08782,-1.835731 0.0024,-2.415668 0.223761,-1.438345 0.765805,-2.731082 1.632716,-3.893914 0.353204,-0.473771 1.18254,-1.30549 1.649602,-1.654344 0.947391,-0.707615 1.928391,-1.18098 3.033216,-1.463629 0.649731,-0.166221 1.126538,-0.233843 1.827948,-0.259242 1.94015,-0.07026 3.814111,0.517516 5.397189,1.692835 0.484944,0.360035 1.31879,1.186156 1.663737,1.648325 0.890877,1.193617 1.438199,2.499896 1.661736,3.966023 0.07887,0.517263 0.08854,1.773309 0.01737,2.253423 -0.23912,1.612873 -0.846098,3.039393 -1.810255,4.254461 -0.293648,0.370069 -0.946591,1.022292 -1.317069,1.315618 -1.186276,0.939234 -2.594384,1.549011 -4.122093,1.785059 -0.343493,0.05308 -1.524586,0.09902 -1.779366,0.06922 z"
id="path85" />
</g>
<metadata id="metadata8">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+38
View File
@@ -0,0 +1,38 @@
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:android)
platform :android do
desc "Runs all the tests"
lane :test do
gradle(task: "test")
end
desc "Submit a new Beta Build to Crashlytics Beta"
lane :beta do
gradle(task: "clean assembleRelease")
crashlytics
# sh "your_script.sh"
# You can also use other beta testing services here
end
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
upload_to_play_store
end
end
@@ -0,0 +1,9 @@
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
Erfasst deine Kilometer, ohne dass es jemand anderes tut.
Funktionen & Features:
* zeichnet Sport und andere Outdooraktivitäten auf
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
* zeichnet deine Herzfrequenz auf (unterstützt Bluetooth-LE-Sensoren)
* exportiert deine Daten als KML oder GPX
* funktioniert ganz ohne Internet
@@ -0,0 +1 @@
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
@@ -0,0 +1,14 @@
v3.1.0: OpenTracks
Changes:
- Added show track in map apps (tested with OsmAnd and Maps.ME)
- Added while tracking, show track statistics without unlocking the device
- Removed landscape mode for track detail view (was buggy)
- Removed search suggestions history
Bugfix:
- Request storage permission for import all/export all
Developer:
- Upgrade to Gradle 5.6
- Do not minify release builds
@@ -0,0 +1,16 @@
v3.2.0: OpenTracks
Changes:
- Removed Widget
- Introduced material design icons
- Removed snowboarding and skiing (need new icons)
Bugfix:
- Fixed search functionality
Developer:
- Fixed (some) test cases
- Removed unused parcelable functionality
- Major cleanup and reformatting
- Do not use broadcast for app to widget communication
- stats_content.xml does not include other files
@@ -0,0 +1,7 @@
v3.2.1: OpenTracks
Changes:
- Settings are now on a single page
Developer:
- Settings moved to Androidx compat (uses fragments)
@@ -0,0 +1,13 @@
v3.2.2: OpenTracks
Changes:
- Removed aggregated stats
- Migrated to Material Design
Bugfix:
- Fixed export of sensor data
- Fixed import of incomplete sensor data
Developer:
- Replaced actionbar with toolbar
- Migrated to androidx appcompat (incl. Material Design)
@@ -0,0 +1,14 @@
v3.2.3: OpenTracks
Changes:
- Sharing: use Android Sharesheet
- Sharing: share multiple tracks as individual files
- Use Android ShareSheet
- Heart rate sensor (Bluetooth LE): automatic reconnect
Bugfix:
- Heart rate sensor (Bluetooth LE): disconnect properly
- Heart rate wasn't always shown while recording
Developer:
- Enabled TravisCI instrumentation tests
@@ -0,0 +1,5 @@
v3.2.4: OpenTracks
Bugfix:
- Settings activity crashes
Removed possibility to change voice/split frequency from the context menu
@@ -0,0 +1,15 @@
v3.2.5: OpenTracks
Changes:
- Disable activity change
- Add permission for Android 10: recording location data
Bugfix:
- Fix reset settings
Developer:
- Preferences
- if Settings cannot be loaded reset all preferences
- moved access to preferences to PreferencesUtil
- moved default of preferences from code to xml
- use SummaryProvider
@@ -0,0 +1,8 @@
v3.2.6: OpenTracks
Changes:
- Heart rate monitor: scan for available devices
- Remove shortcut to Bluetooth settings
Developer:
- Preferences: moved default of preferences from code to xml
@@ -0,0 +1,10 @@
v3.2.7: OpenTracks
Changes:
- Notification show GPS accuracy
- Show heads-up notification if GPS accuracy drops beyond threshold
- Always show chart by time as well as by distance
- Added help (in Settings)
Bugfix:
- TrackDetailActivity: disabled non-working landscape mode (temporary)
@@ -0,0 +1,10 @@
v3.2.8: OpenTracks
Changes:
- Chart: zoom via gestures
Bugfixes:
- Export via KMZ did not export picture filenames correctly
- Sharing multiple tracks did not include KMZ files
- MarkerDetailActivity's UI was not shown
- Settings crashed if preference of heart rate was not initialized
@@ -0,0 +1,10 @@
v3.2.9: OpenTracks
Changes:
- Option to keep screen on while recording
- Markers: removed split markers completely
- Voice announcement now work without READ_PHONE_STATE permission
Bugfixes:
- Statistics: heart rate was not shown
- Statistics: time was briefly reset to 0s
@@ -0,0 +1,11 @@
v3.2.10: OpenTracks
Changes:
- ChartView is not configurable anymore
- After reboot: removed automatic restart of an unfinished recording
Bugfix:
- Speech announcements use MAY_DUCK (again)
- ChartView shows markers (again)
- Fixed landscape layout of TrackListActivity
- Request legacy file API behavior (necessary for Android 10)
@@ -0,0 +1,12 @@
v3.2.11: OpenTracks
Bug:
- Max speed is identical to avg speed
Bugfix:
- Crash on heart rate sensor selection
Developer:
- Replaced TabHost by TabsLayout
- Removed special first waypoint
- Removed duplicate translations
@@ -0,0 +1,13 @@
v3.3.0: OpenTracks
Breaking changes:
- Pictures are now stored in app-private storage (do not show up other picture apps)
NOTE: Pictures taking with previous version cannot be shown
- Localization for Spanish (Latin America) only works on Android 7.0 or later
Changes:
- Import/Export via Storage Access Framework (avoids READ/WRITE_EXTERNAL_STORAGE permission)
Developer:
- Cleaned internal data structures
- Use separate icons/app names for debug version
@@ -0,0 +1,12 @@
v3.3.1: OpenTracks
Changes:
- Android 10: on uninstall allow to keep app data
- Introduced Dashboard API: map icon can be used to open current track in external app
(requires installation of another app like OSMDashboard)
Bugfix:
- App label was broken (introduced in v3.3.0)
Developer:
- Documentation for Dashboard API can be found in README_DEVELOPER.md
@@ -0,0 +1,11 @@
v3.3.2: OpenTracks
Changes:
- Export: overwrites files now
Bugfix:
- Always remove recording notification
Developer:
- On API 26+ java.time is used for parsing and writing dates
- Refactoring of TrackRecordingService
@@ -0,0 +1,12 @@
v3.4.0: OpenTracks
Changes:
- Dashboard API: multiple tracks
- Updated help
Bugfix:
- Fixed crash on fragment re-creation
- When paused: prevent stopping app
Developer:
- Enable testing via CI (Github Actions and TravisCI)
@@ -0,0 +1,14 @@
v3.5.0: OpenTracks
Changes:
- Marker: select pictures from gallery
- Marker: share picture
- Use track name as filename when sharing
- Removed elevation min/max (was imprecise)
Bugfix:
- Charts use preferred units and rate
Developer:
- Database: remove unused columns
- Database: add indices
@@ -0,0 +1,10 @@
v3.6.0: OpenTracks
Changes:
- Stopped tracks can now be resumed
Bugfix:
- Import of KMZ did not process the photo URL correctly
Developer:
- Simplified data processing
@@ -0,0 +1,4 @@
v3.6.1: OpenTracks
Bugfix:
- KMZ import: store correct picture path
@@ -0,0 +1,4 @@
v3.6.2: OpenTracks
Bugfix:
- Charts were not shown correctly (introduced in v3.6.0)
@@ -0,0 +1,13 @@
v3.7.0: OpenTracks
Changes:
- Report speed/pace by activity type
- Added workaround for GPS week rollover
- (Experimental) Support for Bluetooth LE cycling sensors (cadence and speed)
- KMZ/KML import enhanced
Bugfix:
- KMZ import: only import used images
Developer:
- Refactored TrackRecordingService
@@ -0,0 +1,10 @@
(tbd): OpenTracks
Changes:
-
Bugfix:
-
Developer:
-
@@ -3,6 +3,6 @@ OpenTracks is a sport tracking application that completely respects your privacy
Features:
* Sport tracking: track your sport and outdoor activities
* Photos and markers: mark interesting locations while tracking
* Heart rate: supports bluetooth LE sensors
* Export data: Export tracks either as KML or GPX
* Heart rate: supports Bluetooth LE sensors
* Export data: export tracks either as KML or GPX
* No Internet access: Internet is not used
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

@@ -0,0 +1 @@
OpenTracks
@@ -0,0 +1,8 @@
OpenTracks es un tracker deportivo que respeta tu privacidad.
Funciones:
* Tracker deportivo: haz un seguimiento de tus actividades al aire libre
* Marca lugares de interés, incluso agrega fotos a los marcadores
* Pulsómetro (compatible con dispositivos Bluetooth LE)
* Exporta tus rutas como KML o GPX
* Completamente offline, no necesitas internet
@@ -0,0 +1 @@
OpenTracks es un tracker deportivo que respeta tu privacidad.
@@ -0,0 +1 @@
OpenTracks
@@ -0,0 +1,9 @@
OpenTracks est une application de suivi d'activités sportives qui respecte totalement votre vie privée.
Fonctionnalités :
* Suivi d'activité : enregistre vos activités sportives et de plein air
* Photos et repères : ajout de photos et de repères pendant l'enregistrement de l'activité
* Rythme cardiaque : support des capteurs Bluetooth LE
* Export : export des traces au format KML ou GPX
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
@@ -0,0 +1 @@
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
Binary file not shown.
+2 -2
View File
@@ -1,6 +1,6 @@
#Mon Aug 26 18:59:08 CEST 2019
#Sun Apr 12 18:41:12 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
Vendored
+19 -3
View File
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m"'
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
Vendored
+17 -1
View File
@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -1,12 +0,0 @@
(tbd): OpenTracks
Changes:
- Added show track in map apps (tested with OsmAnd and Maps.ME)
- Removed landscape mode for track detail view (was buggy)
- Removed search suggestions history
Bugfix:
-
Developer:
-
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.
Binary file not shown.
@@ -1,101 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
import de.dennisguse.opentracks.services.TrackRecordingService;
/**
* Tests for the BootReceiver.
*
* @author Youtao Liu
*/
@RunWith(AndroidJUnit4.class)
public class BootReceiverTest {
private static final String SERVICE_NAME = "de.dennisguse.opentracks.services.TrackRecordingService";
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
/**
* Tests the behavior when receive notification which is the phone boot.
*/
@Test
public void testOnReceive_startService() {
// Make sure no TrackRecordingService
Intent stopIntent = new Intent(context, TrackRecordingService.class);
context.stopService(stopIntent);
Assert.assertFalse(isServiceExisted(context));
BootReceiver bootReceiver = new BootReceiver();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_BOOT_COMPLETED);
bootReceiver.onReceive(context, intent);
// Check if the service is started
Assert.assertFalse(isServiceExisted(context));
}
/**
* Tests the behavior when receive notification which is not the phone boot.
*/
@Test
public void testOnReceive_noStartService() {
// Make sure no TrackRecordingService
Intent stopIntent = new Intent(context, TrackRecordingService.class);
context.stopService(stopIntent);
Assert.assertFalse(isServiceExisted(context));
BootReceiver bootReceiver = new BootReceiver();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_BUG_REPORT);
bootReceiver.onReceive(context, intent);
// Check if the service is not started
Assert.assertFalse(isServiceExisted(context));
}
/**
* Checks if a service is started in a context.
*
* @param context the context for checking a service
*/
private boolean isServiceExisted(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
for (int i = 0; i < serviceList.size(); i++) {
RunningServiceInfo serviceInfo = serviceList.get(i);
ComponentName componentName = serviceInfo.service;
if (componentName.getClassName().equals(SERVICE_NAME)) {
return true;
}
}
return false;
}
}
@@ -18,13 +18,10 @@ package de.dennisguse.opentracks;
import android.location.Location;
import de.dennisguse.opentracks.content.SensorDataSetLocation;
import de.dennisguse.opentracks.content.Track;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.content.data.TrackPoint;
/**
* Commons utilities for creating stubs of track, location. The class will be
* enriched if needs more similar stubs for test.
* Commons utilities for creating stubs of track, location.
*
* @author Youtao Liu
*/
@@ -38,52 +35,31 @@ public class TrackStubUtils {
private static final float INITIAL_ACCURACY = 5;
private static final float INITIAL_SPEED = 10;
private static final float INITIAL_BEARING = 3.0f;
// Used to change the value of latitude, longitude, and altitude.
private static final double DIFFERENCE = 0.01;
/**
* Gets a a {@link Track} stub with specified number of locations.
*
* @param numberOfLocations the number of locations for the track
* @return a track stub.
*/
public static Track createTrack(int numberOfLocations) {
Track track = new Track();
for (int i = 0; i < numberOfLocations; i++) {
track.addLocation(createSensorDataSetLocation(INITIAL_LATITUDE + i * DIFFERENCE, INITIAL_LONGITUDE
+ i * DIFFERENCE, INITIAL_ALTITUDE + i * DIFFERENCE));
}
return track;
}
/**
* Create a MyTracks location with default values.
*
* @return a track stub.
*/
public static SensorDataSetLocation createSensorDataSetLocation() {
return createSensorDataSetLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
public static TrackPoint createDefaultTrackPoint() {
return createDefaultTrackPoint(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
}
/**
* Creates a {@link SensorDataSetLocation} stub with specified values.
* Creates a {@link TrackPoint} stub with specified values.
*
* @return a SensorDataSetLocation stub.
*/
public static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude, double altitude) {
// Initial Location
Location loc = new Location(LOCATION_PROVIDER);
loc.setLatitude(latitude);
loc.setLongitude(longitude);
loc.setAltitude(altitude);
loc.setAccuracy(INITIAL_ACCURACY);
loc.setSpeed(INITIAL_SPEED);
loc.setTime(INITIAL_TIME);
loc.setBearing(INITIAL_BEARING);
SensorDataSet sd = new SensorDataSet(Float.NaN, Float.NaN);
private static TrackPoint createDefaultTrackPoint(double latitude, double longitude, double altitude) {
Location location = new Location(LOCATION_PROVIDER);
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAltitude(altitude);
location.setAccuracy(INITIAL_ACCURACY);
location.setSpeed(INITIAL_SPEED);
location.setTime(INITIAL_TIME);
location.setBearing(INITIAL_BEARING);
return new SensorDataSetLocation(loc, sd);
return new TrackPoint(location);
}
}
@@ -13,8 +13,9 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks;
package de.dennisguse.opentracks.chart;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -23,6 +24,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
/**
* Tests {@link ChartValueSeries}.
*
@@ -42,7 +45,17 @@ public class ChartValueSeriesTest {
R.string.description_elevation_metric,
R.string.description_elevation_imperial,
R.color.chart_elevation_fill,
R.color.chart_elevation_border);
R.color.chart_elevation_border) {
@Override
double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
return chartPoint.getElevation();
}
@Override
protected boolean drawIfChartPointHasNoData() {
return false;
}
};
}
@Test
@@ -61,8 +74,8 @@ public class ChartValueSeriesTest {
@Test
public void testSmallUpdates() {
series.update(0);
series.update(10);
series.update(new ChartPoint(0));
series.update(new ChartPoint(10));
series.updateDimension();
Assert.assertEquals(100, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue());
@@ -71,8 +84,8 @@ public class ChartValueSeriesTest {
@Test
public void testBigUpdates() {
series.update(0);
series.update(901);
series.update(new ChartPoint(0));
series.update(new ChartPoint(901));
series.updateDimension();
Assert.assertEquals(1000, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue());
@@ -81,8 +94,8 @@ public class ChartValueSeriesTest {
@Test
public void testNotZeroBasedUpdates() {
series.update(220);
series.update(250);
series.update(new ChartPoint(220));
series.update(new ChartPoint(250));
series.updateDimension();
Assert.assertEquals(100, series.getInterval());
Assert.assertEquals(200, series.getMinMarkerValue());
@@ -1,145 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.dennisguse.opentracks.content.CustomContentProvider.DatabaseHelper;
/**
* Tests {@link CustomContentProvider}.
*
* @author Youtao Liu
*/
public class CustomContentProviderTest {
private static final String DATABASE_NAME = "test.db";
private SQLiteDatabase db;
private CustomContentProvider customContentProvider;
private Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
context.deleteDatabase(DATABASE_NAME);
db = (new DatabaseHelper(context, DATABASE_NAME)).getWritableDatabase();
customContentProvider = new CustomContentProvider() {
};
}
/**
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
*/
@Test
public void testDatabaseHelper_OnCreate() {
Assert.assertTrue(hasTable(TracksColumns.TABLE_NAME));
Assert.assertTrue(hasTable(TrackPointsColumns.TABLE_NAME));
Assert.assertTrue(hasTable(WaypointsColumns.TABLE_NAME));
}
/**
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
*/
@Test
public void testOnCreate() {
Assert.assertTrue(customContentProvider.onCreate(context));
}
/**
* Tests {@link CustomContentProvider#getType(Uri)}.
*/
@Test
public void testGetType() {
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
}
/**
* Creates a table, containing one column.
*
* @param table the table name
*/
private void createTable(String table) {
db.execSQL("CREATE TABLE " + table + " (test INTEGER)");
}
/**
* Drops a table in database.
*
* @param table the table name
*/
private void dropTable(String table) {
db.execSQL("Drop TABLE " + table);
}
/**
* Returns true if the table exists.
*
* @param table the table name
*/
private boolean hasTable(String table) {
try {
db.rawQuery("select count(*) from " + table, null);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Returns true if the column in the table exists.
*
* @param table the table name
* @param column the column name
*/
private boolean hasColumn(String table, String column) {
try {
db.execSQL("SElECT count(*) from " + table + " order by " + column);
} catch (Exception e) {
if (e.getMessage().contains("no such column")) {
return false;
}
}
return true;
}
/**
* Sets up upgrade.
*
* @param oldVersion thd old database version
*/
private void setupUpgrade(int oldVersion) {
dropTable(TracksColumns.TABLE_NAME);
dropTable(TrackPointsColumns.TABLE_NAME);
dropTable(WaypointsColumns.TABLE_NAME);
createTable(TracksColumns.TABLE_NAME);
createTable(TrackPointsColumns.TABLE_NAME);
createTable(WaypointsColumns.TABLE_NAME);
DatabaseHelper databaseHelper = new DatabaseHelper(context);
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
}
}
@@ -1,907 +0,0 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
import de.dennisguse.opentracks.stats.TripStatistics;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* A unit test for {@link ContentProviderUtilsImpl}.
*
* @author Bartlomiej Niechwiej
* @author Youtao Liu
*/
@RunWith(MockitoJUnitRunner.class)
public class CustomContentProviderUtilsImplTest {
private static final String NAME_PREFIX = "test name";
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
private static final String TEST_DESC = "Test Desc!";
private static final String TEST_DESC_NEW = "Test Desc new!";
private Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils providerUtils;
private double INITIAL_LATITUDE = 37.0;
private double INITIAL_LONGITUDE = -57.0;
private double ALTITUDE_INTERVAL = 2.5;
@Mock
private Cursor cursorMock;
@Mock
private ContentResolver contentResolverMock;
@Before
public void setUp() {
providerUtils = ContentProviderUtils.Factory.get(context);
providerUtils.deleteAllTracks(context);
}
@Test
public void testLocationIterator_noPoints() {
testIterator(1, 0, 1, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
@Test
public void testLocationIterator_customFactory() {
final Location location = new Location("test_location");
final AtomicInteger counter = new AtomicInteger();
testIterator(1, 15, 4, false, new LocationFactory() {
@Override
public Location createLocation() {
counter.incrementAndGet();
return location;
}
});
// Make sure we were called exactly as many times as we had track points.
Assert.assertEquals(15, counter.get());
}
@Test
public void testLocationIterator_nullFactory() {
try {
testIterator(1, 15, 4, false, null);
Assert.fail("Expecting IllegalArgumentException");
} catch (IllegalArgumentException e) {
// Expected.
}
}
@Test
public void testLocationIterator_noBatchAscending() {
testIterator(1, 50, 100, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
testIterator(2, 50, 50, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
@Test
public void testLocationIterator_noBatchDescending() {
testIterator(1, 50, 100, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
testIterator(2, 50, 50, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
@Test
public void testLocationIterator_batchAscending() {
testIterator(1, 50, 11, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
testIterator(2, 50, 25, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
@Test
public void testLocationIterator_batchDescending() {
testIterator(1, 50, 11, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
testIterator(2, 50, 25, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
@Test
public void testLocationIterator_largeTrack() {
testIterator(1, 20000, 2000, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
}
private List<Location> testIterator(long trackId, int numPoints, int batchSize, boolean descending, LocationFactory locationFactory) {
long lastPointId = initializeTrack(trackId, numPoints);
((ContentProviderUtilsImpl) providerUtils).setDefaultCursorBatchSize(batchSize);
List<Location> locations = new ArrayList<Location>(numPoints);
LocationIterator it = providerUtils.getTrackPointLocationIterator(trackId, -1L, descending, locationFactory);
try {
while (it.hasNext()) {
Location loc = it.next();
Assert.assertNotNull(loc);
locations.add(loc);
// Make sure the IDs are returned in the right order.
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
: lastPointId - numPoints + locations.size(), it.getLocationId());
}
Assert.assertEquals(numPoints, locations.size());
} finally {
it.close();
}
return locations;
}
private long initializeTrack(long id, int numPoints) {
Track track = new Track();
track.setId(id);
track.setName("Test: " + id);
track.setNumberOfPoints(numPoints);
providerUtils.insertTrack(track);
track = providerUtils.getTrack(id);
Assert.assertNotNull(track);
Location[] locations = new Location[numPoints];
for (int i = 0; i < numPoints; ++i) {
Location loc = new Location("test");
loc.setLatitude(37.0 + (double) i / 10000.0);
loc.setLongitude(57.0 - (double) i / 10000.0);
loc.setAccuracy((float) i / 100.0f);
loc.setAltitude(i * 2.5);
locations[i] = loc;
}
providerUtils.bulkInsertTrackPoint(locations, numPoints, id);
// Load all inserted locations.
long lastPointId = -1;
int counter = 0;
LocationIterator it = providerUtils.getTrackPointLocationIterator(id, -1L, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
try {
while (it.hasNext()) {
it.next();
lastPointId = it.getLocationId();
counter++;
}
} finally {
it.close();
}
Assert.assertTrue(numPoints == 0 || lastPointId > 0);
Assert.assertEquals(numPoints, track.getNumberOfPoints());
Assert.assertEquals(numPoints, counter);
return lastPointId;
}
/**
* Tests the method {@link ContentProviderUtilsImpl#createTrack(Cursor)}.
*/
@Test
public void testCreateTrack() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long trackId = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
// Name
String name = NAME_PREFIX + trackId;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
Track track = providerUtils.createTrack(cursorMock);
Assert.assertEquals(trackId, track.getId());
Assert.assertEquals(name, track.getName());
verify(cursorMock);
}
/**
* Tests the method {@link ContentProviderUtilsImpl#deleteAllTracks(Context)}
*/
@Test
public void testDeleteAllTracks() {
// Insert track, points and waypoint at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Waypoint waypoint = new Waypoint();
providerUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(1, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(10, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Delete all.
providerUtils.deleteAllTracks(context);
// Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#deleteTrack(Context, long)}.
*/
@Test
public void testDeleteTrack() {
// Insert three tracks, points of two tracks and way point of one track.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
insertTrackWithLocations(getTrack(trackId + 1, 10));
insertTrackWithLocations(getTrack(trackId + 2, 10));
Waypoint waypoint = new Waypoint();
waypoint.setTrackId(trackId);
providerUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null,
TracksColumns._ID);
Assert.assertEquals(3, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Delete one track.
providerUtils.deleteTrack(context, trackId);
// Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null,
TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
null, WaypointsColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getAllTracks()}
*/
@Test
public void testGetAllTracks() {
int initialTrackNumber = providerUtils.getAllTracks().size();
long trackId = System.currentTimeMillis();
providerUtils.insertTrack(getTrack(trackId, 0));
List<Track> allTracks = providerUtils.getAllTracks();
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getLastTrack()}
*/
@Test
public void testGetLastTrack() {
long trackId = System.currentTimeMillis();
providerUtils.insertTrack(getTrack(trackId, 0));
Assert.assertEquals(trackId, providerUtils.getLastTrack().getId());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getTrack(long)}
*/
@Test
public void testGetTrack() {
long trackId = System.currentTimeMillis();
providerUtils.insertTrack(getTrack(trackId, 0));
Assert.assertNotNull(providerUtils.getTrack(trackId));
}
/**
* Tests the method {@link ContentProviderUtilsImpl#updateTrack(Track)}
*/
@Test
public void testUpdateTrack() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 0);
String nameOld = "name1";
String nameNew = "name2";
track.setName(nameOld);
providerUtils.insertTrack(track);
Assert.assertEquals(nameOld, providerUtils.getTrack(trackId).getName());
track.setName(nameNew);
providerUtils.updateTrack(track);
Assert.assertEquals(nameNew, providerUtils.getTrack(trackId).getName());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#createContentValues(Waypoint)}.
*/
@Test
public void testCreateContentValues_waypoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
// Bottom
long startTime = 1000L;
// AverageSpeed
double minGrade = -20.11;
TripStatistics statistics = new TripStatistics();
statistics.setStartTime(startTime);
statistics.setStopTime(2500L);
statistics.setTotalTime(1500L);
statistics.setMovingTime(700L);
statistics.setTotalDistance(750.0);
statistics.setTotalElevationGain(50.0);
statistics.setMaxSpeed(60.0);
statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0);
statistics.setMaxGrade(15.0);
statistics.setMinGrade(minGrade);
statistics.setBounds(-10000, 20000, 30000, -40000);
// Insert at first.
Waypoint waypoint = new Waypoint();
waypoint.setDescription(TEST_DESC);
waypoint.setType(WaypointType.STATISTICS);
waypoint.setTripStatistics(statistics);
Location loc = new Location("test");
loc.setLatitude(22);
loc.setLongitude(22);
loc.setAccuracy((float) 1 / 100.0f);
loc.setAltitude(2.5);
waypoint.setLocation(loc);
providerUtils.insertWaypoint(waypoint);
ContentProviderUtilsImpl contentProviderUtils = new ContentProviderUtilsImpl(contentResolverMock);
long waypointId = System.currentTimeMillis();
waypoint.setId(waypointId);
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
Assert.assertEquals(22 * 1000000, contentValues.get(WaypointsColumns.LONGITUDE));
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
Assert.assertEquals(startTime, contentValues.get(WaypointsColumns.STARTTIME));
Assert.assertEquals(minGrade, contentValues.get(WaypointsColumns.MINGRADE));
}
/**
* Tests the method {@link ContentProviderUtilsImpl#createWaypoint(Cursor)}.
*/
@Test
public void testCreateWaypoint() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// trackIdIndex
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long id = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(id);
// Name
String name = NAME_PREFIX + id;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
// trackIdIndex
long trackId = 11L;
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
Waypoint waypoint = providerUtils.createWaypoint(cursorMock);
Assert.assertEquals(id, waypoint.getId());
Assert.assertEquals(name, waypoint.getName());
Assert.assertEquals(trackId, waypoint.getTrackId());
verify(cursorMock);
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
* when there is only one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_onlyOneWayPoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
// Insert at first.
Waypoint waypoint1 = new Waypoint();
waypoint1.setDescription(TEST_DESC);
waypoint1.setTrackId(trackId);
waypoint1.setType(WaypointType.STATISTICS);
providerUtils.insertWaypoint(waypoint1);
// Delete
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
@Override
public String generateWaypointDescription(TripStatistics tripStatistics) {
return CustomContentProviderUtilsImplTest.MOCK_DESC;
}
@Override
public String generateTrackDescription(Track aTrack, boolean html) {
return null;
}
};
providerUtils.deleteWaypoint(context, 1, descriptionGenerator);
Assert.assertNull(providerUtils.getWaypoint(1));
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
* when there is more than one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_hasNextWayPoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
TripStatistics statistics = new TripStatistics();
statistics.setStartTime(1000L);
statistics.setStopTime(2500L);
statistics.setTotalTime(1500L);
statistics.setMovingTime(700L);
statistics.setTotalDistance(750.0);
statistics.setTotalElevationGain(50.0);
statistics.setMaxSpeed(60.0);
statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0);
statistics.setMaxGrade(15.0);
statistics.setMinGrade(-25.0);
statistics.setBounds(-10000, 20000, 30000, -40000);
// Insert at first.
Waypoint waypoint1 = new Waypoint();
waypoint1.setDescription(Long.toString(trackId));
waypoint1.setTrackId(trackId);
waypoint1.setType(WaypointType.STATISTICS);
waypoint1.setTripStatistics(statistics);
providerUtils.insertWaypoint(waypoint1);
Waypoint waypoint2 = new Waypoint();
waypoint2.setDescription(Long.toString(trackId));
waypoint2.setTrackId(trackId);
waypoint2.setType(WaypointType.STATISTICS);
waypoint2.setTripStatistics(statistics);
providerUtils.insertWaypoint(waypoint2);
// Delete
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
@Override
public String generateWaypointDescription(TripStatistics tripStatistics) {
return CustomContentProviderUtilsImplTest.MOCK_DESC;
}
@Override
public String generateTrackDescription(Track aTrack, boolean html) {
return null;
}
};
providerUtils.deleteWaypoint(context, 1, descriptionGenerator);
Assert.assertNull(providerUtils.getWaypoint(1));
Assert.assertEquals(CustomContentProviderUtilsImplTest.MOCK_DESC, providerUtils.getWaypoint(2)
.getDescription());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getFirstWaypointId(long)}.
*/
@Test
public void testGetFirstWaypointId() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
Waypoint waypoint1 = new Waypoint();
waypoint1.setTrackId(trackId);
Waypoint waypoint2 = new Waypoint();
waypoint2.setTrackId(trackId);
providerUtils.insertWaypoint(waypoint1);
providerUtils.insertWaypoint(waypoint2);
Assert.assertEquals(-1L, providerUtils.getFirstWaypointId(-1));
Assert.assertEquals(1L, providerUtils.getFirstWaypointId(trackId));
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getNextWaypointNumber(long, WaypointType)}.
*/
@Test
public void testGetNextWaypointNumber() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
Waypoint waypoint1 = new Waypoint();
waypoint1.setType(WaypointType.STATISTICS);
waypoint1.setTrackId(trackId);
Waypoint waypoint2 = new Waypoint();
waypoint2.setType(WaypointType.WAYPOINT);
waypoint2.setTrackId(trackId);
Waypoint waypoint3 = new Waypoint();
waypoint3.setType(WaypointType.STATISTICS);
waypoint3.setTrackId(trackId);
Waypoint waypoint4 = new Waypoint();
waypoint4.setType(WaypointType.WAYPOINT);
waypoint4.setTrackId(trackId);
providerUtils.insertWaypoint(waypoint1);
providerUtils.insertWaypoint(waypoint2);
providerUtils.insertWaypoint(waypoint3);
providerUtils.insertWaypoint(waypoint4);
Assert.assertEquals(2, providerUtils.getNextWaypointNumber(trackId, WaypointType.STATISTICS));
Assert.assertEquals(3, providerUtils.getNextWaypointNumber(trackId, WaypointType.WAYPOINT));
}
/**
* Tests the method
* {@link ContentProviderUtils#getLastWaypoint(long, WaypointType)}.
*/
@Test
public void testGetLastStatisticsWaypoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
Waypoint waypoint1 = new Waypoint();
waypoint1.setTrackId(trackId);
waypoint1.setType(WaypointType.STATISTICS);
waypoint1.setDescription("Desc1");
Waypoint waypoint2 = new Waypoint();
waypoint2.setTrackId(trackId);
waypoint2.setType(WaypointType.STATISTICS);
waypoint2.setDescription("Desc2");
Waypoint waypoint3 = new Waypoint();
waypoint3.setTrackId(trackId);
waypoint3.setType(WaypointType.WAYPOINT);
waypoint3.setDescription("Desc3");
providerUtils.insertWaypoint(waypoint1);
providerUtils.insertWaypoint(waypoint2);
providerUtils.insertWaypoint(waypoint3);
Assert.assertEquals("Desc2", providerUtils.getLastWaypoint(trackId, WaypointType.STATISTICS).getDescription());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#insertWaypoint(Waypoint)} and
* {@link ContentProviderUtilsImpl#getWaypoint(long)}.
*/
@Test
public void testInsertAndGetWaypoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
Waypoint waypoint = new Waypoint();
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
providerUtils.insertWaypoint(waypoint);
Assert.assertEquals(TEST_DESC, providerUtils.getWaypoint(1).getDescription());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#updateWaypoint(Waypoint)}.
*/
@Test
public void testUpdateWaypoint() {
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
providerUtils.insertTrack(track);
// Insert at first.
Waypoint waypoint = new Waypoint();
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
providerUtils.insertWaypoint(waypoint);
// Update
waypoint = providerUtils.getWaypoint(1);
waypoint.setDescription(TEST_DESC_NEW);
providerUtils.updateWaypoint(waypoint);
Assert.assertEquals(TEST_DESC_NEW, providerUtils.getWaypoint(1).getDescription());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#bulkInsertTrackPoint(Location[], int, long)}.
*/
@Test
public void testBulkInsertTrackPoint() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), -1, trackId);
Assert.assertEquals(20, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), 8, trackId);
Assert.assertEquals(28, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#createTrackPoint(Cursor)}.
*/
@Test
public void testCreateTrackPoint() {
// Set index.
int index = 1;
// Id
when(cursorMock.getColumnIndex(TrackPointsColumns._ID)).thenReturn(index++);
// Longitude
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).thenReturn(index++);
// Latitude
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).thenReturn(index++);
// Time
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME))
.thenReturn(index++);
// Speed
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).thenReturn(index++);
// Sensor
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(index++);
// Set return value of isNull().
index = 2;
// Longitude
when(cursorMock.isNull(index++)).thenReturn(false);
// Latitude
when(cursorMock.isNull(index++)).thenReturn(false);
// Time
when(cursorMock.isNull(index++)).thenReturn(false);
// Speed
when(cursorMock.isNull(index++)).thenReturn(false);
// Sensor
when(cursorMock.isNull(index++)).thenReturn(false);
// Set return value of isNull().
index = 2;
// Longitude
int longitude = 11;
when(cursorMock.getInt(index++)).thenReturn(longitude * 1000000);
// Latitude.
int latitude = 22;
when(cursorMock.getInt(index++)).thenReturn(latitude * 1000000);
// Time
long time = System.currentTimeMillis();
when(cursorMock.getLong(index++)).thenReturn(time);
// Speed
float speed = 2.2f;
when(cursorMock.getFloat(index++)).thenReturn(speed);
// Sensor
byte[] sensor = "Sensor state".getBytes();
when(cursorMock.getBlob(index++)).thenReturn(sensor);
Location location = providerUtils.createTrackPoint(cursorMock);
Assert.assertEquals((double) longitude, location.getLongitude(), 0.01);
Assert.assertEquals((double) latitude, location.getLatitude(), 0.01);
Assert.assertEquals(time, location.getTime(), 0.01);
Assert.assertEquals(speed, location.getSpeed(), 0.01);
verify(cursorMock);
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#insertTrackPoint(Location, long)}.
*/
@Test
public void testInsertTrackPoint() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
providerUtils.insertTrackPoint(createLocation(22), trackId);
Assert.assertEquals(11, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getFirstTrackPointId(long)}.
*/
@Test
public void testGetFirstTrackPointId() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Assert.assertEquals(1, providerUtils.getFirstTrackPointId(trackId));
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getLastTrackPointId(long)}.
*/
@Test
public void testGetLastTrackPointId() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Assert.assertEquals(10, providerUtils.getLastTrackPointId(trackId));
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getLastValidTrackPoint(long)}.
*/
@Test
public void testGetLastValidTrackPoint() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Location lastLocation = providerUtils.getLastValidTrackPoint(trackId);
checkLocation(9, lastLocation);
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)}
* in descending.
*/
@Test
public void testGetTrackPointCursor_desc() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Cursor cursor = providerUtils.getTrackPointCursor(trackId, 2L, 5, true);
Assert.assertEquals(2, cursor.getCount());
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)}
* in ascending.
*/
@Test
public void testGetTrackPointCursor_asc() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
Cursor cursor = providerUtils.getTrackPointCursor(trackId, 2L, 5, false);
Assert.assertEquals(5, cursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in descending.
*/
@Test
public void testGetTrackPointLocationIterator_desc() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
long startTrackPointId = 2L;
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
for (int i = 1; i >= 0; i--) {
Assert.assertTrue(locationIterator.hasNext());
Location location = locationIterator.next();
Assert.assertEquals(2 + (i - 1), locationIterator.getLocationId());
checkLocation(i, location);
}
Assert.assertFalse(locationIterator.hasNext());
}
/**
* Tests the method
* {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)}
* in ascending.
*/
@Test
public void testGetTrackPointLocationIterator_asc() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = getTrack(trackId, 10);
insertTrackWithLocations(track);
long startTrackPointId = 2L;
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
for (int i = 1; i < 10; i++) {
Assert.assertTrue(locationIterator.hasNext());
Location location = locationIterator.next();
Assert.assertEquals(2 + (i - 1), locationIterator.getLocationId());
checkLocation(i, location);
}
Assert.assertFalse(locationIterator.hasNext());
}
/**
* Simulates a track which is used for testing.
*
* @param id the id of the track
* @param numPoints the location number in the track
* @return the simulated track
*/
private Track getTrack(long id, int numPoints) {
Track track = new Track();
track.setId(id);
track.setName("Test: " + id);
track.setNumberOfPoints(numPoints);
for (int i = 0; i < numPoints; i++) {
track.addLocation(createLocation(i));
}
return track;
}
/**
* Creates a location.
*
* @param i the index to set the value of location.
* @return created location
*/
private Location createLocation(int i) {
Location loc = new Location("test");
loc.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
loc.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
loc.setAccuracy((float) i / 100.0f);
loc.setAltitude(i * ALTITUDE_INTERVAL);
return loc;
}
/**
* Checks the value of a location.
*
* @param i the index of this location which created in the method
* {@link CustomContentProviderUtilsImplTest#getTrack(long, int)}
* @param location the location to be checked
*/
private void checkLocation(int i, Location location) {
Assert.assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
Assert.assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
Assert.assertEquals(i * ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
}
/**
* Inserts a track with locations into the database.
*
* @param track track to be inserted
*/
private void insertTrackWithLocations(Track track) {
providerUtils.insertTrack(track);
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
}
}
@@ -27,34 +27,35 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.stats.TripStatistics;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.StringUtils;
/**
* Tests for {@link DescriptionGeneratorImpl}.
* Tests for {@link DescriptionGenerator}.
*
* @author Jimmy Shih
*/
@RunWith(AndroidJUnit4.class)
public class DescriptionGeneratorImplTest {
public class DescriptionGeneratorTest {
private static final long START_TIME = 1288721514000L;
private DescriptionGeneratorImpl descriptionGenerator;
private DescriptionGenerator descriptionGenerator;
private Context context = ApplicationProvider.getApplicationContext();
private final Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
descriptionGenerator = new DescriptionGeneratorImpl(ApplicationProvider.getApplicationContext());
descriptionGenerator = new DescriptionGenerator(ApplicationProvider.getApplicationContext());
}
/**
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track, java.util.Vector, java.util.Vector, boolean)}.
* Tests {@link DescriptionGenerator#generateTrackDescription(Track, boolean)}.
*/
@Test
public void testGenerateTrackDescription() {
Track track = new Track();
TripStatistics stats = new TripStatistics();
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
@@ -62,13 +63,11 @@ public class DescriptionGeneratorImplTest {
stats.setMaxElevation(550);
stats.setMinElevation(-500);
stats.setTotalElevationGain(6000);
stats.setMaxGrade(0.42);
stats.setMinGrade(0.11);
stats.setStartTime(START_TIME);
track.setTripStatistics(stats);
stats.setStartTime_ms(START_TIME);
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
"<a href='https://github.com/dennisguse/opentracks'>OpenTracks</a><p>"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks</a><p>"
+ "Name: -<br>"
+ "Activity type: hiking<br>"
+ "Description: -<br>"
@@ -84,52 +83,48 @@ public class DescriptionGeneratorImplTest {
+ "Max elevation: 550 m (1804 ft)<br>"
+ "Min elevation: -500 m (-1640 ft)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Max grade: 42 %<br>"
+ "Min grade: 11 %<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
/**
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(TripStatistics)}.
*/
@Test
public void testGenerateWaypointDescription() {
Waypoint waypoint = new Waypoint();
TripStatistics stats = new TripStatistics();
public void testGenerateTrackDescriptionWithoutMaxMinElevation() {
Track track = new Track();
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setMaxSpeed(100);
stats.setMaxElevation(550);
stats.setMinElevation(-500);
stats.setMaxElevation(Double.POSITIVE_INFINITY);
stats.setMinElevation(Double.NEGATIVE_INFINITY);
stats.setTotalElevationGain(6000);
stats.setMaxGrade(0.42);
stats.setMinGrade(0.11);
stats.setStartTime(START_TIME);
waypoint.setTripStatistics(stats);
String expected = "Total distance: 20.00 km (12.4 mi)\n"
+ "Total time: 10:00\n"
+ "Moving time: 05:00\n"
+ "Average speed: 120.00 km/h (74.6 mi/h)\n"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
+ "Max speed: 360.00 km/h (223.7 mi/h)\n"
+ "Average pace: 0:30 min/km (0:48 min/mi)\n"
+ "Average moving pace: 0:15 min/km (0:24 min/mi)\n"
+ "Fastest pace: 0:10 min/km (0:16 min/mi)\n"
+ "Max elevation: 550 m (1804 ft)\n"
+ "Min elevation: -500 m (-1640 ft)\n"
+ "Elevation gain: 6000 m (19685 ft)\n"
+ "Max grade: 42 %\n"
+ "Min grade: 11 %\n"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "\n";
Assert.assertEquals(expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
stats.setStartTime_ms(START_TIME);
track.setTrackStatistics(stats);
track.setCategory("hiking");
String expected = //"Created by"
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks</a><p>"
+ "Name: -<br>"
+ "Activity type: hiking<br>"
+ "Description: -<br>"
+ "Total distance: 20.00 km (12.4 mi)<br>"
+ "Total time: 10:00<br>"
+ "Moving time: 05:00<br>"
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
+ "Max speed: 360.00 km/h (223.7 mi/h)<br>"
+ "Average pace: 0:30 min/km (0:48 min/mi)<br>"
+ "Average moving pace: 0:15 min/km (0:24 min/mi)<br>"
+ "Fastest pace: 0:10 min/km (0:16 min/mi)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
}
/**
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder,
* int, String)}.
* Tests {@link DescriptionGenerator#writeDistance(double, StringBuilder, int, String)}.
*/
@Test
public void testWriteDistance() {
@@ -139,8 +134,7 @@ public class DescriptionGeneratorImplTest {
}
/**
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int,
* String)}.
* Tests {@link DescriptionGenerator#writeTime(long, StringBuilder, int, String)}.
*/
@Test
public void testWriteTime() {
@@ -150,8 +144,7 @@ public class DescriptionGeneratorImplTest {
}
/**
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder,
* int, String)}.
* Tests {@link DescriptionGenerator#writeSpeed(double, StringBuilder, int, String)}.
*/
@Test
public void testWriteSpeed() {
@@ -161,8 +154,7 @@ public class DescriptionGeneratorImplTest {
}
/**
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder,
* int, String)}.
* Tests {@link DescriptionGenerator#writeElevation(double, StringBuilder, int, String)}.
*/
@Test
public void testWriteElevation() {
@@ -172,8 +164,7 @@ public class DescriptionGeneratorImplTest {
}
/**
* Tests {@link DescriptionGeneratorImpl#writePace(double, StringBuilder, int,
* String)}.
* Tests {@link DescriptionGenerator#writePace(double, StringBuilder, int, String)}.
*/
@Test
public void testWritePace() {
@@ -181,37 +172,4 @@ public class DescriptionGeneratorImplTest {
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
Assert.assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)}.
*/
@Test
public void testWriteGrade() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(.042, builder, R.string.description_max_grade, "<br>");
Assert.assertEquals("Max grade: 4 %<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)} with a NaN.
*/
@Test
public void testWriteGrade_nan() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(Double.NaN, builder, R.string.description_max_grade, "<br>");
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)} with an infinite number.
*/
@Test
public void testWriteGrade_infinite() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
}
}
@@ -36,13 +36,15 @@ import java.util.List;
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
import de.dennisguse.opentracks.stats.TripStatistics;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
* Tests for {@link SearchEngine}.
* These are not meant to be quality tests, but instead feature-by-feature tests
* (in other words, they don't test the mixing of different score boostings, just
* each boosting separately)
* These are not meant to be quality tests, but instead feature-by-feature tests.
* In other words, they don't test the mixing of different score boostings, just each boosting separately.
*
* @author Rodrigo Damazio
*/
@@ -50,71 +52,61 @@ import de.dennisguse.opentracks.stats.TripStatistics;
public class SearchEngineTest {
private static final Location HERE = new Location("gps");
private static final long NOW = 1234567890000L; // After OLDEST_ALLOWED_TIMESTAMP
private static final long NOW = SearchEngine.OLDEST_ALLOWED_TIMESTAMP + 1000; // After OLDEST_ALLOWED_TIMESTAMP
private ContentProviderUtils providerUtils;
private SearchEngine engine;
private Context context = ApplicationProvider.getApplicationContext();
private final Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
providerUtils = ContentProviderUtils.Factory.get(context);
providerUtils = new ContentProviderUtils(context);
engine = new SearchEngine(providerUtils);
}
@After
public void tearDown() throws Exception {
public void tearDown() {
providerUtils.deleteAllTracks(context);
}
private long insertTrack(String title, String description, String category, double distance, long hoursAgo) {
private long insertTrack(String title, String description, String category, long hoursAgo) {
Track track = new Track();
track.setName(title);
track.setDescription(description);
track.setCategory(category);
TripStatistics stats = track.getTripStatistics();
TrackStatistics stats = track.getTrackStatistics();
if (hoursAgo > 0) {
// Started twice hoursAgo, so the average time is hoursAgo.
stats.setStartTime(NOW - hoursAgo * 1000L * 60L * 60L * 2);
stats.setStopTime(NOW);
stats.setStartTime_ms(NOW - hoursAgo * 1000L * 60L * 60L * 2);
stats.setStopTime_ms(NOW);
}
int latitude = (int) ((HERE.getLatitude() + distance) * 1E6);
int longitude = (int) ((HERE.getLongitude() + distance) * 1E6);
stats.setBounds(latitude, longitude, latitude, longitude);
Uri uri = providerUtils.insertTrack(track);
return ContentUris.parseId(uri);
}
private long insertTrack(String title, String description, String category) {
return insertTrack(title, description, category, 0, -1);
}
private long insertTrack(String title, double distance) {
return insertTrack(title, "", "", distance, -1);
return insertTrack(title, description, category, -1);
}
private long insertTrack(String title, long hoursAgo) {
return insertTrack(title, "", "", 0.0, hoursAgo);
return insertTrack(title, "", "", hoursAgo);
}
private long insertWaypoint(String title, String description, String category, double distance, long hoursAgo, long trackId) {
Waypoint waypoint = new Waypoint();
waypoint.setName(title);
waypoint.setDescription(description);
waypoint.setCategory(category);
waypoint.setTrackId(trackId);
Location location = new Location(HERE);
location.setLatitude(location.getLatitude() + distance);
location.setLongitude(location.getLongitude() + distance);
if (hoursAgo >= 0) {
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
}
waypoint.setLocation(location);
Waypoint waypoint = new Waypoint(location);
waypoint.setName(title);
waypoint.setDescription(description);
waypoint.setCategory(category);
waypoint.setTrackId(trackId);
Uri uri = providerUtils.insertWaypoint(waypoint);
return ContentUris.parseId(uri);
@@ -155,7 +147,7 @@ public class SearchEngineTest {
long allMatchId = insertTrack("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
assertTrackResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
@@ -180,7 +172,7 @@ public class SearchEngineTest {
long allMatchId = insertWaypoint("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
assertWaypointResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
@@ -201,7 +193,7 @@ public class SearchEngineTest {
long trackId = insertTrack("aa", "bb", "cc");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category.
Assert.assertEquals(results.toString(), 3, results.size());
@@ -210,20 +202,6 @@ public class SearchEngineTest {
assertWaypointResult(descriptionWaypointId, results.get(2));
}
@Test
public void testSearchTrackDistance() {
// All results match text, but they're at difference distances from the user.
long farFarAwayId = insertTrack("aa", 0.3);
long nearId = insertTrack("ab", 0.1);
long farId = insertTrack("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Distance order.
assertTrackResults(results, nearId, farId, farFarAwayId);
}
@Test
public void testSearchWaypointDistance() {
// All results match text, but they're at difference distances from the user.
@@ -232,7 +210,7 @@ public class SearchEngineTest {
long farId = insertWaypoint("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Distance order.
assertWaypointResults(results, nearId, farId, farFarAwayId);
@@ -245,7 +223,7 @@ public class SearchEngineTest {
long oldId = insertTrack("ac", 2);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Reverse time order.
assertTrackResults(results, recentId, oldId, oldestId);
@@ -259,7 +237,7 @@ public class SearchEngineTest {
long oldId = insertWaypoint("ac", 1);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Reverse time order.
assertWaypointResults(results, recentId, oldId, oldestId);
@@ -272,7 +250,7 @@ public class SearchEngineTest {
long otherId = insertTrack("aa", 1);
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Current track should be demoted.
assertTrackResults(results, otherId, currentId);
@@ -285,7 +263,7 @@ public class SearchEngineTest {
long currentId = insertWaypoint("ab", 1, 123);
SearchQuery query = new SearchQuery("a", null, 123, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Waypoint in current track should be promoted.
assertWaypointResults(results, currentId, otherId);
@@ -1,761 +0,0 @@
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.location.Location;
import androidx.test.core.app.ApplicationProvider;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link TrackDataHub}.
*
* @author Rodrigo Damazio
*/
@RunWith(MockitoJUnitRunner.class)
public class TrackDataHubTest {
private static final long TRACK_ID = 42L;
private static final int TARGET_POINTS = 50;
private Context context = ApplicationProvider.getApplicationContext();
private SharedPreferences sharedPreferences;
@Mock
private ContentProviderUtils contentProviderUtils;
@Mock
private DataSource dataSource;
private TrackDataHub trackDataHub;
@Mock
private TrackDataListener trackDataListener1;
@Mock
private TrackDataListener trackDataListener2;
private ArgumentCaptor<OnSharedPreferenceChangeListener> preferenceChangeListenerCapture = ArgumentCaptor.forClass(OnSharedPreferenceChangeListener.class);
@Before
public void setUp() {
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
trackDataHub = new TrackDataHub(context, new TrackDataManager(), contentProviderUtils, TARGET_POINTS) {
@Override
protected DataSource newDataSource() {
return dataSource;
}
@Override
protected void runInHandlerThread(Runnable runnable) {
// Run everything in the same thread
runnable.run();
}
};
PreferencesUtils.setLong(context, R.string.recording_track_id_key, TRACK_ID);
}
@After
public void tearDown() {
reset(dataSource);
// Expect everything to be unregistered.
dataSource.unregisterContentObserver(isA(ContentObserver.class));
//TODO
// AndroidMock.expectLastCall().times(3);
dataSource.unregisterOnSharedPreferenceChangeListener(isA(OnSharedPreferenceChangeListener.class));
verify(dataSource);
trackDataHub.stop();
trackDataHub = null;
}
/**
* Tests registering for tracks table update.
*/
@Test
public void testTracksTableUpdate() {
// Register two listeners
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
Track track = new Track();
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), contentObserverCapture.capture());
trackDataListener1.onTrackUpdated(track);
trackDataListener2.onTrackUpdated(track);
replay();
trackDataHub.start();
trackDataHub.loadTrack(TRACK_ID);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.TRACKS_TABLE));
verifyAndReset();
// Causes tracks table update
ContentObserver contentObserver = contentObserverCapture.getValue();
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
trackDataListener1.onTrackUpdated(track);
trackDataListener2.onTrackUpdated(track);
replay();
contentObserver.onChange(false);
verifyAndReset();
// Unregister one listener
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
trackDataListener2.onTrackUpdated(track);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
contentObserver.onChange(false);
verifyAndReset();
// Unregister the second listener
dataSource.unregisterContentObserver(contentObserver);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener2);
contentObserver.onChange(false);
verifyAndReset();
}
//TODO
/**
* Tests registering for waypoints table update.
*/
// @Test
// public void testWaypointsTableUpdate() {
// Waypoint waypoint1 = new Waypoint();
// Waypoint waypoint2 = new Waypoint();
// Waypoint waypoint3 = new Waypoint();
// Waypoint waypoint4 = new Waypoint();
// Location location = new Location("gps");
// location.setLatitude(10.0);
// location.setLongitude(8.0);
// waypoint1.setLocation(location);
// waypoint2.setLocation(location);
// waypoint3.setLocation(location);
// waypoint4.setLocation(location);
//
// // Register two listeners
// ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
// when(contentProviderUtils.getWaypointCursor(
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
// .andStubAnswer(new FixedSizeCursorAnswer(2));
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
// .thenReturn(waypoint1)
// .thenReturn(waypoint2)
// .thenReturn(waypoint1)
// .thenReturn(waypoint2);
// dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_BASE_URI), contentObserverCapture.capture());
// trackDataListener1.clearWaypoints();
// trackDataListener2.clearWaypoints();
// trackDataListener1.onNewWaypoint(waypoint1);
// trackDataListener2.onNewWaypoint(waypoint1);
// trackDataListener1.onNewWaypoint(waypoint2);
// trackDataListener2.onNewWaypoint(waypoint2);
// trackDataListener1.onNewWaypointsDone();
// trackDataListener2.onNewWaypointsDone();
// replay();
//
// trackDataHub.start();
// trackDataHub.loadTrack(TRACK_ID);
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
// trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
// verifyAndReset();
//
// // Cause waypoints table update
// ContentObserver contentObserver = contentObserverCapture.getValue();
// when(contentProviderUtils.getWaypointCursor(
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
// .andStubAnswer(new FixedSizeCursorAnswer(3));
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
// .thenReturn(waypoint1)
// .thenReturn(waypoint2)
// .thenReturn(waypoint3);
// trackDataListener1.clearWaypoints();
// trackDataListener2.clearWaypoints();
// trackDataListener1.onNewWaypoint(waypoint1);
// trackDataListener2.onNewWaypoint(waypoint1);
// trackDataListener1.onNewWaypoint(waypoint2);
// trackDataListener2.onNewWaypoint(waypoint2);
// trackDataListener1.onNewWaypoint(waypoint3);
// trackDataListener2.onNewWaypoint(waypoint3);
// trackDataListener1.onNewWaypointsDone();
// trackDataListener2.onNewWaypointsDone();
// replay();
//
// contentObserver.onChange(false);
// verifyAndReset();
//
// // Unregister one listener
// when(contentProviderUtils.getWaypointCursor(
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
// .andStubAnswer(new FixedSizeCursorAnswer(4));
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
// .thenReturn(waypoint1)
// .thenReturn(waypoint2)
// .thenReturn(waypoint3)
// .thenReturn(waypoint4);
// trackDataListener2.clearWaypoints();
// trackDataListener2.onNewWaypoint(waypoint1);
// trackDataListener2.onNewWaypoint(waypoint2);
// trackDataListener2.onNewWaypoint(waypoint3);
// trackDataListener2.onNewWaypoint(waypoint4);
// trackDataListener2.onNewWaypointsDone();
// replay();
//
// trackDataHub.unregisterTrackDataListener(trackDataListener1);
// contentObserver.onChange(false);
// verifyAndReset();
//
// // Unregister the second listener
// dataSource.unregisterContentObserver(contentObserver);
// replay();
//
// trackDataHub.unregisterTrackDataListener(trackDataListener2);
// contentObserver.onChange(false);
// verifyAndReset();
// }
/**
* Tests track points table update.
*/
@Test
public void testTrackPointsTableUpdate() {
// Register one listener
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.loadTrack(TRACK_ID);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Register a second listener
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
trackDataListener2.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener2.onNewTrackPointsDone();
replay();
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Deliver more points - should go to both listeners without clearing
ContentObserver contentObserver = contentObserverCapture.getValue();
locationIterator = new FixedSizeLocationIterator(11, 10, 1);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(11L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(20L);
locationIterator.expectLocationsDelivered(trackDataListener1);
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener1.onNewTrackPointsDone();
trackDataListener2.onNewTrackPointsDone();
replay();
contentObserver.onChange(false);
verifyAndReset();
// Unregister one listener and change track
locationIterator = new FixedSizeLocationIterator(101, 10);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
.thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).thenReturn(110L);
trackDataListener2.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener2.onNewTrackPointsDone();
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
trackDataHub.loadTrack(TRACK_ID + 1);
verifyAndReset();
}
/**
* Tests track points table update with registering the same listener.
*/
@Test
public void testTrackPointsTableUpdate_reRegister() {
// Register one listener
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
when(contentProviderUtils.getTrackPointLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.loadTrack(TRACK_ID);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Unregister the listener
ContentObserver observer = contentObserverCapture.getValue();
dataSource.unregisterContentObserver(observer);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
verifyAndReset();
// Register again
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
}
/**
* Tests tracks point able change. Register a listener after a track change.
*/
@Test
public void testTrackPointsTableUpdate_reRegisterAfterTrackChange() {
// Register one listener
ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
when(contentProviderUtils.getTrackPointLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.loadTrack(TRACK_ID);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Unregister the listener
ContentObserver observer = observerCapture.getValue();
dataSource.unregisterContentObserver(observer);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
verifyAndReset();
// Register the listener after a new track
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
locationIterator = new FixedSizeLocationIterator(1, 10);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
.thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).thenReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.loadTrack(TRACK_ID + 1);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
}
//TODO
/**
* Tests track points table update with large track sampling.
*/
// @Test
// public void testTrackPointsTableUpdate_largeTrackSampling() {
// ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
// dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_BASE_URI), contentObserverCapture.capture());
//
// when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(200L);
// AndroidMock.expectLastCall().anyTimes();
// FixedSizeLocationIterator locationIterator1 = new FixedSizeLocationIterator(1, 200, 4, 25, 71, 120);
// when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator1);
// FixedSizeLocationIterator locationIterator2 = new FixedSizeLocationIterator(1, 200, 4, 25, 71, 120);
// when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator2);
//
// trackDataListener1.clearTrackPoints();
// locationIterator1.expectSampledLocationsDelivered(trackDataListener1, 4, false);
// trackDataListener1.onNewTrackPointsDone();
// trackDataListener2.clearTrackPoints();
// locationIterator2.expectSampledLocationsDelivered(trackDataListener2, 4, true);
// trackDataListener2.onNewTrackPointsDone();
// replay();
//
// trackDataHub.start();
// trackDataHub.loadTrack(TRACK_ID);
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
// trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
// verifyAndReset();
// }
/**
* Tests track points table update with resampling.
*/
@Test
public void testTrackPointsTableUpdate_resampling() {
ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
// Deliver 30 points (no sampling happens)
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 30, 5);
when(contentProviderUtils.getTrackPointLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(30L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.loadTrack(TRACK_ID);
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Now deliver 30 more (incrementally sampled)
ContentObserver observer = observerCapture.getValue();
locationIterator = new FixedSizeLocationIterator(31, 30);
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(31L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(60L);
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
trackDataListener1.onNewTrackPointsDone();
replay();
observer.onChange(false);
verifyAndReset();
// Now another 30 (triggers resampling)
locationIterator = new FixedSizeLocationIterator(1, 90);
when(contentProviderUtils.getTrackPointLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(90L);
trackDataListener1.clearTrackPoints();
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
trackDataListener1.onNewTrackPointsDone();
replay();
observer.onChange(false);
verifyAndReset();
}
/**
* Tests preferences change.
*/
@Test
public void testPreferencesChange() throws Exception {
// Register two listeners
PreferencesUtils.setString(context, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
dataSource.registerOnSharedPreferenceChangeListener(preferenceChangeListenerCapture.capture());
when(trackDataListener1.onMetricUnitsChanged(true)).thenReturn(false);
when(trackDataListener1.onReportSpeedChanged(true)).thenReturn(false);
when(trackDataListener1.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT)).thenReturn(false);
when(trackDataListener1.onRecordingDistanceIntervalChanged(PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).thenReturn(false);
when(trackDataListener2.onMetricUnitsChanged(true)).thenReturn(false);
when(trackDataListener2.onReportSpeedChanged(true)).thenReturn(false);
when(trackDataListener2.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT)).thenReturn(false);
when(trackDataListener2.onRecordingDistanceIntervalChanged(PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).thenReturn(false);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.PREFERENCE));
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.PREFERENCE));
verifyAndReset();
// Change report speed to false
when(trackDataListener1.onReportSpeedChanged(false)).thenReturn(false);
when(trackDataListener2.onReportSpeedChanged(false)).thenReturn(false);
replay();
PreferencesUtils.setString(context, R.string.stats_rate_key, context.getString(R.string.stats_rate_pace));
OnSharedPreferenceChangeListener listener = preferenceChangeListenerCapture.getValue();
listener.onSharedPreferenceChanged(sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_rate_key));
verifyAndReset();
// Change metric units to false
when(trackDataListener1.onMetricUnitsChanged(false)).thenReturn(false);
when(trackDataListener2.onMetricUnitsChanged(false)).thenReturn(false);
replay();
String imperialUnits = context.getString(R.string.stats_units_imperial);
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
listener.onSharedPreferenceChanged(sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_units_key));
verifyAndReset();
}
/**
* Replays mocks.
*/
private void replay() {
// AndroidMock.replay(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
}
/**
* Verifies and resets mocks.
*/
private void verifyAndReset() {
verify(contentProviderUtils);
verify(dataSource);
verify(trackDataListener1);
verify(trackDataListener2);
reset(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
}
//TODO
/**
* Tests the method {@link TrackDataHub#start()}.
*/
// @Test
// public void testRegisterTracksTableListener() {
// ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
// dataSource.registerContentObserver(eq(TracksColumns.CONTENT_BASE_URI), observerCapture.capture());
// Track track = TrackStubUtils.createTrack(1);
//
// when(contentProviderUtils.getTrack(capture(new Capture<Long>())).thenReturn(track);
// // Make the track id is unique.
// PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
// trackDataListener1.onTrackUpdated(track);
// replay();
// trackDataHub.start();
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
// verifyAndReset();
// }
//TODO
/**
* Tests the method {@link TrackDataHub#start()}.
*/
// @Test
// public void testRegisterWaypointsTableListener() {
// ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
// dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_BASE_URI), observerCapture.capture());
// when(contentProviderUtils.getWaypointCursor(
// capture(new Capture<Long>()), capture(new Capture<Long>()),
// capture(new Capture<Integer>()))).thenReturn(null);
// trackDataListener1.clearWaypoints();
// trackDataListener1.onNewWaypointsDone();
// replay();
// trackDataHub.start();
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
// verifyAndReset();
// }
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.min_required_accuracy_key.
*/
@Test
public void testNotifyPreferenceChanged_minRequiredAccuracy() {
int value = 1;
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key));
Assert.assertEquals(value, trackDataHub.getRecordingGpsAccuracy());
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value + 1);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key));
Assert.assertEquals(value + 1, trackDataHub.getRecordingGpsAccuracy());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
@Test
public void testNotifyPreferenceChanged_metricUnitsNoNotify() {
String imperialUnits = context.getString(R.string.stats_units_imperial);
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_units_key));
Assert.assertFalse(trackDataHub.isMetricUnits());
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_units_key));
Assert.assertTrue(trackDataHub.isMetricUnits());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
@Test
public void testNotifyPreferenceChanged_reportSpeedNoNotify() {
String value = context.getString(R.string.stats_rate_pace);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_rate_key));
Assert.assertFalse(trackDataHub.isReportSpeed());
value = context.getString(R.string.stats_rate_speed);
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_rate_key));
Assert.assertTrue(trackDataHub.isReportSpeed());
}
//TODO
/**
* Fixed size cursor answer.
*
* @author Jimmy Shih
*/
private static class FixedSizeCursorAnswer {
private final int size;
public FixedSizeCursorAnswer(int size) {
this.size = size;
}
//
// @Override
// public Cursor answer() throws Throwable {
// MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID});
// for (long i = 0; i < size; i++) {
// cursor.addRow(new Object[]{i});
// }
// return cursor;
// }
}
/**
* Fixed size location iterator.
*
* @author Jimmy Shih
*/
private static class FixedSizeLocationIterator implements LocationIterator {
private final long startId;
private final Location[] locations;
private final Set<Integer> splitIndexSet = new HashSet<Integer>();
private int currentIndex = -1;
public FixedSizeLocationIterator(long startId, int size) {
this(startId, size, null);
}
public FixedSizeLocationIterator(long startId, int size, int... splitIndexes) {
this.startId = startId;
this.locations = new Location[size];
for (int i = 0; i < size; i++) {
Location location = new Location("gps");
location.setLatitude(-15.0 + i / 1000.0);
location.setLongitude(37 + i / 1000.0);
location.setAltitude(i);
locations[i] = location;
}
if (splitIndexes != null) {
for (int splitIndex : splitIndexes) {
splitIndexSet.add(splitIndex);
Location splitLocation = locations[splitIndex];
splitLocation.setLatitude(100.0);
splitLocation.setLongitude(200.0);
}
}
}
public void expectLocationsDelivered(TrackDataListener listener) {
for (int i = 0; i < locations.length; i++) {
if (splitIndexSet.contains(i)) {
listener.onSegmentSplit(locations[i]);
} else {
listener.onSampledInTrackPoint(locations[i]);
}
}
}
public void expectSampledLocationsDelivered(
TrackDataListener listener, int sampleFrequency, boolean includeSampledOut) {
boolean includeNext = false;
for (int i = 0; i < locations.length; i++) {
if (splitIndexSet.contains(i)) {
listener.onSegmentSplit(locations[i]);
includeNext = true;
} else if (includeNext || (i % sampleFrequency == 0)) {
listener.onSampledInTrackPoint(locations[i]);
includeNext = false;
} else if (includeSampledOut) {
listener.onSampledOutTrackPoint(locations[i]);
}
}
}
@Override
public boolean hasNext() {
return currentIndex < locations.length - 1;
}
@Override
public Location next() {
currentIndex++;
return locations[currentIndex];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public long getLocationId() {
return startId + currentIndex;
}
@Override
public void close() {
// Do nothing
}
}
}
@@ -1,65 +0,0 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
/**
* Tests for the WaypointCreationRequest class.
* {@link WaypointCreationRequest}
*
* @author Sandor Dornbush
*/
@RunWith(AndroidJUnit4.class)
public class WaypointCreationRequestTest {
@Test
public void testTypeParceling() {
WaypointCreationRequest original = WaypointCreationRequest.DEFAULT_WAYPOINT;
Parcel p = Parcel.obtain();
original.writeToParcel(p, 0);
p.setDataPosition(0);
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
Assert.assertEquals(original.getType(), copy.getType());
Assert.assertFalse(copy.isTrackStatistics());
Assert.assertNull(copy.getName());
Assert.assertNull(copy.getDescription());
Assert.assertNull(copy.getIconUrl());
}
@Test
public void testAllAttributesParceling() {
WaypointCreationRequest original = new WaypointCreationRequest(WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null);
Parcel p = Parcel.obtain();
original.writeToParcel(p, 0);
p.setDataPosition(0);
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
Assert.assertEquals(original.getType(), copy.getType());
Assert.assertFalse(copy.isTrackStatistics());
Assert.assertEquals("name", copy.getName());
Assert.assertEquals("category", copy.getCategory());
Assert.assertEquals("description", copy.getDescription());
Assert.assertEquals("img.png", copy.getIconUrl());
}
}
@@ -0,0 +1,100 @@
package de.dennisguse.opentracks.content.data;
import android.content.Context;
import android.location.Location;
import android.net.Uri;
import android.util.Pair;
import java.io.File;
import java.io.IOException;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.FileUtils;
public class TestDataUtil {
public static final double INITIAL_LATITUDE = 37.0;
public static final double INITIAL_LONGITUDE = -57.0;
public static final double ALTITUDE_INTERVAL = 2.5;
/**
* Create a track without any trackPoints.
*/
public static Track createTrack(long trackId) {
Track track = new Track();
track.setId(trackId);
track.setName("Test: " + trackId);
return track;
}
/**
* Simulates a track which is used for testing.
*
* @param trackId the trackId of the track
* @param numPoints the trackPoints number in the track
*/
public static Pair<Track, TrackPoint[]> createTrack(long trackId, int numPoints) {
Track track = createTrack(trackId);
TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; i++) {
trackPoints[i] = (createTrackPoint(i));
}
return new Pair<>(track, trackPoints);
}
public static Track createTrackAndInsert(ContentProviderUtils contentProviderUtils, long trackId, int numPoints) {
Pair<Track, TrackPoint[]> pair = createTrack(trackId, numPoints);
insertTrackWithLocations(contentProviderUtils, pair.first, pair.second);
return pair.first;
}
/**
* Creates a location.
*
* @param i the index to set the value of location.
* @return created location
*/
public static TrackPoint createTrackPoint(int i) {
Location location = new Location("test");
location.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
location.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
location.setAccuracy((float) i / 100.0f);
location.setAltitude(i * ALTITUDE_INTERVAL);
location.setTime(i + 1);
return new TrackPoint(location);
}
/**
* Inserts a track with locations into the database.
*
* @param track track to be inserted
* @param trackPoints trackPoints to be inserted
*/
public static void insertTrackWithLocations(ContentProviderUtils contentProviderUtils, Track track, TrackPoint[] trackPoints) {
contentProviderUtils.insertTrack(track);
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.getId());
}
/**
* Creates a Waypoint with a photo.
*
* @param context The context.
* @param trackId The track id.
* @param location The location.
* @return the Waypoint created.
*/
public static Waypoint createWaypointWithPhoto(Context context, long trackId, Location location) throws IOException {
String photoUrl = "";
File dstFile = new File(FileUtils.getImageUrl(context, trackId));
dstFile.createNewFile();
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
photoUrl = photoUri.toString();
return new Waypoint("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl);
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content.provider;
import android.content.ContentUris;
import android.content.Context;
import android.net.Uri;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
/**
* Tests {@link CustomContentProvider}.
*
* @author Youtao Liu
*/
public class CustomContentProviderTest {
private CustomContentProvider customContentProvider;
private final Context context = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
customContentProvider = new CustomContentProvider() {
};
}
/**
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
*/
@Test
public void testOnCreate() {
Assert.assertTrue(customContentProvider.onCreate(context));
}
/**
* Tests {@link CustomContentProvider#getType(Uri)}.
*/
@Test
public void testGetType() {
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
Assert.assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
Assert.assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
Assert.assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
}
}
@@ -0,0 +1,883 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.content.provider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.util.Pair;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import static org.mockito.Mockito.when;
/**
* A unit test for {@link ContentProviderUtils}.
*
* @author Bartlomiej Niechwiej
* @author Youtao Liu
*/
@RunWith(MockitoJUnitRunner.class)
public class CustomContentProviderUtilsTest {
private static final String NAME_PREFIX = "test name";
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
private static final String TEST_DESC = "Test Desc!";
private static final String TEST_DESC_NEW = "Test Desc new!";
private final Context context = ApplicationProvider.getApplicationContext();
private ContentProviderUtils contentProviderUtils;
@Mock
private Cursor cursorMock;
@Mock
private ContentResolver contentResolverMock;
@Before
public void setUp() {
contentProviderUtils = new ContentProviderUtils(context);
contentProviderUtils.deleteAllTracks(context);
}
@Test
public void testLocationIterator_noPoints() {
testIterator(1, 0, 1, false);
}
@Test
public void testLocationIterator_noBatchAscending() {
testIterator(1, 50, 100, false);
testIterator(2, 50, 50, false);
}
@Test
public void testLocationIterator_noBatchDescending() {
testIterator(1, 50, 100, true);
testIterator(2, 50, 50, true);
}
@Test
public void testLocationIterator_batchAscending() {
testIterator(1, 50, 11, false);
testIterator(2, 50, 25, false);
}
@Test
public void testLocationIterator_batchDescending() {
testIterator(1, 50, 11, true);
testIterator(2, 50, 25, true);
}
@Test
public void testLocationIterator_largeTrack() {
testIterator(1, 20000, 2000, false);
}
private void testIterator(long trackId, int numPoints, int batchSize, boolean descending) {
long lastPointId = initializeTrack(trackId, numPoints);
contentProviderUtils.setDefaultCursorBatchSize(batchSize);
List<TrackPoint> locations = new ArrayList<>(numPoints);
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, -1L, descending)) {
while (it.hasNext()) {
TrackPoint loc = it.next();
Assert.assertNotNull(loc);
locations.add(loc);
// Make sure the IDs are returned in the right order.
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
: lastPointId - numPoints + locations.size(), it.getTrackPointId());
}
Assert.assertEquals(numPoints, locations.size());
}
}
private long initializeTrack(long id, int numPoints) {
Track track = new Track();
track.setId(id);
track.setName("Test: " + id);
contentProviderUtils.insertTrack(track);
track = contentProviderUtils.getTrack(id);
Assert.assertNotNull(track);
TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; ++i) {
Location loc = new Location("test");
loc.setLatitude(37.0 + (double) i / 10000.0);
loc.setLongitude(57.0 - (double) i / 10000.0);
loc.setAccuracy((float) i / 100.0f);
loc.setAltitude(i * 2.5);
trackPoints[i] = new TrackPoint(loc);
}
contentProviderUtils.bulkInsertTrackPoint(trackPoints, id);
// Load all inserted trackPoints.
long lastPointId = -1;
int counter = 0;
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, -1L, false)) {
while (it.hasNext()) {
it.next();
lastPointId = it.getTrackPointId();
counter++;
}
}
Assert.assertTrue(numPoints == 0 || lastPointId > 0);
Assert.assertEquals(numPoints, counter);
return lastPointId;
}
/**
* Tests the method {@link ContentProviderUtils#createTrack(Cursor)}.
*/
@Test
public void testCreateTrack() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long trackId = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
// Name
String name = NAME_PREFIX + trackId;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
Track track = contentProviderUtils.createTrack(cursorMock);
Assert.assertEquals(trackId, track.getId());
Assert.assertEquals(name, track.getName());
}
/**
* Tests the method {@link ContentProviderUtils#deleteAllTracks(Context)}
*/
@Test
public void testDeleteAllTracks() {
// Insert track, points and waypoint at first.
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
contentProviderUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(1, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(10, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Delete all.
contentProviderUtils.deleteAllTracks(context);
// Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtils#deleteAllTracks(Context)}
*/
@Test
public void testDeleteAllTracks_withWaypointAndPhoto() throws IOException {
// Insert track, points and waypoint with photo at first.
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
contentProviderUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(1, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(10, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Check waypoint has photo and it's in the external storage.
Assert.assertTrue(waypoint.hasPhoto());
File dir = FileUtils.getPhotoDir(context, trackId);
Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length);
Assert.assertTrue(dir.exists());
// Delete all.
contentProviderUtils.deleteAllTracks(context);
// Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount());
Assert.assertFalse(dir.exists());
}
/**
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, long)}.
*/
@Test
public void testDeleteTrack() {
// Insert three tracks, points of two tracks and way point of one track.
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 0);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 1, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 2, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId + 1));
waypoint.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint);
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(3, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
// Delete one track.
contentProviderUtils.deleteTrack(context, trackId);
// Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, long)}.
*/
@Test
public void testDeleteTrack_withWaypointPhoto() throws IOException {
// Insert three tracks.
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 1, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId + 2, 10);
// Insert a waypoint in tracks trackId and trackId + 1.
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint1.getLocation());
contentProviderUtils.insertWaypoint(waypoint1);
File dir1 = FileUtils.getPhotoDir(context, trackId);
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId + 1);
Waypoint waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId + 1, trackPoint2.getLocation());
contentProviderUtils.insertWaypoint(waypoint2);
File dir2 = FileUtils.getPhotoDir(context, trackId + 1);
// Check.
ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(3, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(30, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(2, waypointCursor.getCount());
Assert.assertTrue(waypoint1.hasPhoto());
Assert.assertTrue(dir1.isDirectory());
Assert.assertEquals(1, dir1.list().length);
Assert.assertTrue(dir1.exists());
Assert.assertTrue(dir2.isDirectory());
Assert.assertEquals(1, dir2.list().length);
Assert.assertTrue(dir2.exists());
// Delete one track.
contentProviderUtils.deleteTrack(context, trackId);
// Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount());
Assert.assertFalse(dir1.exists());
Assert.assertTrue(dir2.exists());
}
/**
* Tests the method {@link ContentProviderUtils#getAllTracks()}
*/
@Test
public void testGetAllTracks() {
// given
int initialTrackNumber = contentProviderUtils.getAllTracks().size();
long trackId = System.currentTimeMillis();
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
// when
List<Track> allTracks = contentProviderUtils.getAllTracks();
// then
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
}
/**
* Tests the method {@link ContentProviderUtils#getLastTrack()}
*/
@Test
public void testGetLastTrack() {
long trackId = System.currentTimeMillis();
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
}
/**
* Tests the method {@link ContentProviderUtils#getTrack(long)}
*/
@Test
public void testGetTrack() {
long trackId = System.currentTimeMillis();
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
Assert.assertNotNull(contentProviderUtils.getTrack(trackId));
}
/**
* Tests the method {@link ContentProviderUtils#updateTrack(Track)}
*/
@Test
public void testUpdateTrack() {
// given
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrack(trackId);
String nameOld = "name1";
String nameNew = "name2";
track.setName(nameOld);
// when / then
contentProviderUtils.insertTrack(track);
Assert.assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
track.setName(nameNew);
contentProviderUtils.updateTrack(track);
Assert.assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
}
/**
* Tests the method {@link ContentProviderUtils#createContentValues(Waypoint)}.
*/
@Test
public void testCreateContentValues_waypoint() {
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
// Bottom
long startTime = 1000L;
// AverageSpeed
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime_ms(startTime);
statistics.setStopTime_ms(2500L);
statistics.setTotalTime(1500L);
statistics.setMovingTime(700L);
statistics.setTotalDistance(750.0);
statistics.setTotalElevationGain(50.0);
statistics.setMaxSpeed(60.0);
statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0);
track.first.setTrackStatistics(statistics);
contentProviderUtils.insertTrack(track.first);
Waypoint waypoint = new Waypoint(track.second[0]);
waypoint.setDescription(TEST_DESC);
contentProviderUtils.insertWaypoint(waypoint);
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
long waypointId = System.currentTimeMillis();
waypoint.setId(waypointId);
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE));
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
}
/**
* Tests the method {@link ContentProviderUtils#createWaypoint(Cursor)}.
*/
@Test
public void testCreateWaypoint() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// trackIdIndex
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long id = System.currentTimeMillis();
columnIndex = startColumnIndex;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(id);
// Name
String name = NAME_PREFIX + id;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
// trackIdIndex
long trackId = 11L;
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock);
Assert.assertEquals(id, waypoint.getId());
Assert.assertEquals(name, waypoint.getName());
Assert.assertEquals(trackId, waypoint.getTrackId());
}
/**
* Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, long)}
* when there is only one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_onlyOneWayPoint() {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setDescription(TEST_DESC);
waypoint1.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint1);
// Check insert was done.
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
// Get waypoint id that needs to delete.
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
// Delete
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
}
/**
* Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, long)}
* when there is only one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_onlyOneWayPointWithPhotoUrl() throws IOException {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
contentProviderUtils.insertWaypoint(waypoint1);
// Check insert was done.
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
// Get waypoint id that needs to delete.
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
// Check waypoint has photo and it's in the external storage.
Assert.assertTrue(waypoint1.hasPhoto());
File dir = FileUtils.getPhotoDir(context, trackId);
Assert.assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length);
Assert.assertTrue(dir.exists());
// Delete
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
// Check waypoint doesn't exists and photo folder was deleted.
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
Assert.assertFalse(dir.exists());
}
/**
* Tests the method {@link ContentProviderUtils#deleteWaypoint(Context, long)} when there is more than one waypoint in the track.
*/
@Test
public void testDeleteWaypoint_hasNextWayPoint() {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Track track = TestDataUtil.createTrackAndInsert(trackId, 10);
//
// TrackStatistics statistics = new TrackStatistics();
// statistics.setStartTime_ms(1000L);
// statistics.setStopTime_ms(2500L);
// statistics.setTotalTime(1500L);
// statistics.setMovingTime(700L);
// statistics.setTotalDistance(750.0);
// statistics.setTotalElevationGain(50.0);
// statistics.setMaxSpeed(60.0);
// statistics.setMaxElevation(1250.0);
// statistics.setMinElevation(1200.0);
//
// track.setTrackStatistics(statistics);
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
// Insert at first.
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setDescription(MOCK_DESC);
waypoint1.setTrackId(trackId);
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint2.setDescription(MOCK_DESC);
waypoint2.setTrackId(trackId);
long waypoint2Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2));
// Delete
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id));
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
Assert.assertEquals(MOCK_DESC, contentProviderUtils.getWaypoint(waypoint2Id).getDescription());
}
/**
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(long)}.
*/
@Test
public void testGetNextWaypointNumber() {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setTrackId(trackId);
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint2.setTrackId(trackId);
Waypoint waypoint3 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint3.setTrackId(trackId);
Waypoint waypoint4 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint4.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint1);
contentProviderUtils.insertWaypoint(waypoint2);
contentProviderUtils.insertWaypoint(waypoint3);
contentProviderUtils.insertWaypoint(waypoint4);
Assert.assertEquals(4, contentProviderUtils.getNextWaypointNumber(trackId));
}
/**
* Tests the method {@link ContentProviderUtils#insertWaypoint(Waypoint)} and
* {@link ContentProviderUtils#getWaypoint(long)}.
*/
@Test
public void testInsertAndGetWaypoint() {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription());
}
/**
* Tests the method {@link ContentProviderUtils#updateWaypoint(Waypoint)}.
*/
@Test
public void testUpdateWaypoint() {
long trackId = System.currentTimeMillis();
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first.
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId);
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
// Update
waypoint = contentProviderUtils.getWaypoint(waypointId);
waypoint.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateWaypoint(waypoint);
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription());
}
/**
* Tests the method {@link ContentProviderUtils#bulkInsertTrackPoint(TrackPoint[], long)}.
*/
@Test
public void testBulkInsertTrackPoint() {
// given
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track.first, track.second);
// when / then
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
Assert.assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
contentProviderUtils.bulkInsertTrackPoint(Arrays.copyOfRange(track.second, 0, 8), trackId);
Assert.assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
}
/**
* Tests the method {@link ContentProviderUtils#createTrackPoint(Cursor)}.
*/
//TODO incomplete
@Test
public void testCreateTrackPoint() {
// given
when(cursorMock.getColumnIndex(TrackPointsColumns._ID)).thenReturn(1);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).thenReturn(2);
when(cursorMock.isNull(2)).thenReturn(false);
int longitude = 11;
when(cursorMock.getInt(2)).thenReturn(longitude * 1000000);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).thenReturn(3);
when(cursorMock.isNull(3)).thenReturn(false);
int latitude = 22;
when(cursorMock.getInt(3)).thenReturn(latitude * 1000000);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME)).thenReturn(4);
when(cursorMock.isNull(4)).thenReturn(false);
long time = System.currentTimeMillis();
when(cursorMock.getLong(4)).thenReturn(time);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).thenReturn(5);
when(cursorMock.isNull(5)).thenReturn(false);
float speed = 2.2f;
when(cursorMock.getFloat(5)).thenReturn(speed);
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(6);
when(cursorMock.isNull(6)).thenReturn(false);
// when
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
// then
Assert.assertEquals(longitude, trackPoint.getLongitude(), 0.01);
Assert.assertEquals(latitude, trackPoint.getLatitude(), 0.01);
Assert.assertEquals(time, trackPoint.getTime(), 0.01);
Assert.assertEquals(speed, trackPoint.getSpeed(), 0.01);
Assert.assertFalse(trackPoint.hasHeartRate());
}
/**
* Tests the method
* {@link ContentProviderUtils#insertTrackPoint(TrackPoint, long)}.
*/
@Test
public void testInsertTrackPoint() {
// Insert track, point at first.
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
Assert.assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
}
@Test
public void testInsertAndLoadTrackPoint() {
// given
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
trackPoint.setHeartRate_bpm(1F);
trackPoint.setCyclingCadence_rpm(2F);
trackPoint.setPower(3F);
// when
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
// then
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertTrue(trackPoints.get(10).hasHeartRate());
Assert.assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
Assert.assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
Assert.assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
}
/**
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(long)}.
*/
@Test
public void testGetLastValidTrackPoint() {
// Insert track, points at first.
long trackId = System.currentTimeMillis();
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint lastTrackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
checkLocation(9, lastTrackPoint.getLocation());
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in descending.
*/
@Test
public void testGetTrackPointCursor_desc() {
// given
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
long[] trackpointIds = new long[track.second.length];
for (int i = 0; i < trackpointIds.length; i++) {
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.second[i], track.first.getId()));
}
// when
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
// then
Assert.assertEquals(2, cursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in ascending.
*/
@Test
public void testGetTrackPointCursor_asc() {
// given
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
long[] trackpointIds = new long[track.second.length];
for (int i = 0; i < trackpointIds.length; i++) {
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.second[i], track.first.getId()));
}
// when
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
// then
Assert.assertEquals(2, cursor.getCount());
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean)} in descending.
*/
@Test
public void testGetTrackPointLocationIterator_desc() {
// given
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
long[] trackpointIds = new long[track.second.length];
for (int i = 0; i < trackpointIds.length; i++) {
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.second[i], track.first.getId()));
}
long startTrackPointId = trackpointIds[9];
// when
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true);
// then
for (int i = 0; i < trackpointIds.length; i++) {
Assert.assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next();
Assert.assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId());
checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation());
}
Assert.assertFalse(trackPointIterator.hasNext());
}
/**
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean)} in ascending.
*/
@Test
public void testGetTrackPointLocationIterator_asc() {
// given
long trackId = System.currentTimeMillis();
Pair<Track, TrackPoint[]> track = TestDataUtil.createTrack(trackId, 10);
contentProviderUtils.insertTrack(track.first);
long[] trackpointIds = new long[track.second.length];
for (int i = 0; i < trackpointIds.length; i++) {
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.second[i], track.first.getId()));
}
long startTrackPointId = trackpointIds[0];
// when
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false);
// then
for (int i = 0; i < trackpointIds.length; i++) {
Assert.assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next();
Assert.assertEquals(startTrackPointId + i, trackPointIterator.getTrackPointId());
checkLocation(i, trackPoint.getLocation());
}
Assert.assertFalse(trackPointIterator.hasNext());
}
/**
* Checks the value of a location.
*
* @param i the index of this location which created in the method {@link TestDataUtil#createTrack(long, int)}
* @param location the location to be checked
*/
private void checkLocation(int i, Location location) {
Assert.assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
Assert.assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
Assert.assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
}
@Test
public void testFormatIdListForUri() {
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri(new long[]{}));
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new long[]{12}));
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new long[]{42, 43, 44}));
}
}
@@ -0,0 +1,179 @@
package de.dennisguse.opentracks.content.provider;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.HashMap;
import java.util.Map;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
@RunWith(AndroidJUnit4.class)
public class CustomSQLiteOpenHelperTest {
private static final String TRACKS_CREATE_TABLE_V23 = "CREATE TABLE tracks (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, starttime INTEGER, stoptime INTEGER, numpoints INTEGER, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, icon TEXT)";
private static final String TRACKPOINTS_CREATE_TABLE_V23 = "CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT)";
private static final String WAYPOINTS_CREATE_TABLE_V23 = "CREATE TABLE waypoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, icon TEXT, trackid INTEGER, type INTEGER, length FLOAT, duration INTEGER, starttime INTEGER, startid INTEGER, stopid INTEGER, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, photoUrl TEXT)";
private static final String DATABASE_NAME = "test.db";
private final Context context = ApplicationProvider.getApplicationContext();
/**
* Get the SQL create statements for all tables (ordered by name).
*
* @return Map(TableName, SQL)
*/
private static Map<String, String> getTableSQL(SQLiteDatabase db) {
HashMap<String, String> tableSQL = new HashMap<>();
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + WaypointsColumns.TABLE_NAME + "')", null, null, null, "name")) {
if (cursor != null) {
while (cursor.moveToNext()) {
tableSQL.put(cursor.getString(0), cursor.getString(1));
}
}
}
return tableSQL;
}
/**
* Get the SQL create statements for all indices (ordered by name).
*
* @return Map(tableName, SQL)
*/
public static Map<String, String> getIndexSQL(SQLiteDatabase db) {
HashMap<String, String> indexSQL = new HashMap<>();
try (Cursor cursor = db.rawQuery("SELECT tbl_name, SQL FROM sqlite_master WHERE type = 'index'", null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
indexSQL.put(cursor.getString(0), cursor.getString(1));
}
}
}
return indexSQL;
}
/**
* Returns true if a SQL create statement is in the schema.
*
* @param sqlCreate the table name
*/
private static boolean hasSqlCreate(SQLiteDatabase db, String sqlCreate) {
try (Cursor cursor = db.rawQuery("SELECT SQL FROM sqlite_master", null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
String sql = cursor.getString(0);
if (sqlCreate.equals(sql)) {
return true;
}
}
}
}
return false;
}
@Before
public void setUp() {
context.deleteDatabase(DATABASE_NAME);
}
@Test
public void onCreate() {
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase()) {
Assert.assertTrue(hasSqlCreate(db, TracksColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX));
Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE));
Assert.assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX));
}
}
@Test
public void onUpgrade_FromVersion23() {
// Manually create database schema with version 23 (base version)
{
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}
// Open database with SQL upgrade
Map<String, String> tableByUpgrade;
Map<String, String> indicesByUpgrade;
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
tableByUpgrade = getTableSQL(dbUpgraded);
indicesByUpgrade = getIndexSQL(dbUpgraded);
}
// Open database via
context.deleteDatabase(DATABASE_NAME);
Map<String, String> tablesByCreate;
Map<String, String> indicesByCreate;
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
tablesByCreate = getTableSQL(dbCreated);
indicesByCreate = getIndexSQL(dbCreated);
}
// then - verify table structure
Assert.assertEquals(3, tableByUpgrade.size());
Assert.assertEquals(tableByUpgrade.size(), tablesByCreate.size());
Assert.assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME));
Assert.assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME));
Assert.assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME));
// then - verify custom indices
Assert.assertEquals(2, indicesByCreate.size());
Assert.assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME));
Assert.assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME));
}
@Test
public void onDowngrade_ToVersion23() {
// Create most recent database schema
new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase().close();
// Downgrade schema to version 23 (base version)
Map<String, String> tablesByDowngrade;
Map<String, String> indicesByDowngrade;
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
tablesByDowngrade = getTableSQL(db);
indicesByDowngrade = getIndexSQL(db);
}
// then - verify table structure
Assert.assertEquals(TRACKS_CREATE_TABLE_V23, tablesByDowngrade.get("tracks"));
Assert.assertEquals(TRACKPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("trackpoints"));
Assert.assertEquals(WAYPOINTS_CREATE_TABLE_V23, tablesByDowngrade.get("waypoints"));
// then - verify custom indices
Assert.assertEquals(0, indicesByDowngrade.size());
}
}
@@ -0,0 +1,28 @@
package de.dennisguse.opentracks.content.provider;
import android.net.Uri;
import android.util.Pair;
import org.junit.Assert;
import org.junit.Test;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
public class ShareContentProviderTest {
@Test
public void testCreateandParseURI() {
long[] trackIds = {1, 3, 5};
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_ONLY_TRACK);
Assert.assertArrayEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
}
@Test
public void testCreateURIescapeFilename() {
long[] trackIds = {1};
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_ONLY_TRACK);
Assert.assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
}
}
@@ -0,0 +1,92 @@
package de.dennisguse.opentracks.content.sensor;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.util.UintUtils;
@RunWith(AndroidJUnit4.class)
public class SensorDataCyclingTest {
@Test
public void compute_cadence() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024); // 1s
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 2048); // 2s
// when
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_cadence_sameCount() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 2048);
// when
current.compute(previous);
// then
Assert.assertEquals(0, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_cadence_sameTime() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 1024);
// when
current.compute(previous);
// then
Assert.assertFalse(current.hasCadence_rpm());
}
@Test
public void compute_cadence_rollOverTime() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 2, 0);
// when
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_cadence_rollOverCount() {
// given
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous);
// then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
}
@Test
public void compute_speed_rollOverCount() {
// given
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
// when
current.compute(previous, 2000);
// then
Assert.assertEquals(2, current.getSpeed_mps(), 0.01);
}
}
@@ -17,19 +17,22 @@
package de.dennisguse.opentracks.fragments;
import android.location.Location;
import android.os.Looper;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.ChartView;
import de.dennisguse.opentracks.TrackStubUtils;
import de.dennisguse.opentracks.content.SensorDataSetLocation;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.chart.ChartPoint;
import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.UnitConversions;
/**
@@ -44,298 +47,274 @@ public class ChartFragmentTest {
private ChartFragment chartFragment;
@BeforeClass
public static void preSetUp() {
// Prepare looper for Android's message queue
if (Looper.myLooper() == null) Looper.prepare();
}
@AfterClass
public static void finalTearDown() {
if (Looper.myLooper() != null) Looper.myLooper().quit();
}
@Before
public void setUp() {
chartFragment = new ChartFragment();
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext()));
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
boolean chartByDistance = false;
chartFragment = (ChartFragment) ChartFragment.newInstance(chartByDistance);
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
chartFragment.setTrackStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
}
/**
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#fillDataPoint(Location, double[])}
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testFillDataPoint_sensorIncorrect() {
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
public void testCreatePendingPoint_sensorIncorrect() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
// No input.
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// Input incorrect state.
// Creates SensorData.
SensorDataSet sensorDataSet = new SensorDataSet(SensorDataSet.DATA_UNAVAILABLE, SensorDataSet.DATA_UNAVAILABLE);
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
// Test.
point = fillDataPointTestHelper(sensorDataSetLocation);
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
// then
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
}
/**
* Tests the logic to get the correct values of sensor in {@link ChartFragment#fillDataPoint(Location, double[])}.
* Tests the logic to get the correct values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testFillDataPoint_sensorCorrect() {
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
// No input.
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
public void testCreatePendingPoint_sensorCorrect() {
// given
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setHeartRate_bpm(100f);
trackPoint.setCyclingCadence_rpm(101f);
trackPoint.setPower(102f);
// Creates SensorData.
SensorDataSet sensorDataSet = new SensorDataSet(100, 101, 102);
// when
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// Creates SensorDataSet.
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
// Test.
point = fillDataPointTestHelper(sensorDataSetLocation);
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1], 0.01);
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1], 0.01);
// then
Assert.assertEquals(100.0, point.getHeartRate(), 0.01);
Assert.assertEquals(101.0, point.getCadence(), 0.01);
Assert.assertEquals(102.0, point.getPower(), 0.01);
}
/**
* Tests the logic to get the value of metric Distance in {@link ChartFragment#fillDataPoint(Location, double[])}.
* Tests the logic to get the value of metric Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testFillDataPoint_distanceMetric() {
// By distance.
public void testCreatePendingPoint_distanceMetric() {
chartFragment.setChartByDistance(true);
// Resets last location and writes first location.
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[0], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second is a same location, just different time.
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals(0.0, point[0], 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use metric.
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation3.setLatitude(23);
point = fillDataPointTestHelper(sensorDataSetLocation3);
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint3);
// Computes the distance between Latitude 22 and 23.
float[] results = new float[4];
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals(distance1, point[0], 0.01);
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The fourth location is a new location, and use metric.
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation4.setLatitude(24);
point = fillDataPointTestHelper(sensorDataSetLocation4);
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
trackPoint4.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint4);
// Computes the distance between Latitude 23 and 24.
Location.distanceBetween(sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(),
sensorDataSetLocation4.getLatitude(), sensorDataSetLocation4.getLongitude(), results);
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals((distance1 + distance2), point[0], 0.01);
Assert.assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#fillDataPoint(Location, double[])}.
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testFillDataPoint_distanceImperial() {
public void testCreatePendingPoint_distanceImperial() {
// By distance.
chartFragment.setChartByDistance(true);
// Setups to use imperial.
chartFragment.setMetricUnits(false);
// The first is a same location, just different time.
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[0], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second location is a new location, and use imperial.
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation2.setLatitude(23);
point = fillDataPointTestHelper(sensorDataSetLocation2);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setLatitude(23);
point = chartFragment.createPendingPoint(trackPoint2);
/*
* Computes the distance between Latitude 22 and 23. And for we set using
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
* Computes the distance between Latitude 22 and 23.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
float[] results = new float[4];
Location.distanceBetween(sensorDataSetLocation1.getLatitude(), sensorDataSetLocation1.getLongitude(),
sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(), results);
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1, point[0], 0.01);
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use imperial.
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation3.setLatitude(24);
point = fillDataPointTestHelper(sensorDataSetLocation3);
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
trackPoint3.setLatitude(24);
point = chartFragment.createPendingPoint(trackPoint3);
/*
* Computes the distance between Latitude 23 and 24. And for we set using
* imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
* Computes the distance between Latitude 23 and 24.
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
*/
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1 + distance2, point[0], 0.01);
Assert.assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the values of time in {@link ChartFragment#fillDataPoint(Location, double[])}.
* Tests the logic to get the values of time in {@link ChartFragment#createPendingPoint(TrackPoint)}.
*/
@Test
public void testFillDataPoint_time() {
// By time
public void testCreatePendingPoint_time() {
chartFragment.setChartByDistance(false);
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[0], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01);
long timeSpan = 222;
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals((double) timeSpan, point[0], 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
}
/**
* Tests the logic to get the value of elevation in {@link ChartFragment#fillDataPoint(Location, double[])} by one and two points.
* Tests the logic to get the value of elevation in {@link ChartFragment#createPendingPoint(TrackPoint)} by one and two points.
*/
@Test
public void testFillDataPoint_elevation() {
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
public void testCreatePendingPoint_elevation() {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
/*
* At first, clear old points of elevation, so give true to the second
* parameter. Then only one value INITIALLONGTITUDE in buffer.
* At first, clear old points of elevation, so give true to the second parameter.
* Then only one value INITIAL_ALTITUDE in buffer.
*/
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1], 0.01);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
/*
* Send another value to buffer, now there are two values, INITIALALTITUDE
* and INITIALALTITUDE * 2.
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
*/
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
point[ChartView.ELEVATION_SERIES + 1], 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
}
/**
* Tests the logic to get the value of speed in {@link ChartFragment#fillDataPoint(Location, double[])}.
* Tests the logic to get the value of speed in {@link ChartFragment#createPendingPoint(TrackPoint)}.
* In this test, firstly remove all points in memory, and then fill in two points one by one.
* The speed values of these points are 129, 130.
*/
@Test
public void testFillDataPoint_speed() {
public void testCreatePendingPoint_speed() {
/*
* At first, clear old points of speed, so give true to the second
* parameter. It will not be filled in to the speed buffer.
* At first, clear old points of speed, so give true to the second parameter.
* It will not be filled in to the speed buffer.
*/
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation1.setSpeed(128.5f);
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(128.5f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
/*
* Tests the logic when both metricUnits and reportSpeed are true.This
* location will be filled into speed buffer.
* Tests the logic when both metricUnits and reportSpeed are true.
* This location will be filled into speed buffer.
*/
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point is valid, the value
* 222 here is doesn't matter.
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
*/
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + 222);
sensorDataSetLocation2.setSpeed(130);
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1], 0.01);
trackPoint2.setTime(trackPoint1.getTime() + 222);
trackPoint2.setSpeed(130);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point.getSpeed(), 0.01);
}
/**
* Tests the logic to compute speed when use Imperial.
*/
@Test
public void testFillDataPoint_speedImperial() {
// Setups to use imperial.
public void testCreatePendingPoint_speedImperial() {
chartFragment.setMetricUnits(false);
// First data point is not added to the speed buffer
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation1.setSpeed(100.0f);
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point is valid and the speed
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
* * timeDifference speedDifference = 102 -100 timeDifference = 222
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
sensorDataSetLocation2.setSpeed(102);
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1], 0.01);
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false.
*/
@Test
public void testFillDataPoint_pace_nonZeroSpeed() {
// Setups reportSpeed to false.
public void testCreatePendingPoint_pace_nonZeroSpeed() {
chartFragment.setReportSpeed(false);
// First data point is not added to the speed buffer
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation1.setSpeed(100.0f);
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
/*
* Add a time span here to make sure the second point is valid and the speed
* is valid. Speed is valid if: speedDifference > Constants.MAX_ACCELERATION
* * timeDifference speedDifference = 102 -100 timeDifference = 222
* Add a time span here to make sure the second point and the speed is valid.
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
*/
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
sensorDataSetLocation2.setSpeed(102);
point = fillDataPointTestHelper(sensorDataSetLocation2);
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1], 0.01);
trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point.getPace(), 0.01);
}
/**
* Tests the logic to get pace value when reportSpeed is false and average
* speed is zero.
* Tests the logic to get pace value when reportSpeed is false and average speed is zero.
*/
@Test
public void testFillDataPoint_pace_zeroSpeed() {
// Setups reportSpeed to false.
public void testCreatePendingPoint_pace_zeroSpeed() {
chartFragment.setReportSpeed(false);
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
sensorDataSetLocation.setSpeed(0);
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
}
/**
* Helper method to test fillDataPoint.
*
* @param location location to fill
* @return data of this location
*/
private double[] fillDataPointTestHelper(Location location) {
double[] point = new double[ChartView.NUM_SERIES + 1];
chartFragment.fillDataPoint(location, point);
return point;
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setSpeed(0);
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
Assert.assertEquals(0.0, point.getPace(), 0.01);
}
}

Some files were not shown because too many files have changed in this diff Show More