Compare commits

...

120 Commits

Author SHA1 Message Date
Dennis Guse a42bdf32e1 Release: v3.11.1 2020-10-06 08:16:58 +02:00
Dennis Guse c29d69b44d Cleanup. 2020-10-05 22:59:21 +02:00
Román 547b1d07bd Bugfix: interval stats announced incorrectly because an error in last interval computing. Fixes #443. 2020-10-05 20:08:07 +02:00
Dennis Guse 22a1dfd354 README: removed link to Maps.ME due to the amount of embedded trackers. 2020-10-04 12:51:14 +02:00
Dennis Guse be81e758c6 License: use non-Markdown, so Github-API identifies it. 2020-09-26 23:16:19 +02:00
Dennis Guse b3117e6410 Release: v3.11.0 2020-09-26 20:23:32 +02:00
Dennis Guse 47ab109c2b F-Droid now signs APKs with v2, so installing on Android 11 works.
More info: #421
This reverts commit ee22fd8e
2020-09-26 19:56:22 +02:00
Dennis Guse bc04efbaac Bugfix: for export all write permissions are required.
Android 11 enforces this (and it is good practice).
Fixes #439.
2020-09-26 19:52:48 +02:00
Román fbb9d21e73 TrackRecordingActivity keeps Track.Id value instead of track object. Every time the track is needed it gets it. Fixes #435. 2020-09-26 16:47:51 +02:00
dependabot[bot] f5308e4287 Bump mockito-android from 3.5.11 to 3.5.13
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.11 to 3.5.13.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.11...v3.5.13)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-25 18:20:32 +02:00
Dennis Guse 15b5920e3b README: add F-Droid version shield 2020-09-25 12:37:58 +02:00
Dennis Guse d09aaab398 Theming: import/export finished dialog needs to be AndroidX. 2020-09-24 22:45:20 +02:00
pappapisshu f7008e2362 Update strings.xml
Fixed italian translation.
2020-09-24 17:16:54 +02:00
Dennis Guse e939778beb Refactor: use marker instead of waypoint.
Fixes #389.
2020-09-23 17:16:34 +02:00
Dennis Guse 8643b50bd1 Bugfix: application context does not provide access to theme attributes. 2020-09-23 12:12:34 +02:00
Dennis Guse 74e42525d4 Cleanup. 2020-09-23 08:11:10 +02:00
Dennis Guse 833a387574 Theming: splashscreen uses dark background color. 2020-09-23 08:05:40 +02:00
Dennis Guse e574c67ad6 Theming: activity icons are now in textColorPrimary.
And replaced spinner in StatisticsRecordedFragment.
2020-09-23 07:36:15 +02:00
Dennis Guse ce38d5e891 Theming: define colorAccent. 2020-09-23 07:36:15 +02:00
Dennis Guse 3a44da4a9d Theming: stats labels use color accent. 2020-09-23 07:36:15 +02:00
Dennis Guse eba9106db5 Theming: delete dialog needs to be Androidx for proper theming. 2020-09-23 07:36:15 +02:00
Dennis Guse 4501226562 Theming: reduce font size of lists first line. 2020-09-23 07:36:15 +02:00
Dennis Guse b974193564 Theming: delete dialog uses delete icon. 2020-09-23 07:36:15 +02:00
Dennis Guse 0b3e447258 Theming: charts use theme text colors. 2020-09-23 07:36:15 +02:00
Dennis Guse 9908cc02a2 Theming: added color.xml for night styling. 2020-09-23 07:36:15 +02:00
Dennis Guse 4e67011d7c Theming: style selected list items with transparent color. 2020-09-23 07:36:15 +02:00
Dennis Guse 5f93c0bab3 Theming: tabs use default indicator color. 2020-09-23 07:36:15 +02:00
Dennis Guse caec5e7c45 Theming: chart use default design background color. 2020-09-23 07:36:15 +02:00
Dennis Guse 240cb4b363 Theming: chart colors extracted. 2020-09-23 07:36:15 +02:00
Dennis Guse 91e8eb66b1 Theming: chart text is now rendered as FILL_AND_STROKE. 2020-09-23 07:36:15 +02:00
Dennis Guse 27bd69ec62 Code cleanup. 2020-09-23 07:36:15 +02:00
Dennis Guse 553fd39e21 Theming: make SplashScreen fullscreen. 2020-09-23 07:36:15 +02:00
Dennis Guse 733ee1c6e6 Theming: move TrackEdit to material design text fields. 2020-09-23 07:36:15 +02:00
Dennis Guse ef61c9597d Theming: activity chooser matches parent height. 2020-09-23 07:36:15 +02:00
Dennis Guse e874af791e Theming: removed all custom modifications to Holo theme. 2020-09-23 07:36:15 +02:00
Dennis Guse a4b2bb1aa0 Theming: tint all icons with colorControlNormal. 2020-09-23 07:36:15 +02:00
Dennis Guse bc9b20ffc8 Theming: marker detail uses default background. 2020-09-23 07:36:15 +02:00
Dennis Guse 6f212328a1 Theming: merged edit buttons into layout. 2020-09-23 07:36:15 +02:00
Dennis Guse a68cde7ac9 Theming: listView main item is now TextLarge. 2020-09-23 07:36:15 +02:00
Dennis Guse 5cab331058 Theming: removed custom spinner style. 2020-09-23 07:36:15 +02:00
Dennis Guse 6d25a10abd Theming: use default text color. 2020-09-23 07:36:15 +02:00
Dennis Guse d2516947f2 Theming: removed custom radio button style - use Material design. 2020-09-23 07:36:15 +02:00
Dennis Guse 4150d060ee Theming: enable day/night mode incl. app-wide setting. 2020-09-23 07:36:15 +02:00
Dennis Guse 1688076457 Theming: use AppCompat themes instead of Holo. 2020-09-23 07:36:15 +02:00
Román 5c1f669b2b Bugfix: spinner adapter needed notifyDataSetChanged to reload intervals with unit when user change from km to mi and the other way around. 2020-09-21 19:12:57 +02:00
Dennis Guse cc1e080ce1 Interval statistics: show unit in dropdown. 2020-09-20 19:05:32 +02:00
Dennis Guse 0dfd31a308 Code cleanup via Android Studio analyze.
Notably change: TrackPoint's copy constructor missed to copy the elevationGain.
2020-09-20 18:46:05 +02:00
dependabot[bot] bd13535d3c Bump mockito-android from 3.5.10 to 3.5.11
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.10 to 3.5.11.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.10...v3.5.11)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-18 08:07:00 +02:00
Dennis Guse 27386bb4f0 Localization: update via Transifex CLI. 2020-09-17 23:44:37 +02:00
Dennis Guse c6c07f03bf Localization: add configuration for Transifex CLI. 2020-09-17 23:43:28 +02:00
Dennis Guse b0ad08f7c5 Release: v3.10.0 2020-09-14 18:38:48 +02:00
Román d1957331b8 Intervals refactoring: added stack mode to the intervals adapter with two possibilities: stack from top and stack from bottom.
Stack from top for recorded intervals (the first lap in the top).
Stack from bottom for recording intervals (the last lap in the top).
2020-09-14 18:31:12 +02:00
Román ffedc04777 Intervals refactoring: avoid duplication code: IntervalsRecordingFragment is a version of IntervalsFragment. 2020-09-14 18:31:12 +02:00
Román 684762db58 Intervals refactoring: ListView instead of LinearLayout with a list of views. 2020-09-14 18:31:12 +02:00
thrillfall 5a44da3fb0 extract logic for creating DocumentFile for export to align naming 2020-09-14 17:25:29 +02:00
Dennis Guse 88cf46d999 Bugfix: another missed toString() in the id-refactoring. 2020-09-14 12:29:31 +02:00
Román 906b9413d1 Improved and simplified lap time/speed announcement message. 2020-09-13 19:30:17 +02:00
Dennis Guse 060c4f1529 Theming: add a SplashScreen for TrackListActivity. 2020-09-12 00:03:42 +02:00
Dennis Guse 7902aea849 Disable flacky test. 2020-09-12 00:01:56 +02:00
Sudeep Duggal ee22fd8ee8 Added workaround for allowing Android 11 install with only apk v1 signature 2020-09-10 08:28:55 +02:00
Román a6e275f8aa Several hidden bugfixes around Track.Id 2020-09-09 17:53:07 +02:00
Román a61468d600 Bugfix: String.valueOf doesn't work with Track.Id. Fixes #422. 2020-09-09 17:53:07 +02:00
thrillfall 78c26cb669 unify export filename logic 2020-09-09 17:50:00 +02:00
Dennis Guse 0d493c410e Merge branch 'barometer#10' into main 2020-09-06 22:34:54 +02:00
Dennis Guse 80dbbbc1ed Release: v3.9.5 2020-09-06 19:51:34 +02:00
Dennis Guse 81e8872a11 Added script for creating releases. 2020-09-06 19:49:42 +02:00
Román 6e1aba6fa0 Added barriers in stats recorded layout and stats recording layout. 2020-09-06 18:17:46 +02:00
Román 4220c62ac0 Bugfix: interval recording fragment has to keep select interval. 2020-09-06 12:21:39 +02:00
Dennis Guse 4b73567075 Bugfix: ClassCastException of Marker.Id 2020-09-06 08:06:29 +02:00
Dennis Guse fdff0d255a Autoformated drawables. 2020-09-06 00:09:39 +02:00
Dennis Guse e956cf14c4 Present speed only with one fractional digit. 2020-09-05 20:53:55 +02:00
Román a1646e1b76 Bugfix: bad equal comparison between Track.Id objects. 2020-09-05 15:46:49 +02:00
Dennis Guse 5099118dd7 Elevation gain: make it optional as Float - and if null, then the UI just shows "-".
In TrackStatistics the elevation gain is now nullable.
2020-09-05 12:39:23 +02:00
Dennis Guse 6845ce4984 Elevation gain: make it optional as Float.
So if no sensor data is provided, it is not stored in the database and not exported.
2020-09-05 12:39:23 +02:00
Dennis Guse e0d1ad4b52 Squash: fixed tests. 2020-09-05 12:39:23 +02:00
Dennis Guse 04b8d0f4a8 Import/Export: elevation gain per TrackPoint. 2020-09-05 12:39:23 +02:00
Román fe3bf61acb Squash: load TrackPoint elevationGain. 2020-09-05 12:39:23 +02:00
Román 577b9b4635 Alter table trackpoints: added elevation gain to the trackpoints table. 2020-09-05 12:39:23 +02:00
Dennis Guse 4b7de188fd Elevation gain adheres to visibility setting. 2020-09-05 12:39:23 +02:00
Román 3bfb212dc9 Bugfix: reset elevation every time track point is inserted. 2020-09-05 12:39:22 +02:00
Dennis Guse be7eb41cf3 Bugfix: previous total elevation gain was added twice. 2020-09-05 12:39:22 +02:00
Dennis Guse c7db4f528e Elevation gain: apply exponential smoothing and limit changes (defined change diff per sensor value). 2020-09-05 12:39:22 +02:00
Dennis Guse 8a14bc46f6 Implement elevation gain via barometer. 2020-09-05 12:39:22 +02:00
Dennis Guse 01ee1a1e49 Renamed DoubleBuffer to DoubleRingBuffer. 2020-09-05 12:39:22 +02:00
Dennis Guse 9d6c9676ab Refactoring to marker: content-package and fragments.
#389
2020-09-05 10:37:41 +02:00
Dennis Guse 19d8563a76 Refactoring to marker: TrackRecordingService.
#389
2020-09-05 10:20:16 +02:00
Dennis Guse 74e09e1fa9 Refactoring to marker: data class and ContentProviderUtils.
#389
2020-09-05 10:20:16 +02:00
Román 6533e3e170 Recording intervals received a null trackId because trackId is created in a Runnable.
Unit changed needs refreshing the intervals.
Fixes #406.
2020-09-05 10:18:01 +02:00
Dennis Guse bfeca771f8 Release v3.9.4 2020-09-05 01:41:01 +02:00
Dennis Guse f128111650 Code cleanup. 2020-09-05 01:19:59 +02:00
Dennis Guse 38fadda762 Bugfix: show on map via GPX/KML/KMZ was failing.
We were sending the trackId as intent extra (actually unused) and this trackId could not be unparceled.
Fixes #411.
2020-09-05 01:19:59 +02:00
Dennis Guse d6d40b01e6 Refactoring.
Also fixes #406.
2020-09-05 01:17:40 +02:00
dependabot[bot] bc051f5d81 Bump mockito-android from 3.5.9 to 3.5.10
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.5.9 to 3.5.10.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.5.9...v3.5.10)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-04 23:45:30 +02:00
dependabot[bot] 39bf2190a1 Bump material from 1.2.0 to 1.2.1
Bumps [material](https://github.com/material-components/material-components-android) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/material-components/material-components-android/releases)
- [Commits](https://github.com/material-components/material-components-android/compare/1.2.0...1.2.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-03 16:55:31 +02:00
Román b88f81726b Bugfix: location handler didn't remove updates. Fixes #403. 2020-09-03 15:32:44 +02:00
Dennis Guse 24e6cdec62 Interval Stats: fixed NPE introduced in 25ef0ecad1.
Includes some refactoring.
2020-09-02 23:12:52 +02:00
Dennis Guse 951671b2f3 Update PULL_REQUEST_TEMPLATE.md 2020-09-02 22:20:50 +02:00
dependabot[bot] 21e30545a0 Bump mockito-android from 3.4.6 to 3.5.9
Bumps [mockito-android](https://github.com/mockito/mockito) from 3.4.6 to 3.5.9.
- [Release notes](https://github.com/mockito/mockito/releases)
- [Commits](https://github.com/mockito/mockito/compare/v3.4.6...v3.5.9)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-02 19:26:45 +02:00
Dennis Guse 946edb7e16 Update CONTRIBUTING.md 2020-09-02 19:24:33 +02:00
Román 89c44af374 Recorded stats: natural order for intervals. 2020-09-02 14:32:32 +02:00
Dennis Guse faad451e22 Added contribution guidelines 2020-09-02 08:10:55 +02:00
Dennis Guse 25ef0ecad1 Interval Stats: use a tab for recorded tracks as well. 2020-09-02 07:52:33 +02:00
Román efa6caca66 New style for spinner with text. 2020-09-02 07:52:33 +02:00
Román 5be4c107f4 Announcement: while recording report current interval pace or speed.
Fixes #134.
2020-09-02 07:52:33 +02:00
Román 65f42dd3e5 Interval Stats: improve code and fixed some UI issues. 2020-09-02 07:52:33 +02:00
Román 287d246777 Interval Stats: track's id it's not long anymore. 2020-09-02 07:52:33 +02:00
Román d56f652d0a Interval stats: while user is recording a track intervals are refreshed every 5 seconds.
Interval stats: completed the list of split intervals: 1, 2, 3, 4, 5, 10, 20 or 50 km or mi.
Fixes #87.
2020-09-02 07:52:33 +02:00
Román 1f1ef4445f Interval stats: added IntervalStatisticsTest. 2020-09-02 07:52:33 +02:00
Román 22a669a573 Interval stats: added a new tab in TrackRecordingActivity that shows the intervals on live. 2020-09-02 07:52:33 +02:00
Román f55cce5614 Interval stats: improve code. 2020-09-02 07:52:33 +02:00
Román a6c3cf1c28 Interval statistics: listen preferred units change (metric/imperial) and recreate intervals. 2020-09-02 07:52:33 +02:00
Román cbc803d038 Interval statistics: retain interval value after change orientation and recreation. 2020-09-02 07:52:33 +02:00
Román a8cefcd0fd Interval statistics: show accumulate in distance column. 2020-09-02 07:52:33 +02:00
Román 2b95dbab22 Adjusted intervals. 2020-09-02 07:52:33 +02:00
Román 7a923a947e Added intervals in StatisticsRecordedFragment with distance, speed and pace for all activity types.
Users can select the length of the interval (in a spinner with several options).
The intervals will be in km or mi depending on settings.
2020-09-02 07:52:33 +02:00
Román 19e35a7609 StatisticsRecordedFragment doesn't need TrackDataHub.
TrackRecordedActivity saves Track object instead of track's id.
2020-09-02 07:52:33 +02:00
Dennis Guse b8088169ee Settings: animate the Bluetooth icon.
Fixes #314.
2020-09-01 22:19:19 +02:00
Román a5594fbb7a Release v3.9.3 2020-08-31 12:47:33 +02:00
Román a733d87c37 Bugfix: export all failed trying to convert to string the Track.Id. 2020-08-31 12:25:58 +02:00
Dennis Guse 79d8ba9ef8 Configure Dependabot for Gradle and Github actions 2020-08-30 23:28:50 +02:00
349 changed files with 7604 additions and 3594 deletions
+8
View File
@@ -1,5 +1,13 @@
# Thanks for your contribution. # Thanks for your contribution.
## PLEASE REMOVE
To support us in providing a nice (and fast) open-source experience:
1. Verify that the tests are passing
2. Check that the code is properly formatted (using AndroidStudio's autoformatter)
3. Provide write access to the [branch](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork)
4. If the PR is not ready for review, please submit it as a [draft](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests)
## PLEASE REMOVE
**Describe the pull request** **Describe the pull request**
A clear and concise description of what the pull request changes/adds. A clear and concise description of what the pull request changes/adds.
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
+10
View File
@@ -0,0 +1,10 @@
[main]
host = https://www.transifex.com
[opentracks.src-main-res-values-strings-xml--main]
file_filter = src/main/res/values-<lang>/strings.xml
minimum_perc = 0
source_file = src/main/res/values/strings.xml
source_lang = en
type = ANDROID
+35
View File
@@ -0,0 +1,35 @@
# How to contribute?
## Testing
Just use OpenTracks and tell us what can be improved by opening an issue [here](https://github.com/OpenTracksApp/OpenTracks/issues/new/choose)
If we broke things (happens now and then), please tell us also.
## Coding new features
You have a cool idea for a feature that is missing in OpenTracks?
And you would like to help by implementing it?
If not, you can just take a look at the [issue tracker](https://github.com/OpenTracksApp/OpenTracks/issues).
Advices:
* creating an issue beforehand might be a good idea
Sometimes it is helpful to discuss your idea and also your implementation approach first.
We may be able to give you some hints on how to tackle the problem.
It also makes merging your changes easier and, usually, faster.
* please only add new dependencies if is absolutely necessary
If you need to:
* tell us why those are required,
* what alternatives are existing, and
* why you choose this dependency.
And also verify that the license(s) are compatible with _Apache 2.0_ as well as open-source.
## Coding conventions
None so far.
Just keep it simple.
And follow the _scout rule_.
You can take a look at existing code and get a feeling for it.
Please autoformat your code using AndroidStudio's code formatter.
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
-194
View File
@@ -1,194 +0,0 @@
Apache License
==============
_Version 2.0, January 2004_
_&lt;<http://www.apache.org/licenses/>&gt;_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
+3 -1
View File
@@ -13,6 +13,8 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
<td> <td>
<a href="https://f-droid.org/packages/de.dennisguse.opentracks"> <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"> <img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
<br />
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
</a> </a>
</td> </td>
<td> <td>
@@ -61,7 +63,7 @@ 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). The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
The developer documentation is in [README_API.md](README_API.md). The developer documentation is in [README_API.md](README_API.md).
Alternatively, recorded tracks can be shared as KMZ/GPX to installed applications (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)). Alternatively, recorded tracks can be shared as KMZ/GPX to installed applications (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand)).
However, this is rather slow and does not provide updates while recording. However, this is rather slow and does not provide updates while recording.
Executable
+51
View File
@@ -0,0 +1,51 @@
#!/bin/bash
set -e
CHANGELOG_DIR="fastlane/metadata/android/en-US/changelogs/"
echo "Checking main branch"
git checkout main
VERSIONID=$(git rev-list HEAD --count main)
CHANGELOG_FILE="$CHANGELOG_DIR/$VERSIONID.txt"
git diff --exit-code || echo -e "\nWARNING WARNING WARNING WARNING WARNING: this branch contains uncommit changes."
#TODO Execute all tests: we are destroying data, running on a real device.
# ./gradlew connectedCheck
# Changelog
echo -e "\nPrevious changelog\n------------"
PREVIOUS_CHANGELOG=$(find "$CHANGELOG_DIR" -iname "[[:digit:]]**.txt" | sort -n | tail -1)
sed 's/^/>/' "$PREVIOUS_CHANGELOG"
#For F-Droid, a version starts with a 'v'
read -p $'\n\nPlease enter the versionname (e.g., v3.9.5):\n> v' VERSIONNAME
VERSIONNAME="v$VERSIONNAME"
cp "$CHANGELOG_DIR/next_release.txt" "$CHANGELOG_FILE"
sed -i "s/(versionName)/$VERSIONNAME/" "$CHANGELOG_FILE"
echo -e "\nGit commit since last release\n------------"
git log $(git describe --tags --abbrev=0)..HEAD --no-merges --oneline
echo -e "\nPlease edit the changelog for the new release"
gedit -w "$CHANGELOG_FILE"
git add "$CHANGELOG_FILE"
echo "Updating build.gradle"
sed -i s/versionCode\ .*/versionCode\ "$VERSIONID"/g build.gradle
sed -i s/versionName\ .*/versionName\ "\"$VERSIONNAME\""/g build.gradle
git diff
COMMIT_MESSAGE="Release: $VERSIONNAME"
echo -e "\nCommit message will be: '$COMMIT_MESSAGE'"
echo -e "\nPress ENTER, if everything looks good."
read
git add -u
git commit -m "$COMMIT_MESSAGE"
echo -e "Next steps:\n1. Push to Github\n2. Create release on Github: https://github.com/OpenTracksApp/OpenTracks/releases"
+4 -4
View File
@@ -47,8 +47,8 @@ android {
defaultConfig { defaultConfig {
applicationId "de.dennisguse.opentracks" applicationId "de.dennisguse.opentracks"
versionCode 3541 versionCode 3661
versionName "v3.9.2" versionName "v3.11.1"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\"" buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
@@ -87,12 +87,12 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.documentfile:documentfile:1.0.1' implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.google.android.material:material:1.2.0' implementation 'com.google.android.material:material:1.2.1'
implementation 'com.android.support.constraint:constraint-layout:2.0.1' implementation 'com.android.support.constraint:constraint-layout:2.0.1'
androidTestImplementation 'androidx.test:core:1.3.0' androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'androidx.test:rules:1.3.0'
androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'org.mockito:mockito-android:3.4.6' androidTestImplementation 'org.mockito:mockito-android:3.5.13'
} }
@@ -0,0 +1,4 @@
v3.9.3: OpenTracks
Bugfixes:
- Fixed export all tracks bug.
@@ -0,0 +1,10 @@
v3.9.4: OpenTracks
Changes:
- Added interval computation
- Voice announcements present last interval statistics
- Settings: Bluetooth icon is animated
Bugfix:
- GPS was not turned off when stopping a recording
- Show on map via KML/KMZ/GPX was broken due to refactoring
@@ -0,0 +1,8 @@
v3.9.5: OpenTracks
Changes:
- UI shows speed only with one fractional digit
Bugfix:
- App crash in search (due to Track.Id refactoring)
- Track list did now show if a recording was ongoing (due to Track.Id refactoring)
@@ -0,0 +1,10 @@
v3.10.0: OpenTracks
Changes:
- Elevation gain via barometer
- Add splash screen
- Android 11/F-Droid: downgrade to targetSDK 29 (v1 signature)
see https://gitlab.com/fdroid/fdroidserver/-/issues/827
Bugfix:
- Several fixes due to trackId refactoring
@@ -0,0 +1,10 @@
v3.11.0: OpenTracks
Changes:
- Theming: day/night mode (default: night)
- Theming: use Material Design instead of custom element theming
- Use targetSDK 30 again (F-Droid signs now with V2)
Bugfix:
- Android 11: export all crashed
- TrackDetail not updated in UI after data was changed by user
@@ -0,0 +1,4 @@
v3.11.1: OpenTracks
Bugfix:
- Voice announcements reported wrong statistics
@@ -1,4 +1,4 @@
(tbd): OpenTracks (versionName): OpenTracks
Changes: Changes:
- -
@@ -19,13 +19,16 @@ import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/** /**
* Tests {@link ChartValueSeries}. * Tests {@link ChartValueSeries}.
* *
@@ -60,16 +63,16 @@ public class ChartValueSeriesTest {
@Test @Test
public void testInitialConditions() { public void testInitialConditions() {
Assert.assertEquals(1, series.getInterval()); assertEquals(1, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue()); assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(5, series.getMaxMarkerValue()); assertEquals(5, series.getMaxMarkerValue());
Assert.assertTrue(series.isEnabled()); assertTrue(series.isEnabled());
} }
@Test @Test
public void testEnabled() { public void testEnabled() {
series.setEnabled(false); series.setEnabled(false);
Assert.assertFalse(series.isEnabled()); assertFalse(series.isEnabled());
} }
@Test @Test
@@ -77,9 +80,9 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(0)); series.update(new ChartPoint(0));
series.update(new ChartPoint(10)); series.update(new ChartPoint(10));
series.updateDimension(); series.updateDimension();
Assert.assertEquals(100, series.getInterval()); assertEquals(100, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue()); assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(500, series.getMaxMarkerValue()); assertEquals(500, series.getMaxMarkerValue());
} }
@Test @Test
@@ -87,9 +90,9 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(0)); series.update(new ChartPoint(0));
series.update(new ChartPoint(901)); series.update(new ChartPoint(901));
series.updateDimension(); series.updateDimension();
Assert.assertEquals(1000, series.getInterval()); assertEquals(1000, series.getInterval());
Assert.assertEquals(0, series.getMinMarkerValue()); assertEquals(0, series.getMinMarkerValue());
Assert.assertEquals(5000, series.getMaxMarkerValue()); assertEquals(5000, series.getMaxMarkerValue());
} }
@Test @Test
@@ -97,8 +100,8 @@ public class ChartValueSeriesTest {
series.update(new ChartPoint(220)); series.update(new ChartPoint(220));
series.update(new ChartPoint(250)); series.update(new ChartPoint(250));
series.updateDimension(); series.updateDimension();
Assert.assertEquals(100, series.getInterval()); assertEquals(100, series.getInterval());
Assert.assertEquals(200, series.getMinMarkerValue()); assertEquals(200, series.getMinMarkerValue());
Assert.assertEquals(700, series.getMaxMarkerValue()); assertEquals(700, series.getMaxMarkerValue());
} }
} }
@@ -21,7 +21,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -31,6 +30,8 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link DescriptionGenerator}. * Tests for {@link DescriptionGenerator}.
* *
@@ -85,7 +86,7 @@ public class DescriptionGeneratorTest {
+ "Elevation gain: 6000 m (19685 ft)<br>" + "Elevation gain: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>"; + "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true)); assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
} }
@Test @Test
@@ -119,7 +120,7 @@ public class DescriptionGeneratorTest {
+ "Elevation gain: 6000 m (19685 ft)<br>" + "Elevation gain: 6000 m (19685 ft)<br>"
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>"; + "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true)); assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
} }
@@ -130,7 +131,7 @@ public class DescriptionGeneratorTest {
public void testWriteDistance() { public void testWriteDistance() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>"); descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
Assert.assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString()); assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
} }
/** /**
@@ -140,7 +141,7 @@ public class DescriptionGeneratorTest {
public void testWriteTime() { public void testWriteTime() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>"); descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
Assert.assertEquals("Total time: 00:01<br>", builder.toString()); assertEquals("Total time: 00:01<br>", builder.toString());
} }
/** /**
@@ -150,7 +151,7 @@ public class DescriptionGeneratorTest {
public void testWriteSpeed() { public void testWriteSpeed() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n"); descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
Assert.assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString()); assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
} }
/** /**
@@ -160,7 +161,7 @@ public class DescriptionGeneratorTest {
public void testWriteElevation() { public void testWriteElevation() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>"); descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
Assert.assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString()); assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
} }
/** /**
@@ -170,6 +171,6 @@ public class DescriptionGeneratorTest {
public void testWritePace() { public void testWritePace() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n"); 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()); assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
} }
} }
@@ -24,7 +24,6 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.provider.ProviderTestRule; import androidx.test.rule.provider.ProviderTestRule;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -35,13 +34,17 @@ import java.util.List;
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult; import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery; import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider; import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.content.provider.CustomSQLiteOpenHelper; import de.dennisguse.opentracks.content.provider.CustomSQLiteOpenHelper;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
/** /**
* Tests for {@link SearchEngine}. * Tests for {@link SearchEngine}.
* These are not meant to be quality tests, but instead feature-by-feature tests. * These are not meant to be quality tests, but instead feature-by-feature tests.
@@ -99,36 +102,36 @@ public class SearchEngineTest {
return insertTrack(title, "", "", hoursAgo); return insertTrack(title, "", "", hoursAgo);
} }
private Waypoint.Id insertWaypoint(String title, String description, String category, double distance, long hoursAgo, Track.Id trackId) { private Marker.Id insertWaypoint(String title, String description, String category, double distance, long hoursAgo, Track.Id trackId) {
Location location = new Location(HERE); Location location = new Location(HERE);
location.setLatitude(location.getLatitude() + distance); location.setLatitude(location.getLatitude() + distance);
location.setLongitude(location.getLongitude() + distance); location.setLongitude(location.getLongitude() + distance);
if (hoursAgo >= 0) { if (hoursAgo >= 0) {
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L); location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
} }
Waypoint waypoint = new Waypoint(location); Marker waypoint = new Marker(location);
waypoint.setName(title); waypoint.setName(title);
waypoint.setDescription(description); waypoint.setDescription(description);
waypoint.setCategory(category); waypoint.setCategory(category);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Uri uri = providerUtils.insertWaypoint(waypoint); Uri uri = providerUtils.insertMarker(waypoint);
return new Waypoint.Id(ContentUris.parseId(uri)); return new Marker.Id(ContentUris.parseId(uri));
} }
private Waypoint.Id insertWaypoint(String title, String description, String category) { private Marker.Id insertWaypoint(String title, String description, String category) {
return insertWaypoint(title, description, category, 0.0, -1, null); return insertWaypoint(title, description, category, 0.0, -1, null);
} }
private Waypoint.Id insertWaypoint(String title, double distance) { private Marker.Id insertWaypoint(String title, double distance) {
return insertWaypoint(title, "", "", distance, -1, null); return insertWaypoint(title, "", "", distance, -1, null);
} }
private Waypoint.Id insertWaypoint(String title, long hoursAgo) { private Marker.Id insertWaypoint(String title, long hoursAgo) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, null); return insertWaypoint(title, "", "", 0.0, hoursAgo, null);
} }
private Waypoint.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) { private Marker.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId); return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
} }
@@ -168,12 +171,12 @@ public class SearchEngineTest {
// - one which will match in title and description // - one which will match in title and description
// - one which will match in all fields // - one which will match in all fields
insertWaypoint("bb", "cc", "dd"); insertWaypoint("bb", "cc", "dd");
Waypoint.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc"); Marker.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc");
Waypoint.Id categoryMatchId = insertWaypoint("bb", "cc", "aa"); Marker.Id categoryMatchId = insertWaypoint("bb", "cc", "aa");
Waypoint.Id titleMatchId = insertWaypoint("aa", "bb", "cc"); Marker.Id titleMatchId = insertWaypoint("aa", "bb", "cc");
Waypoint.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca"); Marker.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
Waypoint.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc"); Marker.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
Waypoint.Id allMatchId = insertWaypoint("aa", "ba", "ca"); Marker.Id allMatchId = insertWaypoint("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, null, NOW); SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query)); ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
@@ -190,9 +193,9 @@ public class SearchEngineTest {
// - one waypoint which will match by title // - one waypoint which will match by title
// - one track which won't match // - one track which won't match
// - one track which will match by title // - one track which will match by title
Waypoint.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc"); Marker.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
insertWaypoint("bb", "cc", "dd"); insertWaypoint("bb", "cc", "dd");
Waypoint.Id titleWaypointId = insertWaypoint("aa", "bb", "cc"); Marker.Id titleWaypointId = insertWaypoint("aa", "bb", "cc");
insertTrack("bb", "cc", "dd"); insertTrack("bb", "cc", "dd");
Track.Id trackId = insertTrack("aa", "bb", "cc"); Track.Id trackId = insertTrack("aa", "bb", "cc");
@@ -200,7 +203,7 @@ public class SearchEngineTest {
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query)); ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
// Title > Description > Category. // Title > Description > Category.
Assert.assertEquals(results.toString(), 3, results.size()); assertEquals(results.toString(), 3, results.size());
assertTrackResult(trackId, results.get(0)); assertTrackResult(trackId, results.get(0));
assertWaypointResult(titleWaypointId, results.get(1)); assertWaypointResult(titleWaypointId, results.get(1));
assertWaypointResult(descriptionWaypointId, results.get(2)); assertWaypointResult(descriptionWaypointId, results.get(2));
@@ -209,9 +212,9 @@ public class SearchEngineTest {
@Test @Test
public void testSearchWaypointDistance() { public void testSearchWaypointDistance() {
// All results match text, but they're at difference distances from the user. // All results match text, but they're at difference distances from the user.
Waypoint.Id farFarAwayId = insertWaypoint("aa", 0.3); Marker.Id farFarAwayId = insertWaypoint("aa", 0.3);
Waypoint.Id nearId = insertWaypoint("ab", 0.1); Marker.Id nearId = insertWaypoint("ab", 0.1);
Waypoint.Id farId = insertWaypoint("ac", 0.2); Marker.Id farId = insertWaypoint("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, null, NOW); SearchQuery query = new SearchQuery("a", HERE, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query)); ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
@@ -236,9 +239,9 @@ public class SearchEngineTest {
@Test @Test
public void testSearchWaypointRecent() { public void testSearchWaypointRecent() {
// All results match text, but they're were recorded at different times. // All results match text, but they're were recorded at different times.
Waypoint.Id oldestId = insertWaypoint("aa", 2); Marker.Id oldestId = insertWaypoint("aa", 2);
Waypoint.Id recentId = insertWaypoint("ab", 0); Marker.Id recentId = insertWaypoint("ab", 0);
Waypoint.Id oldId = insertWaypoint("ac", 1); Marker.Id oldId = insertWaypoint("ac", 1);
SearchQuery query = new SearchQuery("a", null, null, NOW); SearchQuery query = new SearchQuery("a", null, null, NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query)); ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
@@ -263,8 +266,8 @@ public class SearchEngineTest {
@Test @Test
public void testSearchCurrentTrackWaypoint() { public void testSearchCurrentTrackWaypoint() {
// All results match text, but one of them is in the current track. // All results match text, but one of them is in the current track.
Waypoint.Id otherId = insertWaypoint("aa", 1, new Track.Id(456)); Marker.Id otherId = insertWaypoint("aa", 1, new Track.Id(456));
Waypoint.Id currentId = insertWaypoint("ab", 1, new Track.Id(123)); Marker.Id currentId = insertWaypoint("ab", 1, new Track.Id(123));
SearchQuery query = new SearchQuery("a", null, new Track.Id(123), NOW); SearchQuery query = new SearchQuery("a", null, new Track.Id(123), NOW);
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query)); ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
@@ -274,9 +277,9 @@ public class SearchEngineTest {
} }
private void assertTrackResult(Track.Id trackId, ScoredResult result) { private void assertTrackResult(Track.Id trackId, ScoredResult result) {
Assert.assertNotNull("Not a track", result.track); assertNotNull("Not a track", result.track);
Assert.assertNull("Ambiguous result", result.waypoint); assertNull("Ambiguous result", result.marker);
Assert.assertEquals(trackId, result.track.getId()); assertEquals(trackId, result.track.getId());
} }
private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) { private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) {
@@ -286,34 +289,34 @@ public class SearchEngineTest {
} }
String errMsg = "Expected IDs=" + Arrays.toString(longTrackIds) + "; results=" + results; String errMsg = "Expected IDs=" + Arrays.toString(longTrackIds) + "; results=" + results;
Assert.assertEquals(results.size(), trackIds.length); assertEquals(results.size(), trackIds.length);
for (int i = 0; i < results.size(); i++) { for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i); ScoredResult result = results.get(i);
Assert.assertNotNull(errMsg, result.track); assertNotNull(errMsg, result.track);
Assert.assertNull(errMsg, result.waypoint); assertNull(errMsg, result.marker);
Assert.assertEquals(errMsg, trackIds[i], result.track.getId()); assertEquals(errMsg, trackIds[i], result.track.getId());
} }
} }
private void assertWaypointResult(Waypoint.Id waypointId, ScoredResult result) { private void assertWaypointResult(Marker.Id waypointId, ScoredResult result) {
Assert.assertNotNull("Not a waypoint", result.waypoint); assertNotNull("Not a waypoint", result.marker);
Assert.assertNull("Ambiguous result", result.track); assertNull("Ambiguous result", result.track);
Assert.assertEquals(waypointId, result.waypoint.getId()); assertEquals(waypointId, result.marker.getId());
} }
private void assertWaypointResults(List<ScoredResult> results, Waypoint.Id... waypointIds) { private void assertWaypointResults(List<ScoredResult> results, Marker.Id... waypointIds) {
long[] longWaypointIds = new long[waypointIds.length]; long[] longWaypointIds = new long[waypointIds.length];
for (int i = 0; i < waypointIds.length; i++) { for (int i = 0; i < waypointIds.length; i++) {
longWaypointIds[i] = waypointIds[i].getId(); longWaypointIds[i] = waypointIds[i].getId();
} }
String errMsg = "Expected IDs=" + Arrays.toString(longWaypointIds) + "; results=" + results; String errMsg = "Expected IDs=" + Arrays.toString(longWaypointIds) + "; results=" + results;
Assert.assertEquals(results.size(), waypointIds.length); assertEquals(results.size(), waypointIds.length);
for (int i = 0; i < results.size(); i++) { for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i); ScoredResult result = results.get(i);
Assert.assertNotNull(errMsg, result.waypoint); assertNotNull(errMsg, result.marker);
Assert.assertNull(errMsg, result.track); assertNull(errMsg, result.track);
Assert.assertEquals(errMsg, waypointIds[i], result.waypoint.getId()); assertEquals(errMsg, waypointIds[i], result.marker.getId());
} }
} }
} }
@@ -68,9 +68,9 @@ public class TestDataUtil {
trackPoint.setSpeed(5f + (i / 10)); trackPoint.setSpeed(5f + (i / 10));
trackPoint.setHeartRate_bpm(100f + i); trackPoint.setHeartRate_bpm(100f + i);
trackPoint.setCyclingCadence_rpm(200f + i);
trackPoint.setCyclingCadence_rpm(300f + i); trackPoint.setCyclingCadence_rpm(300f + i);
trackPoint.setPower(400f + i); trackPoint.setPower(400f + i);
trackPoint.setElevationGain(500f + i);
return trackPoint; return trackPoint;
} }
@@ -93,12 +93,12 @@ public class TestDataUtil {
* @param location The location. * @param location The location.
* @return the Waypoint created. * @return the Waypoint created.
*/ */
public static Waypoint createWaypointWithPhoto(Context context, Track.Id trackId, Location location) throws IOException { public static Marker createWaypointWithPhoto(Context context, Track.Id trackId, Location location) throws IOException {
File dstFile = new File(FileUtils.getImageUrl(context, trackId)); File dstFile = new File(FileUtils.getImageUrl(context, trackId));
dstFile.createNewFile(); dstFile.createNewFile();
Uri photoUri = FileUtils.getUriForFile(context, dstFile); Uri photoUri = FileUtils.getUriForFile(context, dstFile);
String photoUrl = photoUri.toString(); String photoUrl = photoUri.toString();
return new Waypoint("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl); return new Marker("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl);
} }
} }
@@ -25,9 +25,9 @@ import androidx.test.core.app.ApplicationProvider;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -67,7 +67,7 @@ public class CustomContentProviderTest {
assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID)); assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build())); assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI)); assertEquals(MarkerColumns.CONTENT_TYPE, customContentProvider.getType(MarkerColumns.CONTENT_URI));
assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build())); assertEquals(MarkerColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), 1).build()));
} }
} }
@@ -25,7 +25,6 @@ import android.util.Pair;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -39,17 +38,22 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; 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.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.UUIDUtils; import de.dennisguse.opentracks.util.UUIDUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@@ -122,13 +126,13 @@ public class CustomContentProviderUtilsTest {
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, -1L, descending)) { try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, -1L, descending)) {
while (it.hasNext()) { while (it.hasNext()) {
TrackPoint loc = it.next(); TrackPoint loc = it.next();
Assert.assertNotNull(loc); assertNotNull(loc);
locations.add(loc); locations.add(loc);
// Make sure the IDs are returned in the right order. // Make sure the IDs are returned in the right order.
Assert.assertEquals(descending ? lastPointId - locations.size() + 1 assertEquals(descending ? lastPointId - locations.size() + 1
: lastPointId - numPoints + locations.size(), it.getTrackPointId()); : lastPointId - numPoints + locations.size(), it.getTrackPointId());
} }
Assert.assertEquals(numPoints, locations.size()); assertEquals(numPoints, locations.size());
} }
} }
@@ -138,7 +142,7 @@ public class CustomContentProviderUtilsTest {
track.setName("Test: " + id.getId()); track.setName("Test: " + id.getId());
contentProviderUtils.insertTrack(track); contentProviderUtils.insertTrack(track);
track = contentProviderUtils.getTrack(id); track = contentProviderUtils.getTrack(id);
Assert.assertNotNull(track); assertNotNull(track);
TrackPoint[] trackPoints = new TrackPoint[numPoints]; TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
@@ -162,8 +166,8 @@ public class CustomContentProviderUtilsTest {
} }
} }
Assert.assertTrue(numPoints == 0 || lastPointId > 0); assertTrue(numPoints == 0 || lastPointId > 0);
Assert.assertEquals(numPoints, counter); assertEquals(numPoints, counter);
return lastPointId; return lastPointId;
} }
@@ -195,8 +199,8 @@ public class CustomContentProviderUtilsTest {
when(cursorMock.getString(columnIndex)).thenReturn(name); when(cursorMock.getString(columnIndex)).thenReturn(name);
Track track = contentProviderUtils.createTrack(cursorMock); Track track = contentProviderUtils.createTrack(cursorMock);
Assert.assertEquals(trackId, track.getId()); assertEquals(trackId, track.getId());
Assert.assertEquals(name, track.getName()); assertEquals(name, track.getName());
} }
/** /**
@@ -208,25 +212,25 @@ public class CustomContentProviderUtilsTest {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
contentProviderUtils.insertWaypoint(waypoint); contentProviderUtils.insertMarker(waypoint);
ContentResolver contentResolver = context.getContentResolver(); ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(1, tracksCursor.getCount()); assertEquals(1, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(10, tracksPointsCursor.getCount()); assertEquals(10, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount()); assertEquals(1, waypointCursor.getCount());
// Delete all. // Delete all.
contentProviderUtils.deleteAllTracks(context); contentProviderUtils.deleteAllTracks(context);
// Check whether all have been deleted. // Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(0, tracksCursor.getCount()); assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(0, tracksPointsCursor.getCount()); assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount()); assertEquals(0, waypointCursor.getCount());
} }
/** /**
@@ -239,32 +243,32 @@ public class CustomContentProviderUtilsTest {
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId); TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
contentProviderUtils.insertWaypoint(waypoint); contentProviderUtils.insertMarker(waypoint);
ContentResolver contentResolver = context.getContentResolver(); ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(1, tracksCursor.getCount()); assertEquals(1, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(10, tracksPointsCursor.getCount()); assertEquals(10, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount()); assertEquals(1, waypointCursor.getCount());
// Check waypoint has photo and it's in the external storage. // Check waypoint has photo and it's in the external storage.
Assert.assertTrue(waypoint.hasPhoto()); assertTrue(waypoint.hasPhoto());
File dir = FileUtils.getPhotoDir(context, trackId); File dir = FileUtils.getPhotoDir(context, trackId);
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
// Delete all. // Delete all.
contentProviderUtils.deleteAllTracks(context); contentProviderUtils.deleteAllTracks(context);
// Check whether all have been deleted. // Check whether all have been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(0, tracksCursor.getCount()); assertEquals(0, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(0, tracksPointsCursor.getCount()); assertEquals(0, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount()); assertEquals(0, waypointCursor.getCount());
Assert.assertFalse(dir.exists()); assertFalse(dir.exists());
} }
/** /**
@@ -283,26 +287,26 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId2)); Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId2));
waypoint.setTrackId(trackId1); waypoint.setTrackId(trackId1);
contentProviderUtils.insertWaypoint(waypoint); contentProviderUtils.insertMarker(waypoint);
ContentResolver contentResolver = context.getContentResolver(); ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(3, tracksCursor.getCount()); assertEquals(3, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount()); assertEquals(20, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount()); assertEquals(1, waypointCursor.getCount());
// Delete one track. // Delete one track.
contentProviderUtils.deleteTrack(context, trackId1); contentProviderUtils.deleteTrack(context, trackId1);
// Check whether all data of a track has been deleted. // Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount()); assertEquals(2, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount()); assertEquals(20, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(0, waypointCursor.getCount()); assertEquals(0, waypointCursor.getCount());
} }
/** /**
@@ -322,41 +326,41 @@ public class CustomContentProviderUtilsTest {
// Insert a waypoint in tracks trackId and trackId + 1. // Insert a waypoint in tracks trackId and trackId + 1.
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1); TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId1, trackPoint1.getLocation()); Marker waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId1, trackPoint1.getLocation());
contentProviderUtils.insertWaypoint(waypoint1); contentProviderUtils.insertMarker(waypoint1);
File dir1 = FileUtils.getPhotoDir(context, trackId1); File dir1 = FileUtils.getPhotoDir(context, trackId1);
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2); TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
Waypoint waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId2, trackPoint2.getLocation()); Marker waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId2, trackPoint2.getLocation());
contentProviderUtils.insertWaypoint(waypoint2); contentProviderUtils.insertMarker(waypoint2);
File dir2 = FileUtils.getPhotoDir(context, trackId2); File dir2 = FileUtils.getPhotoDir(context, trackId2);
// Check. // Check.
ContentResolver contentResolver = context.getContentResolver(); ContentResolver contentResolver = context.getContentResolver();
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(3, tracksCursor.getCount()); assertEquals(3, tracksCursor.getCount());
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(30, tracksPointsCursor.getCount()); assertEquals(30, tracksPointsCursor.getCount());
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(2, waypointCursor.getCount()); assertEquals(2, waypointCursor.getCount());
Assert.assertTrue(waypoint1.hasPhoto()); assertTrue(waypoint1.hasPhoto());
Assert.assertTrue(dir1.isDirectory()); assertTrue(dir1.isDirectory());
Assert.assertEquals(1, dir1.list().length); assertEquals(1, dir1.list().length);
Assert.assertTrue(dir1.exists()); assertTrue(dir1.exists());
Assert.assertTrue(dir2.isDirectory()); assertTrue(dir2.isDirectory());
Assert.assertEquals(1, dir2.list().length); assertEquals(1, dir2.list().length);
Assert.assertTrue(dir2.exists()); assertTrue(dir2.exists());
// Delete one track. // Delete one track.
contentProviderUtils.deleteTrack(context, trackId1); contentProviderUtils.deleteTrack(context, trackId1);
// Check whether all data of a track has been deleted. // Check whether all data of a track has been deleted.
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID); tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
Assert.assertEquals(2, tracksCursor.getCount()); assertEquals(2, tracksCursor.getCount());
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID); tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
Assert.assertEquals(20, tracksPointsCursor.getCount()); assertEquals(20, tracksPointsCursor.getCount());
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID); waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
Assert.assertEquals(1, waypointCursor.getCount()); assertEquals(1, waypointCursor.getCount());
Assert.assertFalse(dir1.exists()); assertFalse(dir1.exists());
Assert.assertTrue(dir2.exists()); assertTrue(dir2.exists());
} }
/** /**
@@ -373,8 +377,8 @@ public class CustomContentProviderUtilsTest {
List<Track> allTracks = contentProviderUtils.getTracks(); List<Track> allTracks = contentProviderUtils.getTracks();
// then // then
Assert.assertEquals(initialTrackNumber + 1, allTracks.size()); assertEquals(initialTrackNumber + 1, allTracks.size());
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId()); assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
} }
/** /**
@@ -384,7 +388,7 @@ public class CustomContentProviderUtilsTest {
public void testGetLastTrack() { public void testGetLastTrack() {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId)); contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId()); assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
} }
/** /**
@@ -397,7 +401,7 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId)); contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
// when / then // when / then
Assert.assertNotNull(contentProviderUtils.getTrack(trackId)); assertNotNull(contentProviderUtils.getTrack(trackId));
} }
/** /**
@@ -411,7 +415,7 @@ public class CustomContentProviderUtilsTest {
contentProviderUtils.insertTrack(track); contentProviderUtils.insertTrack(track);
// when / then // when / then
Assert.assertNotNull(contentProviderUtils.getTrack(track.getUuid())); assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
} }
/** /**
@@ -428,14 +432,14 @@ public class CustomContentProviderUtilsTest {
// when / then // when / then
contentProviderUtils.insertTrack(track); contentProviderUtils.insertTrack(track);
Assert.assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName()); assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
track.setName(nameNew); track.setName(nameNew);
contentProviderUtils.updateTrack(track); contentProviderUtils.updateTrack(track);
Assert.assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName()); assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
} }
/** /**
* Tests the method {@link ContentProviderUtils#createContentValues(Waypoint)}. * Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
*/ */
@Test @Test
public void testCreateContentValues_waypoint() { public void testCreateContentValues_waypoint() {
@@ -451,7 +455,7 @@ public class CustomContentProviderUtilsTest {
statistics.setTotalTime(1500L); statistics.setTotalTime(1500L);
statistics.setMovingTime(700L); statistics.setMovingTime(700L);
statistics.setTotalDistance(750.0); statistics.setTotalDistance(750.0);
statistics.setTotalElevationGain(50.0); statistics.setTotalElevationGain(50.0f);
statistics.setMaxSpeed(60.0); statistics.setMaxSpeed(60.0);
statistics.setMaxElevation(1250.0); statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0); statistics.setMinElevation(1200.0);
@@ -459,30 +463,30 @@ public class CustomContentProviderUtilsTest {
track.first.setTrackStatistics(statistics); track.first.setTrackStatistics(statistics);
contentProviderUtils.insertTrack(track.first); contentProviderUtils.insertTrack(track.first);
Waypoint waypoint = new Waypoint(track.second[0]); Marker waypoint = new Marker(track.second[0]);
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
contentProviderUtils.insertWaypoint(waypoint); contentProviderUtils.insertMarker(waypoint);
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
Waypoint.Id waypointId = new Waypoint.Id(System.currentTimeMillis()); Marker.Id waypointId = new Marker.Id(System.currentTimeMillis());
waypoint.setId(waypointId); waypoint.setId(waypointId);
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint); ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
Assert.assertEquals(waypointId.getId(), contentValues.get(WaypointsColumns._ID)); assertEquals(waypointId.getId(), contentValues.get(MarkerColumns._ID));
Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE)); assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION)); assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
} }
/** /**
* Tests the method {@link ContentProviderUtils#createWaypoint(Cursor)}. * Tests the method {@link ContentProviderUtils#createMarker(Cursor)}.
*/ */
@Test @Test
public void testCreateWaypoint() { public void testCreateWaypoint() {
int startColumnIndex = 1; int startColumnIndex = 1;
int columnIndex = startColumnIndex; int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)).thenReturn(columnIndex++); when(cursorMock.getColumnIndexOrThrow(MarkerColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).thenReturn(columnIndex++); when(cursorMock.getColumnIndexOrThrow(MarkerColumns.NAME)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).thenReturn(columnIndex++); when(cursorMock.getColumnIndexOrThrow(MarkerColumns.TRACKID)).thenReturn(columnIndex++);
columnIndex = startColumnIndex; columnIndex = startColumnIndex;
// Id // Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false); when(cursorMock.isNull(columnIndex++)).thenReturn(false);
@@ -501,15 +505,15 @@ public class CustomContentProviderUtilsTest {
long trackId = 11L; long trackId = 11L;
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId); when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock); Marker waypoint = contentProviderUtils.createMarker(cursorMock);
Assert.assertEquals(id, waypoint.getId().getId()); assertEquals(id, waypoint.getId().getId());
Assert.assertEquals(name, waypoint.getName()); assertEquals(name, waypoint.getName());
Assert.assertEquals(trackId, waypoint.getTrackId().getId()); assertEquals(trackId, waypoint.getTrackId().getId());
} }
/** /**
* Tests the method * Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)} * {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)}
* when there is only one waypoint in the track. * when there is only one waypoint in the track.
*/ */
@Test @Test
@@ -518,26 +522,26 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first. // Insert at first.
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setDescription(TEST_DESC); waypoint1.setDescription(TEST_DESC);
waypoint1.setTrackId(trackId); waypoint1.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint1); contentProviderUtils.insertMarker(waypoint1);
// Check insert was done. // Check insert was done.
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1); assertEquals(contentProviderUtils.getMarkerCount(trackId), 1);
// Get waypoint id that needs to delete. // Get waypoint id that needs to delete.
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1))); Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
// Delete // Delete
contentProviderUtils.deleteWaypoint(context, waypoint1Id); contentProviderUtils.deleteMarker(context, waypoint1Id);
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id)); assertNull(contentProviderUtils.getMarker(waypoint1Id));
} }
/** /**
* Tests the method * Tests the method
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)} * {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)}
* when there is only one waypoint in the track. * when there is only one waypoint in the track.
*/ */
@Test @Test
@@ -547,32 +551,32 @@ public class CustomContentProviderUtilsTest {
// Insert at first. // Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId); TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
contentProviderUtils.insertWaypoint(waypoint1); contentProviderUtils.insertMarker(waypoint1);
// Check insert was done. // Check insert was done.
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1); assertEquals(contentProviderUtils.getMarkerCount(trackId), 1);
// Get waypoint id that needs to delete. // Get waypoint id that needs to delete.
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1))); Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
// Check waypoint has photo and it's in the external storage. // Check waypoint has photo and it's in the external storage.
Assert.assertTrue(waypoint1.hasPhoto()); assertTrue(waypoint1.hasPhoto());
File dir = FileUtils.getPhotoDir(context, trackId); File dir = FileUtils.getPhotoDir(context, trackId);
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
// Delete // Delete
contentProviderUtils.deleteWaypoint(context, waypoint1Id); contentProviderUtils.deleteMarker(context, waypoint1Id);
// Check waypoint doesn't exists and photo folder was deleted. // Check waypoint doesn't exists and photo folder was deleted.
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id)); assertNull(contentProviderUtils.getMarker(waypoint1Id));
Assert.assertFalse(dir.exists()); assertFalse(dir.exists());
} }
/** /**
* Tests the method {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)} when there is more than one waypoint in the track. * Tests the method {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)} when there is more than one waypoint in the track.
*/ */
@Test @Test
public void testDeleteWaypoint_hasNextWayPoint() { public void testDeleteWaypoint_hasNextWayPoint() {
@@ -596,67 +600,67 @@ public class CustomContentProviderUtilsTest {
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track); // TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
// Insert at first. // Insert at first.
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setDescription(MOCK_DESC); waypoint1.setDescription(MOCK_DESC);
waypoint1.setTrackId(trackId); waypoint1.setTrackId(trackId);
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1))); Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint2 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint2.setDescription(MOCK_DESC); waypoint2.setDescription(MOCK_DESC);
waypoint2.setTrackId(trackId); waypoint2.setTrackId(trackId);
Waypoint.Id waypoint2Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2))); Marker.Id waypoint2Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint2)));
// Delete // Delete
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id)); assertNotNull(contentProviderUtils.getMarker(waypoint1Id));
contentProviderUtils.deleteWaypoint(context, waypoint1Id); contentProviderUtils.deleteMarker(context, waypoint1Id);
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id)); assertNull(contentProviderUtils.getMarker(waypoint1Id));
Assert.assertEquals(MOCK_DESC, contentProviderUtils.getWaypoint(waypoint2Id).getDescription()); assertEquals(MOCK_DESC, contentProviderUtils.getMarker(waypoint2Id).getDescription());
} }
/** /**
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(Track.Id)}. * Tests the method {@link ContentProviderUtils#getNextMarkerNumber(Track.Id)}.
*/ */
@Test @Test
public void testGetNextWaypointNumber() { public void testGetNextWaypointNumber() {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint1 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint1.setTrackId(trackId); waypoint1.setTrackId(trackId);
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint2 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint2.setTrackId(trackId); waypoint2.setTrackId(trackId);
Waypoint waypoint3 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint3 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint3.setTrackId(trackId); waypoint3.setTrackId(trackId);
Waypoint waypoint4 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint4 = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint4.setTrackId(trackId); waypoint4.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint1); contentProviderUtils.insertMarker(waypoint1);
contentProviderUtils.insertWaypoint(waypoint2); contentProviderUtils.insertMarker(waypoint2);
contentProviderUtils.insertWaypoint(waypoint3); contentProviderUtils.insertMarker(waypoint3);
contentProviderUtils.insertWaypoint(waypoint4); contentProviderUtils.insertMarker(waypoint4);
Assert.assertEquals(4, contentProviderUtils.getNextWaypointNumber(trackId)); assertEquals(4, contentProviderUtils.getNextMarkerNumber(trackId));
} }
/** /**
* Tests the method {@link ContentProviderUtils#insertWaypoint(Waypoint)} and * Tests the method {@link ContentProviderUtils#insertMarker(Marker)} and
* {@link ContentProviderUtils#getWaypoint(Waypoint.Id)}. * {@link ContentProviderUtils#getMarker(Marker.Id)}.
*/ */
@Test @Test
public void testInsertAndGetWaypoint() { public void testInsertAndGetWaypoint() {
Track.Id trackId = new Track.Id(System.currentTimeMillis()); Track.Id trackId = new Track.Id(System.currentTimeMillis());
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription()); assertEquals(TEST_DESC, contentProviderUtils.getMarker(waypointId).getDescription());
} }
/** /**
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}. * Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
*/ */
@Test @Test
public void testUpdateWaypoint() { public void testUpdateWaypoint() {
@@ -664,21 +668,21 @@ public class CustomContentProviderUtilsTest {
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
// Insert at first. // Insert at first.
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId)); Marker waypoint = new Marker(contentProviderUtils.getLastValidTrackPoint(trackId));
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
// Update // Update
waypoint = contentProviderUtils.getWaypoint(waypointId); waypoint = contentProviderUtils.getMarker(waypointId);
waypoint.setDescription(TEST_DESC_NEW); waypoint.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateWaypoint(context, waypoint); contentProviderUtils.updateMarker(context, waypoint);
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
} }
/** /**
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}. * Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
*/ */
@Test @Test
public void testUpdateWaypoint_withPhoto() throws IOException { public void testUpdateWaypoint_withPhoto() throws IOException {
@@ -689,32 +693,32 @@ public class CustomContentProviderUtilsTest {
// Insert at first. // Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId); TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId()); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
// Update // Update
waypoint = contentProviderUtils.getWaypoint(waypointId); waypoint = contentProviderUtils.getMarker(waypointId);
waypoint.setName(TEST_NAME_NEW); waypoint.setName(TEST_NAME_NEW);
waypoint.setDescription(TEST_DESC_NEW); waypoint.setDescription(TEST_DESC_NEW);
contentProviderUtils.updateWaypoint(context, waypoint); contentProviderUtils.updateMarker(context, waypoint);
Assert.assertEquals(TEST_NAME_NEW, contentProviderUtils.getWaypoint(waypointId).getName()); assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(waypointId).getName());
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
Assert.assertTrue(waypoint.hasPhoto()); assertTrue(waypoint.hasPhoto());
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
} }
/** /**
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}. * Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
*/ */
@Test @Test
public void testUpdateWaypoint_delPhotoAndDir() throws IOException { public void testUpdateWaypoint_delPhotoAndDir() throws IOException {
@@ -725,31 +729,31 @@ public class CustomContentProviderUtilsTest {
// Insert at first. // Insert at first.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId); TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId()); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
// Update // Update
waypoint = contentProviderUtils.getWaypoint(waypointId); waypoint = contentProviderUtils.getMarker(waypointId);
waypoint.setName(TEST_NAME_NEW); waypoint.setName(TEST_NAME_NEW);
waypoint.setDescription(TEST_DESC_NEW); waypoint.setDescription(TEST_DESC_NEW);
waypoint.setPhotoUrl(null); waypoint.setPhotoUrl(null);
contentProviderUtils.updateWaypoint(context, waypoint); contentProviderUtils.updateMarker(context, waypoint);
Assert.assertEquals(TEST_NAME_NEW, contentProviderUtils.getWaypoint(waypointId).getName()); assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(waypointId).getName());
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription()); assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
Assert.assertFalse(waypoint.hasPhoto()); assertFalse(waypoint.hasPhoto());
Assert.assertFalse(dir.exists()); assertFalse(dir.exists());
} }
/** /**
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}. * Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
*/ */
@Test @Test
public void testUpdateWaypoint_delPhotoNotDir() throws IOException { public void testUpdateWaypoint_delPhotoNotDir() throws IOException {
@@ -760,30 +764,30 @@ public class CustomContentProviderUtilsTest {
// Insert two waypoints with photos. // Insert two waypoints with photos.
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId); TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
waypoint.setDescription(TEST_DESC); waypoint.setDescription(TEST_DESC);
waypoint.setTrackId(trackId); waypoint.setTrackId(trackId);
Waypoint otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation()); Marker otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
otherWaypoint.setDescription(TEST_DESC); otherWaypoint.setDescription(TEST_DESC);
otherWaypoint.setTrackId(trackId); otherWaypoint.setTrackId(trackId);
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint))); Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
contentProviderUtils.insertWaypoint(otherWaypoint); contentProviderUtils.insertMarker(otherWaypoint);
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId()); File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(2, dir.list().length); assertEquals(2, dir.list().length);
// Update one waypoint deleting photo. // Update one waypoint deleting photo.
waypoint = contentProviderUtils.getWaypoint(waypointId); waypoint = contentProviderUtils.getMarker(waypointId);
waypoint.setPhotoUrl(null); waypoint.setPhotoUrl(null);
contentProviderUtils.updateWaypoint(context, waypoint); contentProviderUtils.updateMarker(context, waypoint);
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription()); assertEquals(TEST_DESC, contentProviderUtils.getMarker(waypointId).getDescription());
Assert.assertFalse(waypoint.hasPhoto()); assertFalse(waypoint.hasPhoto());
Assert.assertTrue(dir.exists()); assertTrue(dir.exists());
Assert.assertTrue(dir.isDirectory()); assertTrue(dir.isDirectory());
Assert.assertEquals(1, dir.list().length); assertEquals(1, dir.list().length);
} }
/** /**
@@ -798,9 +802,9 @@ public class CustomContentProviderUtilsTest {
// when / then // when / then
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId); contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
Assert.assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount()); assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
contentProviderUtils.bulkInsertTrackPoint(Arrays.copyOfRange(track.second, 0, 8), trackId); contentProviderUtils.bulkInsertTrackPoint(Arrays.copyOfRange(track.second, 0, 8), trackId);
Assert.assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount()); assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
} }
/** /**
@@ -839,11 +843,11 @@ public class CustomContentProviderUtilsTest {
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock); TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
// then // then
Assert.assertEquals(longitude, trackPoint.getLongitude(), 0.01); assertEquals(longitude, trackPoint.getLongitude(), 0.01);
Assert.assertEquals(latitude, trackPoint.getLatitude(), 0.01); assertEquals(latitude, trackPoint.getLatitude(), 0.01);
Assert.assertEquals(time, trackPoint.getTime(), 0.01); assertEquals(time, trackPoint.getTime(), 0.01);
Assert.assertEquals(speed, trackPoint.getSpeed(), 0.01); assertEquals(speed, trackPoint.getSpeed(), 0.01);
Assert.assertFalse(trackPoint.hasHeartRate()); assertFalse(trackPoint.hasHeartRate());
} }
/** /**
@@ -857,7 +861,7 @@ public class CustomContentProviderUtilsTest {
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10); Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId); contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
Assert.assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size()); assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
} }
@Test @Test
@@ -876,10 +880,10 @@ public class CustomContentProviderUtilsTest {
// then // then
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertTrue(trackPoints.get(10).hasHeartRate()); assertTrue(trackPoints.get(10).hasHeartRate());
Assert.assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01); assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
Assert.assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01); assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
Assert.assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01); assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
} }
/** /**
@@ -914,7 +918,7 @@ public class CustomContentProviderUtilsTest {
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true); Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
// then // then
Assert.assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount());
} }
/** /**
@@ -936,7 +940,7 @@ public class CustomContentProviderUtilsTest {
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false); Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
// then // then
Assert.assertEquals(2, cursor.getCount()); assertEquals(2, cursor.getCount());
} }
/** /**
@@ -960,12 +964,12 @@ public class CustomContentProviderUtilsTest {
// then // then
for (int i = 0; i < trackpointIds.length; i++) { for (int i = 0; i < trackpointIds.length; i++) {
Assert.assertTrue(trackPointIterator.hasNext()); assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next(); TrackPoint trackPoint = trackPointIterator.next();
Assert.assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId()); assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId());
checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation()); checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation());
} }
Assert.assertFalse(trackPointIterator.hasNext()); assertFalse(trackPointIterator.hasNext());
} }
/** /**
@@ -990,13 +994,13 @@ public class CustomContentProviderUtilsTest {
// then // then
for (int i = 0; i < trackpointIds.length; i++) { for (int i = 0; i < trackpointIds.length; i++) {
Assert.assertTrue(trackPointIterator.hasNext()); assertTrue(trackPointIterator.hasNext());
TrackPoint trackPoint = trackPointIterator.next(); TrackPoint trackPoint = trackPointIterator.next();
Assert.assertEquals(startTrackPointId + i, trackPointIterator.getTrackPointId()); assertEquals(startTrackPointId + i, trackPointIterator.getTrackPointId());
checkLocation(i, trackPoint.getLocation()); checkLocation(i, trackPoint.getLocation());
} }
Assert.assertFalse(trackPointIterator.hasNext()); assertFalse(trackPointIterator.hasNext());
} }
/** /**
@@ -1006,16 +1010,16 @@ public class CustomContentProviderUtilsTest {
* @param location the location to be checked * @param location the location to be checked
*/ */
private void checkLocation(int i, Location location) { private void checkLocation(int i, Location location) {
Assert.assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01); 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); assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01); assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
Assert.assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01); assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
} }
@Test @Test
public void testFormatIdListForUri() { public void testFormatIdListForUri() {
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri()); assertEquals("", ContentProviderUtils.formatIdListForUri());
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12))); assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44))); assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
} }
} }
@@ -18,9 +18,9 @@ import org.junit.runner.RunWith;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -45,7 +45,7 @@ public class CustomSQLiteOpenHelperTest {
@VisibleForTesting @VisibleForTesting
public static Map<String, String> getTableSQL(SQLiteDatabase db) { public static Map<String, String> getTableSQL(SQLiteDatabase db) {
HashMap<String, String> tableSQL = new HashMap<>(); 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")) { try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + MarkerColumns.TABLE_NAME + "')", null, null, null, "name")) {
if (cursor != null) { if (cursor != null) {
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
tableSQL.put(cursor.getString(0), cursor.getString(1)); tableSQL.put(cursor.getString(0), cursor.getString(1));
@@ -106,8 +106,8 @@ public class CustomSQLiteOpenHelperTest {
assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE)); assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX)); assertTrue(hasSqlCreate(db, TrackPointsColumns.CREATE_TABLE_INDEX));
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE)); assertTrue(hasSqlCreate(db, MarkerColumns.CREATE_TABLE));
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX)); assertTrue(hasSqlCreate(db, MarkerColumns.CREATE_TABLE_INDEX));
} catch (Exception e) { } catch (Exception e) {
fail(); fail();
} }
@@ -140,13 +140,13 @@ public class CustomSQLiteOpenHelperTest {
assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME)); assertEquals(tablesByCreate.get(TracksColumns.TABLE_NAME), tableByUpgrade.get(TracksColumns.TABLE_NAME));
assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME)); assertEquals(tablesByCreate.get(TrackPointsColumns.TABLE_NAME), tableByUpgrade.get(TrackPointsColumns.TABLE_NAME));
assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME)); assertEquals(tablesByCreate.get(MarkerColumns.TABLE_NAME), tableByUpgrade.get(MarkerColumns.TABLE_NAME));
// then - verify custom indices // then - verify custom indices
assertEquals(3, indicesByCreate.size()); assertEquals(3, indicesByCreate.size());
assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME)); assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME));
assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME)); assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME));
assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME)); assertEquals(indicesByUpgrade.get(MarkerColumns.TABLE_NAME), indicesByCreate.get(MarkerColumns.TABLE_NAME));
} }
@Test @Test
@@ -3,7 +3,6 @@ package de.dennisguse.opentracks.content.provider;
import android.net.Uri; import android.net.Uri;
import android.util.Pair; import android.util.Pair;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.util.HashSet; import java.util.HashSet;
@@ -12,17 +11,19 @@ import java.util.Set;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import static org.junit.Assert.assertEquals;
public class ShareContentProviderTest { public class ShareContentProviderTest {
@Test @Test
public void testCreateandParseURI_invalid() { public void testCreateAndParseURI_invalid() {
Uri uri = Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/1"); Uri uri = Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/1");
Assert.assertEquals(0, ShareContentProvider.parseURI(uri).size()); assertEquals(0, ShareContentProvider.parseURI(uri).size());
} }
@Test @Test
public void testCreateandParseURI_valid() { public void testCreateAndParseURI_valid() {
Set<Track.Id> trackIds = new HashSet<>(); Set<Track.Id> trackIds = new HashSet<>();
trackIds.add(new Track.Id(1)); trackIds.add(new Track.Id(1));
trackIds.add(new Track.Id(3)); trackIds.add(new Track.Id(3));
@@ -30,7 +31,7 @@ public class ShareContentProviderTest {
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_ONLY_TRACK); Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_ONLY_TRACK);
Assert.assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first)); assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
} }
@Test @Test
@@ -39,6 +40,6 @@ public class ShareContentProviderTest {
trackIds.add(new Track.Id(1)); trackIds.add(new Track.Id(1));
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "../../&1=1", TrackFileFormat.KML_ONLY_TRACK); 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); assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
} }
} }
@@ -2,12 +2,14 @@ package de.dennisguse.opentracks.content.sensor;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.util.UintUtils; import de.dennisguse.opentracks.util.UintUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class SensorDataCyclingTest { public class SensorDataCyclingTest {
@@ -21,7 +23,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01); assertEquals(60, current.getCadence_rpm(), 0.01);
} }
@Test @Test
@@ -34,7 +36,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertEquals(33.53, current.getCadence_rpm(), 0.01); assertEquals(33.53, current.getCadence_rpm(), 0.01);
} }
@Test @Test
@@ -47,7 +49,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertEquals(0, current.getCadence_rpm(), 0.01); assertEquals(0, current.getCadence_rpm(), 0.01);
} }
@@ -61,7 +63,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertFalse(current.hasCadence_rpm()); assertFalse(current.hasCadence_rpm());
} }
@Test @Test
@@ -74,7 +76,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01); assertEquals(60, current.getCadence_rpm(), 0.01);
} }
@Test @Test
@@ -87,7 +89,7 @@ public class SensorDataCyclingTest {
current.compute(previous); current.compute(previous);
// then // then
Assert.assertEquals(60, current.getCadence_rpm(), 0.01); assertEquals(60, current.getCadence_rpm(), 0.01);
} }
@Test @Test
@@ -100,7 +102,7 @@ public class SensorDataCyclingTest {
current.compute(previous, 2150); current.compute(previous, 2150);
// then // then
Assert.assertEquals(1.20, current.getSpeed_mps(), 0.01); assertEquals(1.20, current.getSpeed_mps(), 0.01);
} }
@Test @Test
@@ -113,6 +115,6 @@ public class SensorDataCyclingTest {
current.compute(previous, 2000); current.compute(previous, 2000);
// then // then
Assert.assertEquals(2, current.getSpeed_mps(), 0.01); assertEquals(2, current.getSpeed_mps(), 0.01);
} }
} }
@@ -23,7 +23,6 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@@ -35,6 +34,8 @@ import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
/** /**
* Tests {@link ChartFragment}. * Tests {@link ChartFragment}.
* *
@@ -78,9 +79,9 @@ public class ChartFragmentTest {
ChartPoint point = chartFragment.createPendingPoint(trackPoint); ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then // then
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01); assertEquals(Float.NaN, point.getHeartRate(), 0.01);
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01); assertEquals(Float.NaN, point.getCadence(), 0.01);
Assert.assertEquals(Float.NaN, point.getPower(), 0.01); assertEquals(Float.NaN, point.getPower(), 0.01);
} }
/** /**
@@ -98,9 +99,9 @@ public class ChartFragmentTest {
ChartPoint point = chartFragment.createPendingPoint(trackPoint); ChartPoint point = chartFragment.createPendingPoint(trackPoint);
// then // then
Assert.assertEquals(100.0, point.getHeartRate(), 0.01); assertEquals(100.0, point.getHeartRate(), 0.01);
Assert.assertEquals(101.0, point.getCadence(), 0.01); assertEquals(101.0, point.getCadence(), 0.01);
Assert.assertEquals(102.0, point.getPower(), 0.01); assertEquals(102.0, point.getPower(), 0.01);
} }
/** /**
@@ -112,12 +113,12 @@ public class ChartFragmentTest {
// Resets last location and writes first location. // Resets last location and writes first location.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01); assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second is a same location, just different time. // The second is a same location, just different time.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01); assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use metric. // The third location is a new location, and use metric.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
@@ -129,7 +130,7 @@ public class ChartFragmentTest {
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results); trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
double distance1 = results[0] * UnitConversions.M_TO_KM; double distance1 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01); assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The fourth location is a new location, and use metric. // The fourth location is a new location, and use metric.
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
@@ -140,7 +141,7 @@ public class ChartFragmentTest {
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(), Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results); trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
double distance2 = results[0] * UnitConversions.M_TO_KM; double distance2 = results[0] * UnitConversions.M_TO_KM;
Assert.assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01); assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
} }
/** /**
@@ -156,7 +157,7 @@ public class ChartFragmentTest {
// The first is a same location, just different time. // The first is a same location, just different time.
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01); assertEquals(0.0, point.getTimeOrDistance(), 0.01);
// The second location is a new location, and use imperial. // The second location is a new location, and use imperial.
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -170,7 +171,7 @@ public class ChartFragmentTest {
float[] results = new float[4]; float[] results = new float[4];
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.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; double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1, point.getTimeOrDistance(), 0.01); assertEquals(distance1, point.getTimeOrDistance(), 0.01);
// The third location is a new location, and use imperial. // The third location is a new location, and use imperial.
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
@@ -183,7 +184,7 @@ public class ChartFragmentTest {
*/ */
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.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; double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
Assert.assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01); assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
} }
/** /**
@@ -194,12 +195,12 @@ public class ChartFragmentTest {
chartFragment.setChartByDistance(false); chartFragment.setChartByDistance(false);
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getTimeOrDistance(), 0.01); assertEquals(0.0, point.getTimeOrDistance(), 0.01);
long timeSpan = 222; long timeSpan = 222;
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setTime(trackPoint1.getTime() + timeSpan); trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01); assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
} }
/** /**
@@ -214,7 +215,7 @@ public class ChartFragmentTest {
* Then only one value INITIAL_ALTITUDE in buffer. * Then only one value INITIAL_ALTITUDE in buffer.
*/ */
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01); assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
/* /*
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2. * Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
@@ -222,7 +223,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2); trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01); assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
} }
/** /**
@@ -239,7 +240,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(128.5f); trackPoint1.setSpeed(128.5f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01); assertEquals(0.0, point.getSpeed(), 0.01);
/* /*
* Tests the logic when both metricUnits and reportSpeed are true. * Tests the logic when both metricUnits and reportSpeed are true.
@@ -253,7 +254,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint1.getTime() + 222); trackPoint2.setTime(trackPoint1.getTime() + 222);
trackPoint2.setSpeed(130); trackPoint2.setSpeed(130);
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01); assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
} }
/** /**
@@ -267,7 +268,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f); trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01); assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -278,7 +279,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint2.getTime() + 222); trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102); trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01); assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
} }
/** /**
@@ -292,7 +293,7 @@ public class ChartFragmentTest {
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
trackPoint1.setSpeed(100.0f); trackPoint1.setSpeed(100.0f);
ChartPoint point = chartFragment.createPendingPoint(trackPoint1); ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
Assert.assertEquals(0.0, point.getSpeed(), 0.01); assertEquals(0.0, point.getSpeed(), 0.01);
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
@@ -303,7 +304,7 @@ public class ChartFragmentTest {
trackPoint2.setTime(trackPoint2.getTime() + 222); trackPoint2.setTime(trackPoint2.getTime() + 222);
trackPoint2.setSpeed(102); trackPoint2.setSpeed(102);
point = chartFragment.createPendingPoint(trackPoint2); point = chartFragment.createPendingPoint(trackPoint2);
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01); assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
} }
/** /**
@@ -315,6 +316,6 @@ public class ChartFragmentTest {
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint(); TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
trackPoint.setSpeed(0); trackPoint.setSpeed(0);
ChartPoint point = chartFragment.createPendingPoint(trackPoint); ChartPoint point = chartFragment.createPendingPoint(trackPoint);
Assert.assertEquals(0.0, point.getPace(), 0.01); assertEquals(0.0, point.getPace(), 0.01);
} }
} }
@@ -24,7 +24,6 @@ import android.net.Uri;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
@@ -34,11 +33,12 @@ import java.util.Locale;
import java.util.SimpleTimeZone; import java.util.SimpleTimeZone;
import java.util.TimeZone; import java.util.TimeZone;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -70,7 +70,7 @@ public abstract class AbstractTestFileTrackImporter {
static final long TRACK_POINT_ID_3 = 4; static final long TRACK_POINT_ID_3 = 4;
private static final long WAYPOINT_ID_0 = 1; private static final long WAYPOINT_ID_0 = 1;
private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build(); private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
protected final Context context = ApplicationProvider.getApplicationContext(); protected final Context context = ApplicationProvider.getApplicationContext();
@@ -114,7 +114,7 @@ public abstract class AbstractTestFileTrackImporter {
protected void expectTrackUpdate(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, Track.Id trackId) { protected void expectTrackUpdate(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, Track.Id trackId) {
contentProviderUtils.updateTrack(trackCaptor.capture()); contentProviderUtils.updateTrack(trackCaptor.capture());
when(contentProviderUtils.insertWaypoint(any())).thenReturn(WAYPOINT_ID_O_URI); when(contentProviderUtils.insertMarker(any())).thenReturn(WAYPOINT_ID_O_URI);
if (lastTrack) { if (lastTrack) {
// Return null to not add waypoints // Return null to not add waypoints
when(contentProviderUtils.getTrack(trackId)).thenReturn(null); when(contentProviderUtils.getTrack(trackId)).thenReturn(null);
@@ -122,10 +122,10 @@ public abstract class AbstractTestFileTrackImporter {
} }
protected void verifyTrack(Track track, String name, String description, long time) { protected void verifyTrack(Track track, String name, String description, long time) {
Assert.assertEquals(name, track.getName()); assertEquals(name, track.getName());
Assert.assertEquals(description, track.getDescription()); assertEquals(description, track.getDescription());
if (time != -1L) { if (time != -1L) {
Assert.assertEquals(time, track.getTrackStatistics().getStartTime_ms()); assertEquals(time, track.getTrackStatistics().getStartTime_ms());
} }
} }
} }
@@ -21,10 +21,10 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter; import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
@@ -53,7 +53,7 @@ public class ExportImportTest {
private static final String TRACK_CATEGORY = "the category"; private static final String TRACK_CATEGORY = "the category";
private static final String TRACK_DESCRIPTION = "the description"; private static final String TRACK_DESCRIPTION = "the description";
private final List<Waypoint> waypoints = new ArrayList<>(); private final List<Marker> markers = new ArrayList<>();
private final List<TrackPoint> trackPoints = new ArrayList<>(); private final List<TrackPoint> trackPoints = new ArrayList<>();
private Track.Id importTrackId; private Track.Id importTrackId;
@@ -72,19 +72,19 @@ public class ExportImportTest {
trackPoints.addAll(Arrays.asList(track.second)); trackPoints.addAll(Arrays.asList(track.second));
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
Waypoint waypoint = new Waypoint(track.second[i].getLocation()); Marker marker = new Marker(track.second[i].getLocation());
waypoint.setName("the waypoint " + i); marker.setName("the marker " + i);
waypoint.setDescription("the waypoint description " + i); marker.setDescription("the marker description " + i);
waypoint.setCategory("the waypoint category" + i); marker.setCategory("the marker category" + i);
waypoint.setIcon("the waypoing icon" + i); marker.setIcon("the marker icon" + i);
waypoint.setPhotoUrl("the photo url" + i); marker.setPhotoUrl("the photo url" + i);
waypoint.setTrackId(trackId); marker.setTrackId(trackId);
contentProviderUtils.insertWaypoint(waypoint); contentProviderUtils.insertMarker(marker);
waypoints.add(waypoint); markers.add(marker);
} }
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(trackId)); assertEquals(markers.size(), contentProviderUtils.getMarkerCount(trackId));
} }
@After @After
@@ -129,11 +129,11 @@ public class ExportImportTest {
assertEquals(track.getIcon(), importedTrack.getIcon()); assertEquals(track.getIcon(), importedTrack.getIcon());
assertEquals(track.getUuid(), importedTrack.getUuid()); assertEquals(track.getUuid(), importedTrack.getUuid());
// 2. waypoints // 2. markers
assertWaypoints(); assertMarkers();
// 3. trackpoints // 3. trackpoints
assertTrackpoints(false, false, false); assertTrackpoints(false, false, false, false);
} }
@LargeTest @LargeTest
@@ -164,11 +164,11 @@ public class ExportImportTest {
assertEquals(track.getName(), importedTrack.getName()); assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.getIcon(), importedTrack.getIcon()); assertEquals(track.getIcon(), importedTrack.getIcon());
// 2. waypoints // 2. markers
assertWaypoints(); assertMarkers();
// 3. trackpoints // 3. trackpoints
assertTrackpoints(true, true, true); assertTrackpoints(true, true, true, true);
} }
@LargeTest @LargeTest
@@ -253,11 +253,11 @@ public class ExportImportTest {
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter. //TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
//assertEquals(track.getIcon(), trackImported.getIcon()); //assertEquals(track.getIcon(), trackImported.getIcon());
// 2. waypoints // 2. markers
assertWaypoints(); assertMarkers();
// 3. trackpoints // 3. trackpoints
assertTrackpoints(false, true, true); assertTrackpoints(false, true, true, false);
} }
@LargeTest @LargeTest
@@ -285,26 +285,26 @@ public class ExportImportTest {
assertNull(trackImported); assertNull(trackImported);
} }
private void assertWaypoints() { private void assertMarkers() {
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(importTrackId)); assertEquals(markers.size(), contentProviderUtils.getMarkerCount(importTrackId));
List<Waypoint> importedWaypoints = contentProviderUtils.getWaypoints(importTrackId); List<Marker> importedMarkers = contentProviderUtils.getMarkers(importTrackId);
for (int i = 0; i < waypoints.size(); i++) { for (int i = 0; i < markers.size(); i++) {
Waypoint waypoint = waypoints.get(i); Marker marker = markers.get(i);
Waypoint importedWaypoint = importedWaypoints.get(i); Marker importMarker = importedMarkers.get(i);
assertEquals(waypoint.getCategory(), importedWaypoint.getCategory()); assertEquals(marker.getCategory(), importMarker.getCategory());
assertEquals(waypoint.getDescription(), importedWaypoint.getDescription()); assertEquals(marker.getDescription(), importMarker.getDescription());
// assertEquals(waypoint.getIcon(), importedWaypoint.getIcon()); // TODO for KML // assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
assertEquals(waypoint.getName(), importedWaypoint.getName()); assertEquals(marker.getName(), importMarker.getName());
assertEquals("", importedWaypoint.getPhotoUrl()); assertEquals("", importMarker.getPhotoUrl());
assertEquals(waypoint.getLocation().getLatitude(), importedWaypoint.getLocation().getLatitude(), 0.001); assertEquals(marker.getLocation().getLatitude(), importMarker.getLocation().getLatitude(), 0.001);
assertEquals(waypoint.getLocation().getLongitude(), importedWaypoint.getLocation().getLongitude(), 0.001); assertEquals(marker.getLocation().getLongitude(), importMarker.getLocation().getLongitude(), 0.001);
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001); assertEquals(marker.getLocation().getAltitude(), importMarker.getLocation().getAltitude(), 0.001);
} }
} }
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) { private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain) {
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId); List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
assertEquals(trackPoints.size(), importedTrackPoints.size()); assertEquals(trackPoints.size(), importedTrackPoints.size());
@@ -330,6 +330,9 @@ public class ExportImportTest {
if (verifyPower) { if (verifyPower) {
assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01); assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
} }
if (verifyElevationGain) {
assertEquals(trackPoint.getElevationGain(), importedTrackPoint.getElevationGain(), 0.01);
}
} }
} }
} }
@@ -15,7 +15,6 @@
*/ */
package de.dennisguse.opentracks.services; package de.dennisguse.opentracks.services;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -23,6 +22,8 @@ import org.junit.runners.JUnit4;
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy; import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
import static org.junit.Assert.assertEquals;
/** /**
* Tests the {@link AdaptiveLocationListenerPolicy}. * Tests the {@link AdaptiveLocationListenerPolicy}.
* *
@@ -57,17 +58,17 @@ public class AdaptiveLocationListenerPolicyTest {
@Test @Test
public void testGetDesiredPollingInterval() { public void testGetDesiredPollingInterval() {
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG); adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
Assert.assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval()); assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL); adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second. // First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
Assert.assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval()); assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL); adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval()); assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN); adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval()); assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
} }
/** /**
@@ -75,6 +76,6 @@ public class AdaptiveLocationListenerPolicyTest {
*/ */
@Test @Test
public void testGetMinDistance() { public void testGetMinDistance() {
Assert.assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m()); assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
} }
} }
@@ -25,16 +25,15 @@ import android.os.Looper;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest; import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.rule.GrantPermissionRule; import androidx.test.rule.GrantPermissionRule;
import androidx.test.rule.ServiceTestRule; import androidx.test.rule.ServiceTestRule;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -45,15 +44,22 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider; import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for the track recording service. * Tests for the track recording service.
* *
@@ -120,14 +126,14 @@ public class TrackRecordingServiceTest {
@Test @Test
public void testStartable() throws TimeoutException { public void testStartable() throws TimeoutException {
mServiceRule.startService(createStartIntent(context)); mServiceRule.startService(createStartIntent(context));
Assert.assertNotNull(mServiceRule.bindService(createStartIntent(context))); assertNotNull(mServiceRule.bindService(createStartIntent(context)));
} }
@MediumTest @MediumTest
@Test @Test
public void testBindable() throws TimeoutException { public void testBindable() throws TimeoutException {
IBinder service = mServiceRule.bindService(createStartIntent(context)); IBinder service = mServiceRule.bindService(createStartIntent(context));
Assert.assertNotNull(service); assertNotNull(service);
} }
@MediumTest @MediumTest
@@ -135,7 +141,7 @@ public class TrackRecordingServiceTest {
public void testRecording_noTracks() throws Exception { public void testRecording_noTracks() throws Exception {
// given // given
List<Track> tracks = contentProviderUtils.getTracks(); List<Track> tracks = contentProviderUtils.getTracks();
Assert.assertTrue(tracks.isEmpty()); assertTrue(tracks.isEmpty());
// when // when
Intent startIntent = createStartIntent(context); Intent startIntent = createStartIntent(context);
@@ -144,8 +150,8 @@ public class TrackRecordingServiceTest {
// then // then
// Test if we start in no-recording mode by default. // Test if we start in no-recording mode by default.
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
Assert.assertNull(service.getRecordingTrackId()); assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -158,8 +164,8 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
Assert.assertNull(service.getRecordingTrackId()); assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -172,7 +178,7 @@ public class TrackRecordingServiceTest {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
// then // then
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
} }
@MediumTest @MediumTest
@@ -187,21 +193,21 @@ public class TrackRecordingServiceTest {
service.pauseCurrentTrack(); service.pauseCurrentTrack();
// then // then
Assert.assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size()); assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
//when //when
service.resumeTrack(trackId); service.resumeTrack(trackId);
newTrackPoint(service); newTrackPoint(service);
// then // then
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
Assert.assertEquals(trackId, service.getRecordingTrackId()); assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(5, trackPoints.size()); assertEquals(5, trackPoints.size());
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01); assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01); assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01); assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
} }
@MediumTest @MediumTest
@@ -213,23 +219,23 @@ public class TrackRecordingServiceTest {
newTrackPoint(service); newTrackPoint(service);
service.endCurrentTrack(); service.endCurrentTrack();
Assert.assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size()); assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
// when // when
service.resumeTrack(trackId); service.resumeTrack(trackId);
newTrackPoint(service); newTrackPoint(service);
// then // then
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
Assert.assertEquals(trackId, service.getRecordingTrackId()); assertEquals(trackId, service.getRecordingTrackId());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(4, trackPoints.size()); assertEquals(4, trackPoints.size());
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01); assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01); assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
} }
@FlakyTest(detail = "Sometimes fails on CI.") @Ignore("Sometimes fails on CI.")
@MediumTest @MediumTest
@Test @Test
public void testRecording_orphanedRecordingTrack() throws Exception { public void testRecording_orphanedRecordingTrack() throws Exception {
@@ -242,8 +248,8 @@ public class TrackRecordingServiceTest {
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L); PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L);
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
Assert.assertNull(service.getRecordingTrackId()); assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -252,7 +258,7 @@ public class TrackRecordingServiceTest {
// given // given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack(); service.startNewTrack();
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
Track.Id trackId = service.getRecordingTrackId(); Track.Id trackId = service.getRecordingTrackId();
@@ -260,10 +266,10 @@ public class TrackRecordingServiceTest {
Track.Id newTrackId = service.startNewTrack(); Track.Id newTrackId = service.startNewTrack();
// then // then
Assert.assertNull(newTrackId); assertNull(newTrackId);
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context)); assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId()); assertEquals(trackId, service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -271,15 +277,15 @@ public class TrackRecordingServiceTest {
public void testEndCurrentTrack_noRecording() throws Exception { public void testEndCurrentTrack_noRecording() throws Exception {
// given // given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
// when // when
// Ending the current track when there is no recording should not result in any error. // Ending the current track when there is no recording should not result in any error.
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(PreferencesUtils.isRecording(context)); assertFalse(PreferencesUtils.isRecording(context));
Assert.assertNull(service.getRecordingTrackId()); assertNull(service.getRecordingTrackId());
} }
@MediumTest @MediumTest
@@ -287,13 +293,13 @@ public class TrackRecordingServiceTest {
public void testInsertWaypointMarker_noRecordingTrack() throws Exception { public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
// given // given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
// when // when
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null); Marker.Id waypointId = service.insertMarker(null, null, null, null);
// then // then
Assert.assertNull(waypointId); assertNull(waypointId);
} }
@MediumTest @MediumTest
@@ -302,29 +308,29 @@ public class TrackRecordingServiceTest {
// given // given
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
service.startNewTrack(); service.startNewTrack();
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
newTrackPoint(service); newTrackPoint(service);
Track.Id trackId = service.getRecordingTrackId(); Track.Id trackId = service.getRecordingTrackId();
// when // when
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null); Marker.Id waypointId = service.insertMarker(null, null, null, null);
// then // then
Assert.assertNotEquals(-1L, waypointId); assertNotEquals(-1L, waypointId);
Waypoint wpt = contentProviderUtils.getWaypoint(waypointId); Marker wpt = contentProviderUtils.getMarker(waypointId);
Assert.assertEquals(context.getString(R.string.marker_waypoint_icon_url), wpt.getIcon()); assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName()); assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
Assert.assertEquals(trackId, wpt.getTrackId()); assertEquals(trackId, wpt.getTrackId());
Assert.assertEquals(0.0, wpt.getLength(), 0.01); assertEquals(0.0, wpt.getLength(), 0.01);
Assert.assertNotNull(wpt.getLocation()); assertNotNull(wpt.getLocation());
service.endCurrentTrack(); service.endCurrentTrack();
} }
private void addTrack(Track track, boolean isRecording) { private void addTrack(Track track, boolean isRecording) {
Assert.assertTrue(track.getId().isValid()); assertTrue(track.getId().isValid());
contentProviderUtils.insertTrack(track); contentProviderUtils.insertTrack(track);
Assert.assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId()); assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT); PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording); PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
} }
@@ -12,7 +12,6 @@ import androidx.test.rule.GrantPermissionRule;
import androidx.test.rule.ServiceTestRule; import androidx.test.rule.ServiceTestRule;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
@@ -33,6 +32,9 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
/** /**
* Tests insert location. * Tests insert location.
* <p> * <p>
@@ -103,11 +105,11 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(6, trackPoints.size()); assertEquals(6, trackPoints.size());
Assert.assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01); assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01);
} }
@MediumTest @MediumTest
@@ -127,11 +129,11 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(2, trackPoints.size()); assertEquals(2, trackPoints.size());
Assert.assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01); assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
} }
// @MediumTest // @MediumTest
@@ -169,13 +171,13 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(3, trackPoints.size()); assertEquals(3, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01); assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01); assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01); assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
} }
@MediumTest @MediumTest
@@ -195,14 +197,14 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(4, trackPoints.size()); assertEquals(4, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01); assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01); assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted. assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
Assert.assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01); assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
} }
@@ -238,16 +240,16 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(6, trackPoints.size()); assertEquals(6, trackPoints.size());
Assert.assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01); assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01); assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01); assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01); assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
Assert.assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01); assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01); assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
} }
@MediumTest @MediumTest
@@ -266,16 +268,16 @@ public class TrackRecordingServiceTestLocation {
service.endCurrentTrack(); service.endCurrentTrack();
// then // then
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId); List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
Assert.assertEquals(7, trackPoints.size()); assertEquals(7, trackPoints.size());
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01); assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01); assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01); assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01); assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01); assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
Assert.assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01); assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
Assert.assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01); assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
} }
} }
@@ -13,7 +13,6 @@ import androidx.test.rule.ServiceTestRule;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
@@ -32,6 +31,12 @@ import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for the track recording service, which require a {@link Looper}. * Tests for the track recording service, which require a {@link Looper}.
* *
@@ -188,23 +193,23 @@ public class TrackRecordingServiceTestLooper {
@Test @Test
public void testIntegration_completeRecordingSession() throws TimeoutException { public void testIntegration_completeRecordingSession() throws TimeoutException {
List<Track> tracks = contentProviderUtils.getTracks(); List<Track> tracks = contentProviderUtils.getTracks();
Assert.assertTrue(tracks.isEmpty()); assertTrue(tracks.isEmpty());
fullRecordingSession(); fullRecordingSession();
} }
private void fullRecordingSession() throws TimeoutException { private void fullRecordingSession() throws TimeoutException {
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context))); TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
// Start a track. // Start a track.
Track.Id trackId = service.startNewTrack(); Track.Id trackId = service.startNewTrack();
Assert.assertTrue(trackId.isValid()); assertTrue(trackId.isValid());
Assert.assertTrue(service.isRecording()); assertTrue(service.isRecording());
Track track = contentProviderUtils.getTrack(trackId); Track track = contentProviderUtils.getTrack(trackId);
Assert.assertNotNull(track); assertNotNull(track);
Assert.assertEquals(trackId, track.getId()); assertEquals(trackId, track.getId());
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context)); assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
Assert.assertEquals(trackId, service.getRecordingTrackId()); assertEquals(trackId, service.getRecordingTrackId());
// Insert a few points, markers and statistics. // Insert a few points, markers and statistics.
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
@@ -221,20 +226,20 @@ public class TrackRecordingServiceTestLooper {
service.newTrackPoint(trackPoint, prefAccuracy); service.newTrackPoint(trackPoint, prefAccuracy);
if (i % 7 == 0) { if (i % 7 == 0) {
service.insertWaypoint(null, null, null, null); service.insertMarker(null, null, null, null);
} }
} }
// Stop the track. Validate if it has correct data. // Stop the track. Validate if it has correct data.
service.endCurrentTrack(); service.endCurrentTrack();
Assert.assertFalse(service.isRecording()); assertFalse(service.isRecording());
Assert.assertNull(service.getRecordingTrackId()); assertNull(service.getRecordingTrackId());
track = contentProviderUtils.getTrack(trackId); track = contentProviderUtils.getTrack(trackId);
Assert.assertNotNull(track); assertNotNull(track);
Assert.assertEquals(trackId, track.getId()); assertEquals(trackId, track.getId());
TrackStatistics trackStatistics = track.getTrackStatistics(); TrackStatistics trackStatistics = track.getTrackStatistics();
Assert.assertNotNull(trackStatistics); assertNotNull(trackStatistics);
Assert.assertTrue(trackStatistics.getStartTime_ms() > 0); assertTrue(trackStatistics.getStartTime_ms() > 0);
Assert.assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms()); assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
} }
} }
@@ -0,0 +1,50 @@
package de.dennisguse.opentracks.services.sensors;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* NOTE: Test data is completely artificial.
*/
public class ElevationSumManagerTest {
private ElevationSumManager elevationSumManager = new ElevationSumManager();
private static void addSensorValue(ElevationSumManager elevationSumManager, float[] values) {
for (float f : values) {
elevationSumManager.onSensorValueChanged(f);
}
}
@Before
public void setUp() {
elevationSumManager.reset();
}
@Test
public void getElevationGainLoss_downhill() {
// given
elevationSumManager.setConnected(true);
// then
addSensorValue(elevationSumManager, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
// then
Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01);
Assert.assertEquals(-48.0, elevationSumManager.getElevationLoss_m(), 0.01);
}
@Test
public void sensorUnavailable() {
// given
elevationSumManager.setConnected(false);
// then
elevationSumManager.onSensorValueChanged(999f);
// then
Assert.assertNull(elevationSumManager.getElevationGain_m());
Assert.assertNull(elevationSumManager.getElevationLoss_m());
}
}
@@ -23,11 +23,12 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link AnnouncementPeriodicTaskFactory}. * Tests for {@link AnnouncementPeriodicTaskFactory}.
* *
@@ -53,6 +54,6 @@ public class AnnouncementPeriodicTaskFactoryTest {
public void testCreate() { public void testCreate() {
PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory(); PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory();
PeriodicTask task = factory.create(context); PeriodicTask task = factory.create(context);
Assert.assertTrue(task instanceof AnnouncementPeriodicTask); assertTrue(task instanceof AnnouncementPeriodicTask);
} }
} }
@@ -15,12 +15,13 @@
*/ */
package de.dennisguse.opentracks.stats; package de.dennisguse.opentracks.stats;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link TrackStatistics}. * Tests for {@link TrackStatistics}.
* This only tests non-trivial pieces of that class. * This only tests non-trivial pieces of that class.
@@ -50,8 +51,8 @@ public class TrackStatisticsTest {
statistics2.setMovingTime(600L); // Result: 700+600 statistics2.setMovingTime(600L); // Result: 700+600
statistics.setTotalDistance(750.0); statistics.setTotalDistance(750.0);
statistics2.setTotalDistance(350.0); // Result: 750+350 statistics2.setTotalDistance(350.0); // Result: 750+350
statistics.setTotalElevationGain(50.0); statistics.setTotalElevationGain(50.0f);
statistics2.setTotalElevationGain(850.0); // Result: 850+50 statistics2.setTotalElevationGain(850.0f); // Result: 850+50
statistics.setMaxSpeed(60.0); // Resulting max speed statistics.setMaxSpeed(60.0); // Resulting max speed
statistics2.setMaxSpeed(30.0); statistics2.setMaxSpeed(30.0);
statistics.setMaxElevation(1250.0); statistics.setMaxElevation(1250.0);
@@ -61,28 +62,28 @@ public class TrackStatisticsTest {
statistics.merge(statistics2); statistics.merge(statistics2);
Assert.assertEquals(1000L, statistics.getStartTime_ms()); assertEquals(1000L, statistics.getStartTime_ms());
Assert.assertEquals(4000L, statistics.getStopTime_ms()); assertEquals(4000L, statistics.getStopTime_ms());
Assert.assertEquals(2500L, statistics.getTotalTime()); assertEquals(2500L, statistics.getTotalTime());
Assert.assertEquals(1300L, statistics.getMovingTime()); assertEquals(1300L, statistics.getMovingTime());
Assert.assertEquals(1100.0, statistics.getTotalDistance(), 0.001); assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
Assert.assertEquals(900.0, statistics.getTotalElevationGain(), 0.001); assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
Assert.assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001); assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
Assert.assertEquals(1200.0, statistics.getMinElevation(), 0.001); assertEquals(1200.0, statistics.getMinElevation(), 0.001);
Assert.assertEquals(3575.0, statistics.getMaxElevation(), 0.001); assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
} }
@Test @Test
public void testGetAverageSpeed() { public void testGetAverageSpeed() {
statistics.setTotalDistance(1000.0); statistics.setTotalDistance(1000.0);
statistics.setTotalTime(50000); // in milliseconds statistics.setTotalTime(50000); // in milliseconds
Assert.assertEquals(20.0, statistics.getAverageSpeed(), 0.001); assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
} }
@Test @Test
public void testGetAverageMovingSpeed() { public void testGetAverageMovingSpeed() {
statistics.setTotalDistance(1000.0); statistics.setTotalDistance(1000.0);
statistics.setMovingTime(20000); // in milliseconds statistics.setMovingTime(20000); // in milliseconds
Assert.assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001); assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
} }
} }
@@ -5,11 +5,20 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
import de.dennisguse.opentracks.R;
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.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class AnnouncementUtilsTest { public class AnnouncementUtilsTest {
@@ -26,9 +35,50 @@ public class AnnouncementUtilsTest {
stats.setTotalElevationGain(6000); stats.setTotalElevationGain(6000);
// when // when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane"); String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", null);
// then // then
Assert.assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement); assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
}
@Test
public void getAnnouncement_withInterval() {
TrackStatistics stats = new TrackStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setMaxSpeed(100);
stats.setTotalElevationGain(6000);
IntervalStatistics intervalStatistics = new IntervalStatistics();
List<TrackPoint> trackPoints = Arrays.asList(TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second);
intervalStatistics.build(trackPoints, 1000);
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
int speedId = R.plurals.voiceSpeedKilometersPerHour;
double kmPerHour = lastInterval.getSpeed_ms() * UnitConversions.MPS_TO_KMH;
String firstPartMsg = "OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour";
String rateMsg = " Lap speed of " + context.getResources().getQuantityString(speedId, getQuantityCount(kmPerHour), kmPerHour);
String msg = firstPartMsg + rateMsg;
// when
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", lastInterval);
// then
assertEquals(msg, announcement);
}
private int getQuantityCount(double d) {
if (d == 0) {
return 0;
} else if (d == 1) {
return 1;
} else if (d == 2) {
return 2;
} else {
int count = (int) d;
return Math.max(count, 3);
}
} }
} }
@@ -2,11 +2,13 @@ package de.dennisguse.opentracks.util;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import de.dennisguse.opentracks.content.sensor.SensorDataCycling; import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class BluetoothUtilsTest { public class BluetoothUtilsTest {
@Test @Test
@@ -19,7 +21,7 @@ public class BluetoothUtilsTest {
int heartRate = BluetoothUtils.parseHeartRate(characteristic); int heartRate = BluetoothUtils.parseHeartRate(characteristic);
// then // then
Assert.assertEquals(60, heartRate); assertEquals(60, heartRate);
} }
@Test @Test
@@ -32,7 +34,7 @@ public class BluetoothUtilsTest {
int heartRate = BluetoothUtils.parseHeartRate(characteristic); int heartRate = BluetoothUtils.parseHeartRate(characteristic);
// then // then
Assert.assertEquals(257, heartRate); assertEquals(257, heartRate);
} }
@Test @Test
@@ -44,8 +46,8 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then // then
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount()); assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
Assert.assertNull(sensor.getSpeed()); assertNull(sensor.getSpeed());
} }
@Test @Test
@@ -57,8 +59,8 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then // then
Assert.assertNull(sensor.getCadence()); assertNull(sensor.getCadence());
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount()); assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
} }
@Test @Test
@@ -70,7 +72,7 @@ public class BluetoothUtilsTest {
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
// then // then
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount()); assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount()); assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
} }
} }
@@ -15,7 +15,6 @@
*/ */
package de.dennisguse.opentracks.util; package de.dennisguse.opentracks.util;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
@@ -23,6 +22,8 @@ import org.junit.runners.JUnit4;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import static org.junit.Assert.assertEquals;
/** /**
* Tests for {@link FileUtils}. * Tests for {@link FileUtils}.
* *
@@ -37,7 +38,7 @@ public class FileUtilsTest {
@Test @Test
public void testBuildUniqueFileName_new() { public void testBuildUniqueFileName_new() {
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext"); String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
Assert.assertEquals("Filename.ext", filename); assertEquals("Filename.ext", filename);
} }
/** /**
@@ -47,7 +48,7 @@ public class FileUtilsTest {
public void testBuildUniqueFileName_exist() { public void testBuildUniqueFileName_exist() {
// Expect "/default.prop" to exist on the phone/emulator // Expect "/default.prop" to exist on the phone/emulator
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop"); String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
Assert.assertEquals("default(1).prop", filename); assertEquals("default(1).prop", filename);
} }
/** /**
@@ -58,13 +59,13 @@ public class FileUtilsTest {
public void testSanitizeFileName() { public void testSanitizeFileName() {
String name = "Swim\10ming-^across:/the/ pacific (ocean)."; String name = "Swim\10ming-^across:/the/ pacific (ocean).";
String expected = "Swim_ming-^across_the_ pacific (ocean)."; String expected = "Swim_ming-^across_the_ pacific (ocean).";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name)); assertEquals(expected, FileUtils.sanitizeFileName(name));
} }
@Test @Test
public void testSanitizeFileNameWithSuffix() { public void testSanitizeFileNameWithSuffix() {
String name = "FileName.jpeg"; String name = "FileName.jpeg";
Assert.assertEquals(name, FileUtils.sanitizeFileName(name)); assertEquals(name, FileUtils.sanitizeFileName(name));
} }
/** /**
@@ -75,7 +76,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_i18n() { public void testSanitizeFileName_i18n() {
String name = "您好-привет"; String name = "您好-привет";
String expected = "您好-привет"; String expected = "您好-привет";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name)); assertEquals(expected, FileUtils.sanitizeFileName(name));
} }
/** /**
@@ -86,7 +87,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_special_characters() { public void testSanitizeFileName_special_characters() {
String name = "$%'-_@~`!(){}^#&+,;=[] "; String name = "$%'-_@~`!(){}^#&+,;=[] ";
String expected = "$%'-_@~`!(){}^#&+,;=[] "; String expected = "$%'-_@~`!(){}^#&+,;=[] ";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name)); assertEquals(expected, FileUtils.sanitizeFileName(name));
} }
/** /**
@@ -97,7 +98,7 @@ public class FileUtilsTest {
public void testSanitizeFileName_collapse() { public void testSanitizeFileName_collapse() {
String name = "hello//there"; String name = "hello//there";
String expected = "hello_there"; String expected = "hello_there";
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name)); assertEquals(expected, FileUtils.sanitizeFileName(name));
} }
/** /**
@@ -114,8 +115,8 @@ public class FileUtilsTest {
String truncated = FileUtils.truncateFileName(directory, nameString, suffix); String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
for (int i = 0; i < truncated.length(); i++) { for (int i = 0; i < truncated.length(); i++) {
Assert.assertEquals('a', truncated.charAt(i)); assertEquals('a', truncated.charAt(i));
} }
Assert.assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length()); assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
} }
} }
@@ -6,13 +6,14 @@ import android.content.SharedPreferences;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class PreferencesUtilsTest { public class PreferencesUtilsTest {
@@ -29,7 +30,7 @@ public class PreferencesUtilsTest {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context); TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then // then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat); assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
} }
@Test @Test
@@ -43,7 +44,7 @@ public class PreferencesUtilsTest {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context); TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then // then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat); assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
} }
@Test @Test
@@ -57,6 +58,6 @@ public class PreferencesUtilsTest {
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context); TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
// then // then
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat); assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
} }
} }
@@ -0,0 +1,49 @@
package de.dennisguse.opentracks.util;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class PressureSensorUtilsTest {
// real data at 500Hz stationary
@Test
public void elevationChanges_none() {
// given
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
float firstSensorValue = sensorValues_hPa[0];
// when // then
for (float v : sensorValues_hPa) {
assertNull(PressureSensorUtils.computeChanges_m(firstSensorValue, v));
}
}
// Simulate a sudden drop of elevation.
// test data
@Test
public void computeChanges_downhill() {
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
float firstSensorValue = sensorValues_hPa[0];
// when
float elevationGain_m = 0;
float elevationLoss_m = 0;
float lastUsedPressureValue_hPa = firstSensorValue;
for (float v : sensorValues_hPa) {
PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, v);
if (elevationChange != null) {
elevationGain_m += elevationChange.getElevationGain_m();
elevationLoss_m += elevationChange.getElevationLoss_m();
lastUsedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa();
}
}
// then
assertEquals(0f, elevationGain_m, 0.01);
assertEquals(-15.0f, elevationLoss_m, 0.01);
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
}
}
@@ -21,13 +21,15 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link StringUtils}. * Tests for {@link StringUtils}.
* *
@@ -44,7 +46,7 @@ public class StringUtilsTest {
*/ */
@Test @Test
public void testFormatDateTimeIso8601() { public void testFormatDateTimeIso8601() {
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345)); assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
} }
/** /**
@@ -53,19 +55,19 @@ public class StringUtilsTest {
@Test @Test
public void testformatElapsedTime() { public void testformatElapsedTime() {
// 1 second // 1 second
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000)); assertEquals("00:01", StringUtils.formatElapsedTime(1000));
// 10 seconds // 10 seconds
Assert.assertEquals("00:10", StringUtils.formatElapsedTime(10000)); assertEquals("00:10", StringUtils.formatElapsedTime(10000));
// 1 minute // 1 minute
Assert.assertEquals("01:00", StringUtils.formatElapsedTime(60000)); assertEquals("01:00", StringUtils.formatElapsedTime(60000));
// 10 minutes // 10 minutes
Assert.assertEquals("10:00", StringUtils.formatElapsedTime(600000)); assertEquals("10:00", StringUtils.formatElapsedTime(600000));
// 1 hour // 1 hour
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000)); assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
// 10 hours // 10 hours
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000)); assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
// 100 hours // 100 hours
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000)); assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
} }
/** /**
@@ -74,19 +76,19 @@ public class StringUtilsTest {
@Test @Test
public void testformatElapsedTimeWithHour() { public void testformatElapsedTimeWithHour() {
// 1 second // 1 second
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000)); assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
// 10 seconds // 10 seconds
Assert.assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000)); assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
// 1 minute // 1 minute
Assert.assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000)); assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
// 10 minutes // 10 minutes
Assert.assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000)); assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
// 1 hour // 1 hour
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000)); assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
// 10 hours // 10 hours
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000)); assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
// 100 hours // 100 hours
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000)); assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
} }
/** /**
@@ -96,13 +98,13 @@ public class StringUtilsTest {
@Test @Test
public void testFormatDistance() { public void testFormatDistance() {
// A large number in metric // A large number in metric
Assert.assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true)); assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
// A large number in imperial // A large number in imperial
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false)); assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
// A small number in metric // A small number in metric
Assert.assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true)); assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
// A small number in imperial // A small number in imperial
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false)); assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
} }
/** /**
@@ -110,8 +112,8 @@ public class StringUtilsTest {
*/ */
@Test @Test
public void testFormatCData() { public void testFormatCData() {
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello")); assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there")); assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
} }
/** /**
@@ -168,7 +170,7 @@ public class StringUtilsTest {
// This comparision tends to be flaky (difference of 1ms) // This comparision tends to be flaky (difference of 1ms)
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime)); // Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
Assert.assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1); assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
} }
/** /**
@@ -177,9 +179,9 @@ public class StringUtilsTest {
@Test @Test
public void testGetTimeParts_positive() { public void testGetTimeParts_positive() {
int[] parts = StringUtils.getTimeParts(61000); int[] parts = StringUtils.getTimeParts(61000);
Assert.assertEquals(1, parts[0]); assertEquals(1, parts[0]);
Assert.assertEquals(1, parts[1]); assertEquals(1, parts[1]);
Assert.assertEquals(0, parts[2]); assertEquals(0, parts[2]);
} }
/** /**
@@ -188,40 +190,40 @@ public class StringUtilsTest {
@Test @Test
public void testGetTimeParts_negative() { public void testGetTimeParts_negative() {
int[] parts = StringUtils.getTimeParts(-61000); int[] parts = StringUtils.getTimeParts(-61000);
Assert.assertEquals(-1, parts[0]); assertEquals(-1, parts[0]);
Assert.assertEquals(-1, parts[1]); assertEquals(-1, parts[1]);
Assert.assertEquals(0, parts[2]); assertEquals(0, parts[2]);
} }
@Test @Test
public void testFormatDecimal() { public void testFormatDecimal() {
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 0)); assertEquals("0", StringUtils.formatDecimal(0.0, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(0.1, 0)); assertEquals("0", StringUtils.formatDecimal(0.1, 0));
Assert.assertEquals("1", StringUtils.formatDecimal(1.1, 0)); assertEquals("1", StringUtils.formatDecimal(1.1, 0));
Assert.assertEquals("10", StringUtils.formatDecimal(10, 0)); assertEquals("10", StringUtils.formatDecimal(10, 0));
Assert.assertEquals("10", StringUtils.formatDecimal(10.1, 0)); assertEquals("10", StringUtils.formatDecimal(10.1, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(-0.1, 0)); assertEquals("-0", StringUtils.formatDecimal(-0.1, 0));
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 2)); assertEquals("0", StringUtils.formatDecimal(0.0, 2));
Assert.assertEquals("0.1", StringUtils.formatDecimal(0.1, 2)); assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
Assert.assertEquals("1.1", StringUtils.formatDecimal(1.1, 2)); assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
Assert.assertEquals("10", StringUtils.formatDecimal(10, 2)); assertEquals("10", StringUtils.formatDecimal(10, 2));
Assert.assertEquals("10.1", StringUtils.formatDecimal(10.1, 2)); assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
Assert.assertEquals("10.11", StringUtils.formatDecimal(10.111, 2)); assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
Assert.assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2)); assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
Assert.assertEquals("1", StringUtils.formatDecimal(0.99, 1)); assertEquals("1", StringUtils.formatDecimal(0.99, 1));
} }
@Test @Test
public void testGetSpeedParts() { public void testGetSpeedParts() {
Assert.assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first); assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
Assert.assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first); assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
Assert.assertEquals("11.99", StringUtils.getSpeedParts(context, 3.33, true, true).first); assertEquals("11.9", StringUtils.getSpeedParts(context, 3.31, true, true).first);
Assert.assertEquals("7.45", StringUtils.getSpeedParts(context, 3.33, false, true).first); assertEquals("7.5", StringUtils.getSpeedParts(context, 3.34, false, true).first);
Assert.assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second); assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
Assert.assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second); assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
} }
} }
@@ -21,7 +21,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -32,6 +31,8 @@ import java.util.Locale;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import static org.junit.Assert.assertEquals;
/** /**
* Tests {@link TrackNameUtils}. * Tests {@link TrackNameUtils}.
* *
@@ -51,7 +52,7 @@ public class TrackNameUtilsTest {
@Test @Test
public void testTrackName_date_local() { public void testTrackName_date_local() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value)); PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME)); assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
} }
/** /**
@@ -61,7 +62,7 @@ public class TrackNameUtilsTest {
public void testTrackName_date_iso_8601() { public void testTrackName_date_iso_8601() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value)); PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME)); assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
} }
/** /**
@@ -70,6 +71,6 @@ public class TrackNameUtilsTest {
@Test @Test
public void testTrackName_number() { public void testTrackName_number() {
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value)); PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
Assert.assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME)); assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
} }
} }
@@ -1,10 +1,11 @@
package de.dennisguse.opentracks.util; package de.dennisguse.opentracks.util;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import static org.junit.Assert.assertEquals;
public class TrackPointUtilsTest { public class TrackPointUtilsTest {
@Test @Test
@@ -18,7 +19,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint); TrackPointUtils.fixTime(trackPoint);
// then // then
Assert.assertEquals(time, trackPoint.getTime()); assertEquals(time, trackPoint.getTime());
} }
@Test @Test
@@ -32,7 +33,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint); TrackPointUtils.fixTime(trackPoint);
// then // then
Assert.assertEquals(time, trackPoint.getTime(), 1000); assertEquals(time, trackPoint.getTime(), 1000);
} }
@Test @Test
@@ -46,7 +47,7 @@ public class TrackPointUtilsTest {
TrackPointUtils.fixTime(trackPoint); TrackPointUtils.fixTime(trackPoint);
// then // then
Assert.assertEquals(time, trackPoint.getTime()); assertEquals(time, trackPoint.getTime());
} }
} }
@@ -2,12 +2,13 @@ package de.dennisguse.opentracks.util;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.util.UUID; import java.util.UUID;
import static org.junit.Assert.assertEquals;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class UUIDUtilsTest { public class UUIDUtilsTest {
@@ -18,6 +19,6 @@ public class UUIDUtilsTest {
byte[] bytes = UUIDUtils.toBytes(uuid); byte[] bytes = UUIDUtils.toBytes(uuid);
UUID output = UUIDUtils.fromBytes(bytes); UUID output = UUIDUtils.fromBytes(bytes);
Assert.assertEquals(uuid, output); assertEquals(uuid, output);
} }
} }
@@ -4,7 +4,6 @@ import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
@@ -14,6 +13,9 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
public class AggregatedStatisticsTest { public class AggregatedStatisticsTest {
@@ -33,7 +35,7 @@ public class AggregatedStatisticsTest {
statistics.setTotalTime(totalTime); statistics.setTotalTime(totalTime);
statistics.setMovingTime(totalTime); statistics.setMovingTime(totalTime);
statistics.setTotalDistance(totalDistance); statistics.setTotalDistance(totalDistance);
statistics.setTotalElevationGain(50.0); statistics.setTotalElevationGain(50.0f);
statistics.setMaxSpeed(50.0); // Resulting max speed statistics.setMaxSpeed(50.0); // Resulting max speed
statistics.setMaxElevation(1250.0); statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0); // Resulting min elevation statistics.setMinElevation(1200.0); // Resulting min elevation
@@ -59,13 +61,13 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(track); aggregatedStatistics.aggregate(track);
// then // then
Assert.assertEquals(1, aggregatedStatistics.getCount()); assertEquals(1, aggregatedStatistics.getCount());
Assert.assertNotNull(aggregatedStatistics.get(biking)); assertNotNull(aggregatedStatistics.get(biking));
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks()); assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0); assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime()); assertEquals(totalTime, statistics2.getMovingTime());
} }
@Test @Test
@@ -82,7 +84,7 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(track); aggregatedStatistics.aggregate(track);
// then // then
Assert.assertNotNull(aggregatedStatistics.get(mountainBiking)); assertNotNull(aggregatedStatistics.get(mountainBiking));
} }
@Test @Test
@@ -99,7 +101,7 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(track); aggregatedStatistics.aggregate(track);
// then // then
Assert.assertNotNull(aggregatedStatistics.get(trailRunning)); assertNotNull(aggregatedStatistics.get(trailRunning));
} }
@Test @Test
@@ -120,13 +122,13 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2. aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
// then // then
Assert.assertEquals(1, aggregatedStatistics.getCount()); assertEquals(1, aggregatedStatistics.getCount());
Assert.assertNotNull(aggregatedStatistics.get(biking)); assertNotNull(aggregatedStatistics.get(biking));
Assert.assertEquals(2, aggregatedStatistics.get(biking).getCountTracks()); assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0); assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2L, statistics2.getMovingTime()); assertEquals(totalTime * 2L, statistics2.getMovingTime());
} }
@Test @Test
@@ -152,30 +154,30 @@ public class AggregatedStatisticsTest {
aggregatedStatistics.aggregate(tracks[2]); // biking activity 3. aggregatedStatistics.aggregate(tracks[2]); // biking activity 3.
// then // then
Assert.assertEquals(3, aggregatedStatistics.getCount()); assertEquals(3, aggregatedStatistics.getCount());
Assert.assertNotNull(aggregatedStatistics.get(biking)); assertNotNull(aggregatedStatistics.get(biking));
Assert.assertNotNull(aggregatedStatistics.get(running)); assertNotNull(aggregatedStatistics.get(running));
Assert.assertNotNull(aggregatedStatistics.get(walking)); assertNotNull(aggregatedStatistics.get(walking));
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks()); assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(running).getCountTracks()); assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(walking).getCountTracks()); assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0); assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime()); assertEquals(totalTime, statistics2.getMovingTime());
} }
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0); assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime()); assertEquals(totalTime, statistics2.getMovingTime());
} }
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0); assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime()); assertEquals(totalTime, statistics2.getMovingTime());
} }
} }
@@ -212,46 +214,46 @@ public class AggregatedStatisticsTest {
// then // then
// 4 sports. // 4 sports.
Assert.assertEquals(4, aggregatedStatistics.getCount()); assertEquals(4, aggregatedStatistics.getCount());
// There is a map for every sport. // There is a map for every sport.
Assert.assertNotNull(aggregatedStatistics.get(biking)); assertNotNull(aggregatedStatistics.get(biking));
Assert.assertNotNull(aggregatedStatistics.get(running)); assertNotNull(aggregatedStatistics.get(running));
Assert.assertNotNull(aggregatedStatistics.get(walking)); assertNotNull(aggregatedStatistics.get(walking));
Assert.assertNotNull(aggregatedStatistics.get(driving)); assertNotNull(aggregatedStatistics.get(driving));
// Number of tracks by sport. // Number of tracks by sport.
Assert.assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking. assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
Assert.assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running. assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running.
Assert.assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking. assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking.
Assert.assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving. assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving.
// Biking. // Biking.
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0); assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 5, statistics2.getMovingTime()); assertEquals(totalTime * 5, statistics2.getMovingTime());
} }
// Running. // Running.
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0); assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime()); assertEquals(totalTime * 2, statistics2.getMovingTime());
} }
// Walking. // Walking.
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0); assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime()); assertEquals(totalTime * 2, statistics2.getMovingTime());
} }
// Driving. // Driving.
{ {
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics(); TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0); assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime()); assertEquals(totalTime, statistics2.getMovingTime());
} }
} }
} }
@@ -0,0 +1,144 @@
package de.dennisguse.opentracks.viewmodels;
import android.util.Pair;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
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.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.UnitConversions;
import static org.junit.Assert.assertEquals;
@RunWith(JUnit4.class)
public class IntervalStatisticsTest {
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
private List<TrackPoint> buildTrackPoints(int numberOfTrackPoints) {
Pair<Track, TrackPoint[]> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints);
return Arrays.asList(pair.second);
}
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater(trackPoints.get(0).getTime());
for (TrackPoint tp : trackPoints) {
trackStatisticsUpdater.addTrackPoint(tp, 0);
}
return trackStatisticsUpdater.getTrackStatistics();
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_1() {
// With 50 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(50);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_2() {
// With 200 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000f;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_3() {
// With 200 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(200);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_4() {
// With 1000 points and interval distance of 3000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(1000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 3000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
/**
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
*/
@Test
public void testBuild_5() {
// With 10000 points and interval distance of 1000m.
// given
List<TrackPoint> trackPoints = buildTrackPoints(10000);
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
float distanceInterval = 1000;
IntervalStatistics intervalStatistics = new IntervalStatistics();
// when and then
whenAndThen(trackPoints, trackStatistics, distanceInterval, intervalStatistics);
}
private void whenAndThen(List<TrackPoint> trackPoints, TrackStatistics trackStatistics, float distanceInterval, IntervalStatistics intervalStatistics) {
intervalStatistics.build(trackPoints, distanceInterval);
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
double totalDistance = 0d;
long totalTime = 0L;
for (IntervalStatistics.Interval i : intervalList) {
totalDistance += i.getDistance_m();
totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS);
}
// then
assertEquals(trackStatistics.getTotalDistance(), totalDistance, 0.01);
assertEquals(trackStatistics.getTotalTime() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.1);
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval));
for (int i = 0; i < intervalList.size() - 1; i++) {
assertEquals(intervalList.get(i).getDistance_m(), distanceInterval, 0.001);
totalDistance -= intervalList.get(i).getDistance_m();
}
assertEquals(intervalList.get(intervalList.size() - 1).getDistance_m(), totalDistance, 0.01);
}
}
+3 -2
View File
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="de.dennisguse.opentracks" package="de.dennisguse.opentracks"
android:installLocation="auto"> android:installLocation="auto">
@@ -138,7 +137,9 @@ limitations under the License.
<activity android:name=".TrackEditActivity" /> <activity android:name=".TrackEditActivity" />
<activity android:name=".TrackListActivity"> <activity
android:name=".TrackListActivity"
android:theme="@style/SplashTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@@ -23,7 +23,7 @@ public class AggregatedStatisticsActivity extends AbstractActivity {
final AggregatedStatisticsModel viewModel = new ViewModelProvider(this).get(AggregatedStatisticsModel.class); final AggregatedStatisticsModel viewModel = new ViewModelProvider(this).get(AggregatedStatisticsModel.class);
viewModel.getAggregatedStats().observe(this, aggregatedStatistics -> { viewModel.getAggregatedStats().observe(this, aggregatedStatistics -> {
if (aggregatedStatistics != null) { if (aggregatedStatistics != null) {
adapter = new AggregatedStatisticsAdapter(getApplicationContext(), aggregatedStatistics); adapter = new AggregatedStatisticsAdapter(this, aggregatedStatistics);
listView.setAdapter(adapter); listView.setAdapter(adapter);
} }
adapter.notifyDataSetChanged(); adapter.notifyDataSetChanged();
@@ -30,7 +30,7 @@ import androidx.viewpager.widget.ViewPager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
import de.dennisguse.opentracks.fragments.MarkerDetailFragment; import de.dennisguse.opentracks.fragments.MarkerDetailFragment;
@@ -48,33 +48,33 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
private static final String TAG = MarkerDetailActivity.class.getSimpleName(); private static final String TAG = MarkerDetailActivity.class.getSimpleName();
private List<Waypoint.Id> waypointIds; private List<Marker.Id> markerIds;
@Override @Override
protected void onCreate(Bundle bundle) { protected void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
Waypoint.Id waypointId = getIntent().getParcelableExtra(EXTRA_MARKER_ID); Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
if (waypointId == null) { if (markerId == null) {
Log.d(TAG, "invalid marker id"); Log.d(TAG, "invalid marker id");
finish(); finish();
return; return;
} }
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId); Marker marker = contentProviderUtils.getMarker(markerId);
waypointIds = new ArrayList<>(); markerIds = new ArrayList<>();
int markerIndex = -1; int markerIndex = -1;
//TODO Load only waypointIds, not the whole waypoint //TODO Load only markerIds, not the whole marker
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.getTrackId(), null, -1)) { try (Cursor cursor = contentProviderUtils.getMarkerCursor(marker.getTrackId(), null, -1)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor); Marker currentMarker = contentProviderUtils.createMarker(cursor);
waypointIds.add(currentMarker.getId()); markerIds.add(currentMarker.getId());
if (waypointId.equals(currentMarker.getId())) { if (markerId.equals(currentMarker.getId())) {
markerIndex = waypointIds.size() - 1; markerIndex = markerIds.size() - 1;
} }
cursor.moveToNext(); cursor.moveToNext();
@@ -127,7 +127,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override @Override
@NonNull @NonNull
public Fragment getItem(int position) { public Fragment getItem(int position) {
return MarkerDetailFragment.newInstance(waypointIds.get(position)); return MarkerDetailFragment.newInstance(markerIds.get(position));
} }
@Nullable @Nullable
@@ -138,7 +138,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override @Override
public int getCount() { public int getCount() {
return waypointIds.size(); return markerIds.size();
} }
} }
} }
@@ -32,18 +32,19 @@ import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.material.textfield.TextInputEditText;
import java.io.File; import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
@@ -67,7 +68,7 @@ public class MarkerEditActivity extends AbstractActivity {
private static final String TAG = MarkerEditActivity.class.getSimpleName(); private static final String TAG = MarkerEditActivity.class.getSimpleName();
private Track.Id trackId; private Track.Id trackId;
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection(); private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
private Waypoint waypoint; private Marker marker;
private MenuItem insertPhotoMenuItem; private MenuItem insertPhotoMenuItem;
private MenuItem insertGalleryImgMenuItem; private MenuItem insertGalleryImgMenuItem;
@@ -76,35 +77,35 @@ public class MarkerEditActivity extends AbstractActivity {
private boolean hasCamera; private boolean hasCamera;
// UI elements // UI elements
private EditText waypointName; private TextInputEditText markerName;
private AutoCompleteTextView waypointMarkerType; private AutoCompleteTextView markerType;
private EditText waypointDescription; private TextInputEditText markerDescription;
private ImageView waypointPhoto; private ImageView markerPhoto;
private ImageView waypointDeletePhotoBtn; private ImageView markerDeletePhotoBtn;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID); trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
Waypoint.Id waypointId = getIntent().getParcelableExtra(EXTRA_MARKER_ID); Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY); hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
// Setup UI elements // Setup UI elements
waypointName = findViewById(R.id.marker_edit_waypoint_name); markerName = findViewById(R.id.marker_edit_name);
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type); markerType = findViewById(R.id.marker_edit_marker_type);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.marker_types, android.R.layout.simple_dropdown_item_1line);
waypointMarkerType.setAdapter(adapter); markerType.setAdapter(adapter);
waypointDescription = findViewById(R.id.marker_edit_waypoint_description); markerDescription = findViewById(R.id.marker_edit_description);
waypointPhoto = findViewById(R.id.marker_edit_waypoint_photo); markerPhoto = findViewById(R.id.marker_edit_photo);
waypointDeletePhotoBtn = findViewById(R.id.marker_edit_waypoint_photo_delete); markerDeletePhotoBtn = findViewById(R.id.marker_edit_photo_delete);
waypointDeletePhotoBtn.setOnClickListener(v -> { markerDeletePhotoBtn.setOnClickListener(v -> {
if (waypoint != null && waypoint.hasPhoto()) { if (marker != null && marker.hasPhoto()) {
waypoint.setPhotoUrl(null); marker.setPhotoUrl(null);
} }
waypointPhoto.setImageBitmap(null); markerPhoto.setImageBitmap(null);
photoUri = null; photoUri = null;
hideAndShowOptions(); hideAndShowOptions();
}); });
@@ -112,7 +113,7 @@ public class MarkerEditActivity extends AbstractActivity {
Button cancel = findViewById(R.id.marker_edit_cancel); Button cancel = findViewById(R.id.marker_edit_cancel);
cancel.setOnClickListener(v -> finish()); cancel.setOnClickListener(v -> finish());
final boolean isNewMarker = waypointId == null; final boolean isNewMarker = markerId == null;
setTitle(isNewMarker ? R.string.menu_insert_marker : R.string.menu_edit); setTitle(isNewMarker ? R.string.menu_insert_marker : R.string.menu_edit);
Button done = findViewById(R.id.marker_edit_done); Button done = findViewById(R.id.marker_edit_done);
@@ -127,37 +128,37 @@ public class MarkerEditActivity extends AbstractActivity {
}); });
if (isNewMarker) { if (isNewMarker) {
int nextWaypointNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId); int nextMarkerNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextMarkerNumber(trackId);
if (nextWaypointNumber == -1) { if (nextMarkerNumber == -1) {
nextWaypointNumber = 0; nextMarkerNumber = 0;
} }
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber)); markerName.setText(getString(R.string.marker_name_format, nextMarkerNumber));
waypointName.selectAll(); markerName.selectAll();
waypointMarkerType.setText(""); markerType.setText("");
waypointDescription.setText(""); markerDescription.setText("");
} else { } else {
waypoint = new ContentProviderUtils(this).getWaypoint(waypointId); marker = new ContentProviderUtils(this).getMarker(markerId);
if (waypoint == null) { if (marker == null) {
Log.d(TAG, "waypoint is null"); Log.d(TAG, "marker is null");
finish(); finish();
return; return;
} }
waypointName.setText(waypoint.getName()); markerName.setText(marker.getName());
waypointMarkerType.setText(waypoint.getCategory()); markerType.setText(marker.getCategory());
waypointDescription.setText(waypoint.getDescription()); markerDescription.setText(marker.getDescription());
if (waypoint.hasPhoto()) { if (marker.hasPhoto()) {
photoUri = waypoint.getPhotoURI(); photoUri = marker.getPhotoURI();
} }
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI); photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI);
if (waypoint != null) { if (marker != null) {
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null); marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
} }
} }
if (photoUri != null) { if (photoUri != null) {
setWaypointImageView(photoUri); setMarkerImageView(photoUri);
} }
hideAndShowOptions(); hideAndShowOptions();
@@ -202,10 +203,10 @@ public class MarkerEditActivity extends AbstractActivity {
public boolean onOptionsItemSelected(@NonNull MenuItem item) { public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.marker_edit_insert_photo: case R.id.marker_edit_insert_photo:
createWaypointWithPicture(); createMarkerWithPicture();
return true; return true;
case R.id.marker_edit_insert_gallery_img: case R.id.marker_edit_insert_gallery_img:
createWaypointWithGalleryImage(); createMarkerWithGalleryImage();
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
@@ -219,7 +220,7 @@ public class MarkerEditActivity extends AbstractActivity {
Toast.makeText(this, R.string.marker_add_photo_canceled, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.marker_add_photo_canceled, Toast.LENGTH_LONG).show();
return; return;
} else if (resultCode == RESULT_OK) { } else if (resultCode == RESULT_OK) {
setWaypointImageView(photoUri); setMarkerImageView(photoUri);
} }
} else if (requestCode == GALLERY_IMG_REQUEST_CODE) { } else if (requestCode == GALLERY_IMG_REQUEST_CODE) {
if (resultCode == RESULT_CANCELED) { if (resultCode == RESULT_CANCELED) {
@@ -233,7 +234,7 @@ public class MarkerEditActivity extends AbstractActivity {
FileUtils.copy(srcFd, dstFile); FileUtils.copy(srcFd, dstFile);
photoUri = FileUtils.getUriForFile(this, dstFile); photoUri = FileUtils.getUriForFile(this, dstFile);
setWaypointImageView(photoUri); setMarkerImageView(photoUri);
} catch(Exception e) { } catch(Exception e) {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
Toast.makeText(this, R.string.marker_add_canceled, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.marker_add_canceled, Toast.LENGTH_LONG).show();
@@ -250,31 +251,26 @@ public class MarkerEditActivity extends AbstractActivity {
* If a photo is set then one's options are shown, otherwise another ones are shown. * If a photo is set then one's options are shown, otherwise another ones are shown.
*/ */
private void hideAndShowOptions() { private void hideAndShowOptions() {
boolean isPhotoSet = (waypoint != null && waypoint.hasPhoto()) || photoUri != null; boolean isPhotoSet = (marker != null && marker.hasPhoto()) || photoUri != null;
if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) { if (insertPhotoMenuItem != null && insertGalleryImgMenuItem != null) {
insertPhotoMenuItem.setVisible(!isPhotoSet); insertPhotoMenuItem.setVisible(!isPhotoSet);
insertGalleryImgMenuItem.setVisible(!isPhotoSet); insertGalleryImgMenuItem.setVisible(!isPhotoSet);
} }
waypointDeletePhotoBtn.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE); markerDeletePhotoBtn.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE);
} }
/** /**
* Returns the trackId. If trackId == -1 then get it from waypoint object. * Returns the trackId; either from track or marker.
*/ */
private Track.Id getTrackId() { private Track.Id getTrackId() {
return trackId == null ? waypoint.getTrackId() : trackId; return trackId == null ? marker.getTrackId() : trackId;
} }
/** private void setMarkerImageView(@NonNull Uri uri) {
* Sets the ImageView waypointPhoto with the uri photo.
*
* @param uri the uri photo.
*/
private void setWaypointImageView(@NonNull Uri uri) {
try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r")) { try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r")) {
FileDescriptor fd = pfd.getFileDescriptor(); FileDescriptor fd = pfd.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd); Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd);
waypointPhoto.setImageBitmap(bitmap); markerPhoto.setImageBitmap(bitmap);
hideAndShowOptions(); hideAndShowOptions();
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
@@ -282,31 +278,31 @@ public class MarkerEditActivity extends AbstractActivity {
} }
} }
private void createWaypointWithPicture() { private void createMarkerWithPicture() {
Pair<Intent, Uri> intentAndPhotoUri = IntentUtils.createTakePictureIntent(this, getTrackId()); Pair<Intent, Uri> intentAndPhotoUri = IntentUtils.createTakePictureIntent(this, getTrackId());
photoUri = intentAndPhotoUri.second; photoUri = intentAndPhotoUri.second;
startActivityForResult(intentAndPhotoUri.first, CAMERA_REQUEST_CODE); startActivityForResult(intentAndPhotoUri.first, CAMERA_REQUEST_CODE);
} }
private void createWaypointWithGalleryImage() { private void createMarkerWithGalleryImage() {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, GALLERY_IMG_REQUEST_CODE); startActivityForResult(intent, GALLERY_IMG_REQUEST_CODE);
} }
private void addMarker() { private void addMarker() {
trackRecordingServiceConnection.addMarker(this, trackRecordingServiceConnection.addMarker(this,
waypointName.getText().toString(), markerName.getText().toString(),
waypointMarkerType.getText().toString(), markerType.getText().toString(),
waypointDescription.getText().toString(), markerDescription.getText().toString(),
photoUri != null ? photoUri.toString() : null); photoUri != null ? photoUri.toString() : null);
} }
private void saveMarker() { private void saveMarker() {
waypoint.setName(waypointName.getText().toString()); marker.setName(markerName.getText().toString());
waypoint.setCategory(waypointMarkerType.getText().toString()); marker.setCategory(markerType.getText().toString());
waypoint.setDescription(waypointDescription.getText().toString()); marker.setDescription(markerDescription.getText().toString());
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null); marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
new ContentProviderUtils(this).updateWaypoint(this, waypoint); new ContentProviderUtils(this).updateMarker(this, marker);
} }
} }
@@ -34,9 +34,9 @@ import androidx.loader.app.LoaderManager;
import androidx.loader.app.LoaderManager.LoaderCallbacks; import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.Loader; import androidx.loader.content.Loader;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
@@ -124,19 +124,19 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
listView.setEmptyView(findViewById(R.id.marker_list_empty)); listView.setEmptyView(findViewById(R.id.marker_list_empty));
listView.setOnItemClickListener((parent, view, position, id) -> { listView.setOnItemClickListener((parent, view, position, id) -> {
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class) Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Waypoint.Id(id)); .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Marker.Id(id));
startActivity(intent); startActivity(intent);
}); });
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) { resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@Override @Override
public void bindView(View view, Context context, Cursor cursor) { public void bindView(View view, Context context, Cursor cursor) {
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME); int nameIndex = cursor.getColumnIndex(MarkerColumns.NAME);
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME); int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY); int categoryIndex = cursor.getColumnIndex(MarkerColumns.CATEGORY);
int descriptionIndex = cursor.getColumnIndex(WaypointsColumns.DESCRIPTION); int descriptionIndex = cursor.getColumnIndex(MarkerColumns.DESCRIPTION);
int photoUrlIndex = cursor.getColumnIndex(WaypointsColumns.PHOTOURL); int photoUrlIndex = cursor.getColumnIndex(MarkerColumns.PHOTOURL);
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE); int latitudeIndex = cursor.getColumnIndex(MarkerColumns.LATITUDE);
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE); int longitudeIndex = cursor.getColumnIndex(MarkerColumns.LONGITUDE);
int iconId = MarkerUtils.ICON_ID; int iconId = MarkerUtils.ICON_ID;
String name = cursor.getString(nameIndex); String name = cursor.getString(nameIndex);
@@ -158,7 +158,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
@NonNull @NonNull
@Override @Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return ContentProviderUtils.getWaypointsLoader(MarkerListActivity.this, track != null ? track.getId() : null); return ContentProviderUtils.getMarkersLoader(MarkerListActivity.this, track != null ? track.getId() : null);
} }
@Override @Override
@@ -229,35 +229,35 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
/** /**
* Handles a context item selection. * Handles a context item selection.
* *
* @param itemId the menu item id * @param itemId the menu item id
* @param longWaypointIds the marker ids * @param longMarkerds the marker ids
* @return true if handled. * @return true if handled.
*/ */
private boolean handleContextItem(int itemId, long... longWaypointIds) { private boolean handleContextItem(int itemId, long... longMarkerds) {
Waypoint.Id[] waypointIds = new Waypoint.Id[longWaypointIds.length]; Marker.Id[] markerIds = new Marker.Id[longMarkerds.length];
for (int i = 0; i < longWaypointIds.length; i++) { for (int i = 0; i < longMarkerds.length; i++) {
waypointIds[i] = new Waypoint.Id(longWaypointIds[i]); markerIds[i] = new Marker.Id(longMarkerds[i]);
} }
Intent intent; Intent intent;
switch (itemId) { switch (itemId) {
case R.id.list_context_menu_show_on_map: case R.id.list_context_menu_show_on_map:
if (waypointIds.length == 1) { if (markerIds.length == 1) {
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getWaypoint(waypointIds[0])); IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getMarker(markerIds[0]));
} }
return true; return true;
case R.id.list_context_menu_edit: case R.id.list_context_menu_edit:
if (waypointIds.length == 1) { if (markerIds.length == 1) {
intent = IntentUtils.newIntent(this, MarkerEditActivity.class) intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointIds[0]); .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]);
startActivity(intent); startActivity(intent);
} }
return true; return true;
case R.id.list_context_menu_delete: case R.id.list_context_menu_delete:
if (waypointIds.length > 1 && waypointIds.length == listView.getCount()) { if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
waypointIds = null; markerIds = null;
} }
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointIds); DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerIds);
return true; return true;
case R.id.list_context_menu_select_all: case R.id.list_context_menu_select_all:
for (int i = 0; i < listView.getCount(); i++) { for (int i = 0; i < listView.getCount(); i++) {
@@ -41,8 +41,8 @@ import java.util.SortedSet;
import de.dennisguse.opentracks.content.SearchEngine; import de.dennisguse.opentracks.content.SearchEngine;
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult; import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery; import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment; import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
@@ -204,7 +204,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
listView.setOnItemClickListener((parent, view, position, id) -> { listView.setOnItemClickListener((parent, view, position, id) -> {
Map<String, Object> item = arrayAdapter.getItem(position); Map<String, Object> item = arrayAdapter.getItem(position);
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD); Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
Long markerId = (Long) item.get(MARKER_ID_FIELD); Marker.Id markerId = (Marker.Id) item.get(MARKER_ID_FIELD);
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class); Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class);
if (markerId != null) { if (markerId != null) {
intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId); intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId);
@@ -271,7 +271,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
} }
Map<String, Object> item = arrayAdapter.getItem(positions[0]); Map<String, Object> item = arrayAdapter.getItem(positions[0]);
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD); Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
Waypoint.Id waypointId = (Waypoint.Id) item.get(MARKER_ID_FIELD); Marker.Id markerId = (Marker.Id) item.get(MARKER_ID_FIELD);
Intent intent; Intent intent;
switch (itemId) { switch (itemId) {
case R.id.list_context_menu_show_on_map: case R.id.list_context_menu_show_on_map:
@@ -283,9 +283,9 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.list_context_menu_edit: case R.id.list_context_menu_edit:
if (waypointId != null) { if (markerId != null) {
intent = IntentUtils.newIntent(this, MarkerEditActivity.class) intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointId); .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
} else { } else {
intent = IntentUtils.newIntent(this, TrackEditActivity.class) intent = IntentUtils.newIntent(this, TrackEditActivity.class)
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId); .putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
@@ -296,8 +296,8 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
finish(); finish();
return true; return true;
case R.id.list_context_menu_delete: case R.id.list_context_menu_delete:
if (waypointId != null) { if (markerId != null) {
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointId); DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerId);
} else { } else {
deleteTracks(trackId); deleteTracks(trackId);
} }
@@ -349,30 +349,24 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
* @return a list of result maps * @return a list of result maps
*/ */
private List<Map<String, Object>> prepareResultsforDisplay(Collection<ScoredResult> scoredResults) { private List<Map<String, Object>> prepareResultsforDisplay(Collection<ScoredResult> scoredResults) {
//TODO Replace use of map<string, object>, but rather provide Track or Waypoint directly. //TODO Replace use of map<string, object>, but rather provide Track or Marker directly.
ArrayList<Map<String, Object>> output = new ArrayList<>(scoredResults.size()); ArrayList<Map<String, Object>> output = new ArrayList<>(scoredResults.size());
for (ScoredResult result : scoredResults) { for (ScoredResult result : scoredResults) {
if (result.track != null) { if (result.track != null) {
output.add(prepareTrackForDisplay(result.track)); output.add(prepareTrackForDisplay(result.track));
} else { } else {
output.add(prepareMarkerForDisplay(result.waypoint)); output.add(prepareMarkerForDisplay(result.marker));
} }
} }
return output; return output;
} }
/** private Map<String, Object> prepareMarkerForDisplay(Marker marker) {
* Prepares a marker for display by filling in a result map.
*
* @param waypoint the marker
* @return the result map
*/
private Map<String, Object> prepareMarkerForDisplay(Waypoint waypoint) {
Map<String, Object> resultMap = new HashMap<>(); Map<String, Object> resultMap = new HashMap<>();
//TODO: It may be more appropriate to obtain the track name as a join in the retrieval phase of the searchable. //TODO: It may be more appropriate to obtain the track name as a join in the retrieval phase of the searchable.
String trackName = null; String trackName = null;
Track.Id trackId = waypoint.getTrackId(); Track.Id trackId = marker.getTrackId();
if (!trackId.isValid()) { if (!trackId.isValid()) {
Track track = contentProviderUtils.getTrack(trackId); Track track = contentProviderUtils.getTrack(trackId);
if (track != null) { if (track != null) {
@@ -384,20 +378,20 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
resultMap.put(IS_PAUSED_FIELD, true); resultMap.put(IS_PAUSED_FIELD, true);
resultMap.put(ICON_ID_FIELD, MarkerUtils.ICON_ID); resultMap.put(ICON_ID_FIELD, MarkerUtils.ICON_ID);
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker); resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker);
resultMap.put(NAME_FIELD, waypoint.getName()); resultMap.put(NAME_FIELD, marker.getName());
// Display the marker's track name in the total time field // Display the marker's track name in the total time field
resultMap.put(TOTAL_TIME_FIELD, trackName == null ? null : getString(R.string.search_list_marker_track_location, trackName)); resultMap.put(TOTAL_TIME_FIELD, trackName == null ? null : getString(R.string.search_list_marker_track_location, trackName));
resultMap.put(TOTAL_DISTANCE_FIELD, null); resultMap.put(TOTAL_DISTANCE_FIELD, null);
resultMap.put(MARKER_COUNT_FIELD, 0); resultMap.put(MARKER_COUNT_FIELD, 0);
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime()); resultMap.put(START_TIME_FIELD, marker.getLocation().getTime());
resultMap.put(CATEGORY_FIELD, waypoint.getCategory()); resultMap.put(CATEGORY_FIELD, marker.getCategory());
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription()); resultMap.put(DESCRIPTION_FIELD, marker.getDescription());
resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl()); resultMap.put(PHOTO_URL_FIELD, marker.getPhotoUrl());
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId()); resultMap.put(TRACK_ID_FIELD, marker.getTrackId());
resultMap.put(MARKER_ID_FIELD, waypoint.getId()); resultMap.put(MARKER_ID_FIELD, marker.getId());
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude()); resultMap.put(MARKER_LATITUDE_FIELD, marker.getLocation().getLatitude());
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude()); resultMap.put(MARKER_LONGITUDE_FIELD, marker.getLocation().getLongitude());
return resultMap; return resultMap;
} }
@@ -415,14 +409,14 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
String icon = track.getIcon(); String icon = track.getIcon();
String category = icon != null && !icon.equals("") ? null : track.getCategory(); String category = icon != null && !icon.equals("") ? null : track.getCategory();
resultMap.put(IS_RECORDING_FIELD, track.getId() == recordingTrackId); resultMap.put(IS_RECORDING_FIELD, track.getId().equals(recordingTrackId));
resultMap.put(IS_PAUSED_FIELD, recordingTrackPaused); resultMap.put(IS_PAUSED_FIELD, recordingTrackPaused);
resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon)); resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon));
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_track); resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_track);
resultMap.put(NAME_FIELD, track.getName()); resultMap.put(NAME_FIELD, track.getName());
resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(trackStatistics.getTotalTime())); resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, trackStatistics.getTotalDistance(), metricUnits)); resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, trackStatistics.getTotalDistance(), metricUnits));
resultMap.put(MARKER_COUNT_FIELD, contentProviderUtils.getWaypointCount(track.getId())); resultMap.put(MARKER_COUNT_FIELD, contentProviderUtils.getMarkerCount(track.getId()));
resultMap.put(START_TIME_FIELD, trackStatistics.getStartTime_ms()); resultMap.put(START_TIME_FIELD, trackStatistics.getStartTime_ms());
resultMap.put(CATEGORY_FIELD, category); resultMap.put(CATEGORY_FIELD, category);
resultMap.put(DESCRIPTION_FIELD, track.getDescription()); resultMap.put(DESCRIPTION_FIELD, track.getDescription());
@@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity;
import java.util.Set; import java.util.Set;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.provider.ShareContentProvider; import de.dennisguse.opentracks.content.provider.ShareContentProvider;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.IntentDashboardUtils; import de.dennisguse.opentracks.util.IntentDashboardUtils;
@@ -31,7 +30,7 @@ public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Set<Track.Id> trackIds = IntentDashboardUtils.extractTrackIdsFromIntent(getIntent()); Set<Track.Id> trackIds = IntentDashboardUtils.extractTrackIdsFromIntent(getIntent());
showTrackTrackfileFormat(this, trackFileFormat, trackIds); showTrackfileFormat(this, trackFileFormat, trackIds);
finish(); finish();
} }
@@ -42,14 +41,14 @@ public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
* @param context the context * @param context the context
* @param trackIds the track ids * @param trackIds the track ids
*/ */
private static void showTrackTrackfileFormat(Context context, TrackFileFormat trackFileFormat, Set<Track.Id> trackIds) { private static void showTrackfileFormat(Context context, TrackFileFormat trackFileFormat, Set<Track.Id> trackIds) {
if (trackIds.size() == 0) { if (trackIds.size() == 0) {
return; return;
} }
Intent intent = new Intent(android.content.Intent.ACTION_VIEW); Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(TrackPointsColumns.TRACKID, trackIds.toArray(new Track.Id[0])[0]);
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", trackFileFormat); Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", trackFileFormat);
intent.setDataAndType(uriAndMime.first, uriAndMime.second); intent.setDataAndType(uriAndMime.first, uriAndMime.second);
@@ -6,6 +6,7 @@ import android.util.Log;
import java.util.Locale; import java.util.Locale;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
/** /**
@@ -32,6 +33,8 @@ public class Startup extends Application {
PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits); PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits);
} }
ActivityUtils.applyNightMode(this);
//TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed. //TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed.
if (PreferencesUtils.isRecording(this)) { if (PreferencesUtils.isRecording(this)) {
Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed."); Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed.");
@@ -24,11 +24,12 @@ import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner; import android.widget.Spinner;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.material.textfield.TextInputEditText;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
@@ -55,10 +56,10 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
private Track track; private Track track;
private String iconValue; private String iconValue;
private EditText nameEditText; private TextInputEditText nameEditText;
private AutoCompleteTextView activityType; private AutoCompleteTextView activityType;
private Spinner activityTypeIcon; private Spinner activityTypeIcon;
private EditText description; private TextInputEditText description;
@Override @Override
protected void onCreate(Bundle bundle) { protected void onCreate(Bundle bundle) {
@@ -75,7 +76,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
contentProviderUtils = new ContentProviderUtils(this); contentProviderUtils = new ContentProviderUtils(this);
track = contentProviderUtils.getTrack(trackId); track = contentProviderUtils.getTrack(trackId);
if (track == null) { if (track == null) {
Log.e(TAG, "No track for " + trackId); Log.e(TAG, "No track for " + trackId.getId());
finish(); finish();
return; return;
} }
@@ -214,6 +214,9 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
// Reset theme after splash
setTheme(R.style.ThemeCustom);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
gpsStatusValue = GpsStatusValue.GPS_NONE; gpsStatusValue = GpsStatusValue.GPS_NONE;
@@ -263,13 +266,13 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION); int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
Track.Id trackId = new Track.Id(cursor.getLong(idIndex)); Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
boolean isRecording = trackId == recordingTrackId; boolean isRecording = trackId.equals(recordingTrackId);
String icon = cursor.getString(iconIndex); String icon = cursor.getString(iconIndex);
int iconId = TrackIconUtils.getIconDrawable(icon); int iconId = TrackIconUtils.getIconDrawable(icon);
String name = cursor.getString(nameIndex); String name = cursor.getString(nameIndex);
String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex)); String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex));
String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits); String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits);
int markerCount = contentProviderUtils.getWaypointCount(trackId); int markerCount = contentProviderUtils.getMarkerCount(trackId);
long startTime = cursor.getLong(startTimeIndex); long startTime = cursor.getLong(startTimeIndex);
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex); String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
String description = cursor.getString(descriptionIndex); String description = cursor.getString(descriptionIndex);
@@ -24,18 +24,20 @@ import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ChartFragment; import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.IntervalsFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment; import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.SettingsActivity; import de.dennisguse.opentracks.settings.SettingsActivity;
@@ -53,11 +55,11 @@ import de.dennisguse.opentracks.util.TrackUtils;
*/ */
public class TrackRecordedActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface { public class TrackRecordedActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface {
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
public static final String EXTRA_TRACK_ID = "track_id"; public static final String EXTRA_TRACK_ID = "track_id";
public static final String EXTRA_MARKER_ID = "marker_id"; public static final String EXTRA_MARKER_ID = "marker_id";
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key"; private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
// The following are set in onCreate. // The following are set in onCreate.
@@ -65,7 +67,6 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
private TrackDataHub trackDataHub; private TrackDataHub trackDataHub;
private ViewPager pager; private ViewPager pager;
// From intent.
private Track.Id trackId; private Track.Id trackId;
@Override @Override
@@ -77,42 +78,8 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
trackDataHub = new TrackDataHub(this); trackDataHub = new TrackDataHub(this);
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordedFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
}
return "Unknown Tab";
}
};
pager = findViewById(R.id.track_detail_activity_view_pager); pager = findViewById(R.id.track_detail_activity_view_pager);
pager.setAdapter(adapter); pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT));
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout); TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
tabs.setupWithViewPager(pager); tabs.setupWithViewPager(pager);
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -133,7 +100,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
// Update UI // Update UI
this.invalidateOptionsMenu(); this.invalidateOptionsMenu();
if (trackId.isValid()) { if (trackId != null) {
trackDataHub.loadTrack(trackId); trackDataHub.loadTrack(trackId);
} }
} }
@@ -240,15 +207,15 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
private void handleIntent(Intent intent) { private void handleIntent(Intent intent) {
trackId = intent.getParcelableExtra(EXTRA_TRACK_ID); trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
Waypoint.Id waypointId = intent.getParcelableExtra(EXTRA_MARKER_ID); Marker.Id markerId = intent.getParcelableExtra(EXTRA_MARKER_ID);
if (waypointId != null) { if (markerId != null) {
// Use the trackId from the marker // Use the trackId from the marker
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId); Marker marker = contentProviderUtils.getMarker(markerId);
if (waypoint == null) { if (marker == null) {
finish(); finish();
return; return;
} }
trackId = waypoint.getTrackId(); trackId = marker.getTrackId();
} }
if (trackId == null) { if (trackId == null) {
@@ -263,8 +230,53 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
@Override @Override
public void onChooseActivityTypeDone(String iconValue) { public void onChooseActivityTypeDone(String iconValue) {
Track track = contentProviderUtils.getTrack(trackId);
String category = getString(TrackIconUtils.getIconActivityType(iconValue)); String category = getString(TrackIconUtils.getIconActivityType(iconValue));
Track track = contentProviderUtils.getTrack(trackId);
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils); TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
} }
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
@Override
public int getCount() {
return 4;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return StatisticsRecordedFragment.newInstance(trackId);
case 1:
return IntervalsFragment.newInstance(trackId);
case 2:
return ChartFragment.newInstance(false);
case 3:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.track_detail_intervals_tab);
case 2:
return getString(R.string.settings_chart_by_time);
case 3:
return getString(R.string.settings_chart_by_distance);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
}
} }
@@ -25,6 +25,7 @@ import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.fragments.ChartFragment; import de.dennisguse.opentracks.fragments.ChartFragment;
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment; import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
import de.dennisguse.opentracks.fragments.IntervalsFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment; import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface; import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
@@ -65,6 +66,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
// Preferences // Preferences
private boolean recordingTrackPaused; private boolean recordingTrackPaused;
// Intervals recording fragment needs Track.Id when the activity creates it.
private OnTrackIdListener intervalsListener;
private final Runnable bindChangedCallback = new Runnable() { private final Runnable bindChangedCallback = new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -81,6 +85,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
if (trackId == null) { if (trackId == null) {
// trackId isn't initialized -> leads a new recording. // trackId isn't initialized -> leads a new recording.
trackId = service.startNewTrack(); trackId = service.startNewTrack();
if (intervalsListener != null) {
intervalsListener.onTrackId(trackId);
}
} else { } else {
// trackId is initialized -> resumes the track. // trackId is initialized -> resumes the track.
service.resumeTrack(trackId); service.resumeTrack(trackId);
@@ -166,6 +173,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
//Activity was recreated. //Activity was recreated.
trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID); trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID);
} else { } else {
// Resume a track
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID); trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
if (trackId != null && contentProviderUtils.getTrack(trackId) == null) { if (trackId != null && contentProviderUtils.getTrack(trackId) == null) {
Log.w(TAG, "TrackId does not exists; cannot continue the recording."); Log.w(TAG, "TrackId does not exists; cannot continue the recording.");
@@ -180,7 +188,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackDataHub = new TrackDataHub(this); trackDataHub = new TrackDataHub(this);
pager = findViewById(R.id.track_detail_activity_view_pager); pager = findViewById(R.id.track_detail_activity_view_pager);
pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), 1)); pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT));
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout); TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
tabs.setupWithViewPager(pager); tabs.setupWithViewPager(pager);
if (savedInstanceState != null) { if (savedInstanceState != null) {
@@ -372,7 +380,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
@Override @Override
public int getCount() { public int getCount() {
return 3; return 4;
} }
@NonNull @NonNull
@@ -380,10 +388,12 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
public Fragment getItem(int position) { public Fragment getItem(int position) {
switch (position) { switch (position) {
case 0: case 0:
return new StatisticsRecordingFragment(); return StatisticsRecordingFragment.newInstance();
case 1: case 1:
return ChartFragment.newInstance(false); return IntervalsFragment.IntervalsRecordingFragment.newInstance(trackId);
case 2: case 2:
return ChartFragment.newInstance(false);
case 3:
return ChartFragment.newInstance(true); return ChartFragment.newInstance(true);
default: default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position); throw new RuntimeException("There isn't Fragment associated with the position: " + position);
@@ -396,12 +406,25 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
case 0: case 0:
return getString(R.string.track_detail_stats_tab); return getString(R.string.track_detail_stats_tab);
case 1: case 1:
return getString(R.string.settings_chart_by_time); return getString(R.string.track_detail_intervals_tab);
case 2: case 2:
return getString(R.string.settings_chart_by_time);
case 3:
return getString(R.string.settings_chart_by_distance); return getString(R.string.settings_chart_by_distance);
default: default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position); throw new RuntimeException("There isn't Fragment associated with the position: " + position);
} }
} }
} }
public interface OnTrackIdListener {
void onTrackId(Track.Id trackId);
}
public void setTrackIdListener(OnTrackIdListener listener) {
this.intervalsListener = listener;
if (trackId != null) {
listener.onTrackId(trackId);
}
}
} }
@@ -0,0 +1,83 @@
package de.dennisguse.opentracks.adapters;
import android.content.Context;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.Interval> {
private StackMode stackMode;
private boolean metricUnits;
private float sumDistance_m;
public IntervalStatisticsAdapter(Context context, List<IntervalStatistics.Interval> intervalList, StackMode stackMode) {
super(context, R.layout.interval_stats_list_item, intervalList);
metricUnits = PreferencesUtils.isMetricUnits(context);
this.stackMode = stackMode;
}
@NonNull
@Override
public View getView(int position, @Nullable View intervalView, @NonNull ViewGroup parent) {
int actualPosition = stackMode == StackMode.STACK_FROM_TOP ? position : getCount() - 1 - position;
IntervalStatistics.Interval interval = getItem(actualPosition);
ViewHolder viewHolder;
if (intervalView == null) {
viewHolder = new ViewHolder();
intervalView = LayoutInflater.from(getContext()).inflate(R.layout.interval_stats_list_item, parent, false);
viewHolder.distance = intervalView.findViewById(R.id.interval_item_distance);
viewHolder.speed = intervalView.findViewById(R.id.interval_item_speed);
viewHolder.pace = intervalView.findViewById(R.id.interval_item_pace);
intervalView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) intervalView.getTag();
}
if (actualPosition + 1 == getCount() && actualPosition > 0) {
sumDistance_m = actualPosition * getItem(actualPosition - 1).getDistance_m() + interval.getDistance_m();
} else {
sumDistance_m = (actualPosition + 1) * interval.getDistance_m();
}
viewHolder.distance.setText(StringUtils.formatDistance(getContext(), sumDistance_m, metricUnits));
Pair<String, String> speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true);
viewHolder.speed.setText(speedParts.first + " " + speedParts.second);
Pair<String, String> paceParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, false);
viewHolder.pace.setText(paceParts.first + " " + paceParts.second);
return intervalView;
}
/**
* Defines the two modes of list items stacking: from top or from bottom.
*/
public enum StackMode {
STACK_FROM_BOTTOM,
STACK_FROM_TOP
}
private static class ViewHolder {
private TextView distance;
private TextView speed;
private TextView pace;
}
}
@@ -47,9 +47,9 @@ abstract class ChartValueSeries {
private final Paint strokePaint; private final Paint strokePaint;
private final Paint titlePaint; private final Paint titlePaint;
private final Paint markerPaint; private final Paint markerPaint;
private final ExtremityMonitor extremityMonitor; private final ExtremityMonitor extremityMonitor = new ExtremityMonitor();
private final NumberFormat numberFormat; private final NumberFormat numberFormat = NumberFormat.getIntegerInstance();
private final Path path; private final Path path = new Path();
private int interval = 1; private int interval = 1;
private int minMarkerValue = 0; private int minMarkerValue = 0;
@@ -91,17 +91,15 @@ abstract class ChartValueSeries {
titlePaint = new Paint(strokePaint); titlePaint = new Paint(strokePaint);
titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale); titlePaint.setTextSize(ChartView.MEDIUM_TEXT_SIZE * scale);
titlePaint.setTextAlign(Align.CENTER); titlePaint.setTextAlign(Align.CENTER);
titlePaint.setStyle(Style.FILL_AND_STROKE);
markerPaint = new Paint(strokePaint); markerPaint = new Paint(strokePaint);
markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale); markerPaint.setTextSize(ChartView.SMALL_TEXT_SIZE * scale);
markerPaint.setTextAlign(Align.RIGHT); markerPaint.setTextAlign(Align.RIGHT);
markerPaint.setStyle(Style.FILL_AND_STROKE);
// Set stroke paint thickness // Set stroke paint thickness
strokePaint.setStrokeWidth(STROKE_WIDTH); strokePaint.setStrokeWidth(STROKE_WIDTH);
extremityMonitor = new ExtremityMonitor();
numberFormat = NumberFormat.getIntegerInstance();
path = new Path();
} }
/** /**
@@ -19,7 +19,6 @@ package de.dennisguse.opentracks.chart;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect; import android.graphics.DashPathEffect;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Paint.Align; import android.graphics.Paint.Align;
@@ -42,11 +41,12 @@ import java.util.List;
import de.dennisguse.opentracks.MarkerDetailActivity; import de.dennisguse.opentracks.MarkerDetailActivity;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.stats.ExtremityMonitor; import de.dennisguse.opentracks.stats.ExtremityMonitor;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.MarkerUtils; import de.dennisguse.opentracks.util.MarkerUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.ThemeUtils;
import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.util.UnitConversions;
/** /**
@@ -75,7 +75,7 @@ public class ChartView extends View {
private static final int Y_AXIS_OFFSET = 16; private static final int Y_AXIS_OFFSET = 16;
//TODO Determine from actual size of the used drawable //TODO Determine from actual size of the used drawable
private static final float WAYPOINT_X_ANCHOR = 13f / 48f; private static final float MARKER_X_ANCHOR = 13f / 48f;
static { static {
X_FRACTION_FORMAT.setMaximumFractionDigits(1); X_FRACTION_FORMAT.setMaximumFractionDigits(1);
@@ -87,14 +87,15 @@ public class ChartView extends View {
private final ChartValueSeries paceSeries; private final ChartValueSeries paceSeries;
private final List<ChartPoint> chartPoints = new ArrayList<>(); private final List<ChartPoint> chartPoints = new ArrayList<>();
private final List<Waypoint> waypoints = new ArrayList<>(); private final List<Marker> markers = new ArrayList<>();
private final ExtremityMonitor xExtremityMonitor = new ExtremityMonitor(); private final ExtremityMonitor xExtremityMonitor = new ExtremityMonitor();
private final int backgroundColor;
private final Paint axisPaint; private final Paint axisPaint;
private final Paint xAxisMarkerPaint; private final Paint xAxisMarkerPaint;
private final Paint gridPaint; private final Paint gridPaint;
private final Paint markerPaint; private final Paint markerPaint;
private final Drawable pointer; private final Drawable pointer;
private final Drawable waypointMarker; private final Drawable markerPin;
private final int markerWidth; private final int markerWidth;
private final int markerHeight; private final int markerHeight;
private final Scroller scroller; private final Scroller scroller;
@@ -150,19 +151,19 @@ public class ChartView extends View {
// Check if the y event is within markerHeight of the marker center // Check if the y event is within markerHeight of the marker center
if (Math.abs(event.getY() - topBorder - spacer - markerHeight / 2f) < markerHeight) { if (Math.abs(event.getY() - topBorder - spacer - markerHeight / 2f) < markerHeight) {
int minDistance = Integer.MAX_VALUE; int minDistance = Integer.MAX_VALUE;
Waypoint nearestWaypoint = null; Marker nearestMarker = null;
synchronized (waypoints) { synchronized (markers) {
for (Waypoint waypoint : waypoints) { for (Marker marker : markers) {
int distance = Math.abs(getX(getWaypointXValue(waypoint)) - (int) event.getX() - getScrollX()); int distance = Math.abs(getX(getMarkerXValue(marker)) - (int) event.getX() - getScrollX());
if (distance < minDistance) { if (distance < minDistance) {
minDistance = distance; minDistance = distance;
nearestWaypoint = waypoint; nearestMarker = marker;
} }
} }
} }
if (nearestWaypoint != null && minDistance < markerWidth) { if (nearestMarker != null && minDistance < markerWidth) {
Intent intent = IntentUtils.newIntent(getContext(), MarkerDetailActivity.class) Intent intent = IntentUtils.newIntent(getContext(), MarkerDetailActivity.class)
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, nearestWaypoint.getId()); .putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, nearestMarker.getId());
getContext().startActivity(intent); getContext().startActivity(intent);
return true; return true;
} }
@@ -191,7 +192,6 @@ public class ChartView extends View {
public ChartView(Context context, final boolean chartByDistance) { public ChartView(Context context, final boolean chartByDistance) {
super(context); super(context);
this.chartByDistance = chartByDistance; this.chartByDistance = chartByDistance;
seriesList.add(new ChartValueSeries(context, seriesList.add(new ChartValueSeries(context,
Integer.MIN_VALUE, Integer.MIN_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE,
@@ -307,11 +307,13 @@ public class ChartView extends View {
} }
}); });
backgroundColor = ThemeUtils.getBackgroundColor(context);
float scale = context.getResources().getDisplayMetrics().density; float scale = context.getResources().getDisplayMetrics().density;
axisPaint = new Paint(); axisPaint = new Paint();
axisPaint.setStyle(Style.STROKE); axisPaint.setStyle(Style.FILL_AND_STROKE);
axisPaint.setColor(context.getResources().getColor(android.R.color.black)); axisPaint.setColor(ThemeUtils.getTextColorPrimary(context));
axisPaint.setAntiAlias(true); axisPaint.setAntiAlias(true);
axisPaint.setTextSize(SMALL_TEXT_SIZE * scale); axisPaint.setTextSize(SMALL_TEXT_SIZE * scale);
@@ -320,22 +322,21 @@ public class ChartView extends View {
gridPaint = new Paint(); gridPaint = new Paint();
gridPaint.setStyle(Style.STROKE); gridPaint.setStyle(Style.STROKE);
gridPaint.setColor(context.getResources().getColor(android.R.color.darker_gray)); gridPaint.setColor(ThemeUtils.getTextColorSecondary(context));
gridPaint.setAntiAlias(false); gridPaint.setAntiAlias(false);
gridPaint.setPathEffect(new DashPathEffect(new float[]{3, 2}, 0)); gridPaint.setPathEffect(new DashPathEffect(new float[]{3, 2}, 0));
markerPaint = new Paint(); markerPaint = new Paint();
markerPaint.setStyle(Style.STROKE); markerPaint.setStyle(Style.STROKE);
markerPaint.setColor(context.getResources().getColor(android.R.color.darker_gray));
markerPaint.setAntiAlias(false); markerPaint.setAntiAlias(false);
pointer = context.getResources().getDrawable(R.drawable.ic_logo_color_24dp); pointer = context.getResources().getDrawable(R.drawable.ic_logo_color_24dp);
pointer.setBounds(0, 0, pointer.getIntrinsicWidth(), pointer.getIntrinsicHeight()); pointer.setBounds(0, 0, pointer.getIntrinsicWidth(), pointer.getIntrinsicHeight());
waypointMarker = MarkerUtils.getDefaultPhoto(context); markerPin = MarkerUtils.getDefaultPhoto(context);
markerWidth = waypointMarker.getIntrinsicWidth(); markerWidth = markerPin.getIntrinsicWidth();
markerHeight = waypointMarker.getIntrinsicHeight(); markerHeight = markerPin.getIntrinsicHeight();
waypointMarker.setBounds(0, 0, markerWidth, markerHeight); markerPin.setBounds(0, 0, markerWidth, markerHeight);
scroller = new Scroller(context); scroller = new Scroller(context);
setFocusable(true); setFocusable(true);
@@ -434,15 +435,15 @@ public class ChartView extends View {
scrollTo(0, 0); scrollTo(0, 0);
} }
public void addWaypoint(Waypoint waypoint) { public void addMarker(Marker marker) {
synchronized (waypoints) { synchronized (markers) {
waypoints.add(waypoint); markers.add(marker);
} }
} }
public void clearWaypoints() { public void clearMarker() {
synchronized (waypoints) { synchronized (markers) {
waypoints.clear(); markers.clear();
} }
} }
@@ -540,13 +541,13 @@ public class ChartView extends View {
synchronized (chartPoints) { synchronized (chartPoints) {
canvas.save(); canvas.save();
canvas.drawColor(Color.WHITE); canvas.drawColor(backgroundColor);
canvas.save(); canvas.save();
clipToGraphArea(canvas); clipToGraphArea(canvas);
drawDataSeries(canvas); drawDataSeries(canvas);
drawWaypoints(canvas); drawMarker(canvas);
drawGrid(canvas); drawGrid(canvas);
canvas.restore(); canvas.restore();
@@ -587,24 +588,19 @@ public class ChartView extends View {
} }
} }
/** private void drawMarker(Canvas canvas) {
* Draws the waypoints. synchronized (markers) {
* for (Marker marker : markers) {
* @param canvas the canvas double xValue = getMarkerXValue(marker);
*/
private void drawWaypoints(Canvas canvas) {
synchronized (waypoints) {
for (Waypoint waypoint : waypoints) {
double xValue = getWaypointXValue(waypoint);
if (xValue > maxX) { if (xValue > maxX) {
continue; continue;
} }
canvas.save(); canvas.save();
float x = getX(getWaypointXValue(waypoint)); float x = getX(getMarkerXValue(marker));
canvas.drawLine(x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint); canvas.drawLine(x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
canvas.translate(x - (markerWidth * WAYPOINT_X_ANCHOR), topBorder + spacer); canvas.translate(x - (markerWidth * MARKER_X_ANCHOR), topBorder + spacer);
waypointMarker.draw(canvas); markerPin.draw(canvas);
canvas.restore(); canvas.restore();
} }
} }
@@ -904,7 +900,7 @@ public class ChartView extends View {
for (ChartValueSeries chartValueSeries : seriesList) { for (ChartValueSeries chartValueSeries : seriesList) {
chartValueSeries.updateDimension(); chartValueSeries.updateDimension();
} }
float density = getContext().getResources().getDisplayMetrics().density; float density = getResources().getDisplayMetrics().density;
spacer = (int) (density * SPACER); spacer = (int) (density * SPACER);
yAxisOffset = (int) (density * Y_AXIS_OFFSET); yAxisOffset = (int) (density * Y_AXIS_OFFSET);
@@ -975,17 +971,12 @@ public class ChartView extends View {
return topBorder + yAxisOffset + (int) ((1 - percentage) * rangeHeight); return topBorder + yAxisOffset + (int) ((1 - percentage) * rangeHeight);
} }
/** private double getMarkerXValue(Marker marker) {
* Gets a waypoint's x value.
*
* @param waypoint the waypoint
*/
private double getWaypointXValue(Waypoint waypoint) {
if (chartByDistance) { if (chartByDistance) {
double lenghtInKm = waypoint.getLength() * UnitConversions.M_TO_KM; double length_km = marker.getLength() * UnitConversions.M_TO_KM;
return metricUnits ? lenghtInKm : lenghtInKm * UnitConversions.KM_TO_MI; return metricUnits ? length_km : length_km * UnitConversions.KM_TO_MI;
} else { } else {
return waypoint.getDuration(); return marker.getDuration();
} }
} }
@@ -28,7 +28,7 @@ import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.util.UnitConversions;
/** /**
* Generates descriptions for tracks and waypoints. * Generates descriptions for tracks and markers.
* *
* @author Jimmy Shih * @author Jimmy Shih
*/ */
@@ -136,7 +136,9 @@ public class DescriptionGenerator {
} }
// Elevation gain // Elevation gain
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak); if (stats.hasTotalElevationGain()) {
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
}
// Recorded time // Recorded time
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime_ms()))); builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime_ms())));
@@ -28,17 +28,17 @@ import java.util.Locale;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns; 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.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.UnitConversions; import de.dennisguse.opentracks.util.UnitConversions;
/** /**
* Engine for searching for tracks and waypoints by text. * Engine for searching for tracks and markers by text.
* *
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
@@ -53,12 +53,12 @@ public class SearchEngine {
TracksColumns.CATEGORY + " LIKE ?"; TracksColumns.CATEGORY + " LIKE ?";
/** /**
* WHERE query to get waypoints by name. * WHERE query to get markers by name.
*/ */
private static final String WAYPOINT_SELECTION_QUERY = private static final String MARKER_SELECTION_QUERY =
WaypointsColumns.NAME + " LIKE ? OR " + MarkerColumns.NAME + " LIKE ? OR " +
WaypointsColumns.DESCRIPTION + " LIKE ? OR " + MarkerColumns.DESCRIPTION + " LIKE ? OR " +
WaypointsColumns.CATEGORY + " LIKE ?"; MarkerColumns.CATEGORY + " LIKE ?";
/** /**
* Order of track results. * Order of track results.
@@ -66,9 +66,9 @@ public class SearchEngine {
private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000"; private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000";
/** /**
* Order of waypoint results. * Order of marker results.
*/ */
private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC"; private static final String MARKER_SELECTION_ORDER = MarkerColumns._ID + " DESC";
/** /**
* How much we promote a match in the track category. * How much we promote a match in the track category.
@@ -86,9 +86,9 @@ public class SearchEngine {
private static final double TRACK_NAME_PROMOTION = 16.0; private static final double TRACK_NAME_PROMOTION = 16.0;
/** /**
* How much we promote a waypoint result if it's in the currently-selected track. * How much we promote a marker result if it's in the currently-selected track.
*/ */
private static final double CURRENT_TRACK_WAYPOINT_PROMOTION = 2.0; private static final double CURRENT_TRACK_MARKER_PROMOTION = 2.0;
/** /**
* How much we promote a track result if it's the currently-selected track. * How much we promote a track result if it's the currently-selected track.
@@ -96,9 +96,9 @@ public class SearchEngine {
private static final double CURRENT_TRACK_DEMOTION = 0.5; private static final double CURRENT_TRACK_DEMOTION = 0.5;
/** /**
* Maximum number of waypoints which will be retrieved and scored. * Maximum number of markers which will be retrieved and scored.
*/ */
private static final int MAX_SCORED_WAYPOINTS = 100; private static final int MAX_SCORED_MARKERS = 100;
/** /**
* Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000) * Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000)
@@ -115,16 +115,16 @@ public class SearchEngine {
return scoreDiff; return scoreDiff;
} }
// Make tracks come before waypoints. // Make tracks come before markers.
if (r1.waypoint != null && r2.track != null) { if (r1.marker != null && r2.track != null) {
return 1; return 1;
} else if (r1.track != null && r2.waypoint != null) { } else if (r1.track != null && r2.marker != null) {
return -1; return -1;
} }
// Finally, use arbitrary ordering, by ID. // Finally, use arbitrary ordering, by ID.
long id1 = r1.track != null ? r1.track.getId().getId() : r1.waypoint.getId().getId(); long id1 = r1.track != null ? r1.track.getId().getId() : r1.marker.getId().getId();
long id2 = r2.track != null ? r2.track.getId().getId() : r2.waypoint.getId().getId(); long id2 = r2.track != null ? r2.track.getId().getId() : r2.marker.getId().getId();
long idDiff = id2 - id1; long idDiff = id2 - id1;
return Long.signum(idDiff); return Long.signum(idDiff);
}; };
@@ -153,7 +153,7 @@ public class SearchEngine {
TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR); TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR);
scoreTrackResults(retrieveTracks(query), query, scoredResults); scoreTrackResults(retrieveTracks(query), query, scoredResults);
scoreWaypointResults(retrieveWaypoints(query), query, scoredResults); scoreMarkersResults(retrieveMarkers(query), query, scoredResults);
return scoredResults; return scoredResults;
} }
@@ -186,33 +186,33 @@ public class SearchEngine {
} }
/** /**
* Retrieves waypoints matching the given query from the database. * Retrieves markers matching the given query from the database.
* *
* @param query the query to retrieve for * @param query the query to retrieve for
*/ */
private List<Waypoint> retrieveWaypoints(SearchQuery query) { private List<Marker> retrieveMarkers(SearchQuery query) {
ArrayList<Waypoint> waypoints = new ArrayList<>(); ArrayList<Marker> markers = new ArrayList<>();
String queryLikeSelection2 = "%" + query.textQuery + "%"; String queryLikeSelection2 = "%" + query.textQuery + "%";
String[] waypointSelectionArgs = new String[]{ String[] markersSelectionArgs = new String[]{
queryLikeSelection2, queryLikeSelection2,
queryLikeSelection2, queryLikeSelection2,
queryLikeSelection2 queryLikeSelection2
}; };
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs, WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) { try (Cursor cursor = providerUtils.getMarkerCursor(MARKER_SELECTION_QUERY, markersSelectionArgs, MARKER_SELECTION_ORDER, MAX_SCORED_MARKERS)) {
if (cursor != null) { if (cursor != null) {
waypoints.ensureCapacity(cursor.getCount()); markers.ensureCapacity(cursor.getCount());
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
Waypoint waypoint = providerUtils.createWaypoint(cursor); Marker marker = providerUtils.createMarker(cursor);
if (LocationUtils.isValidLocation(waypoint.getLocation())) { if (LocationUtils.isValidLocation(marker.getLocation())) {
waypoints.add(waypoint); markers.add(marker);
} }
} }
} }
} }
return waypoints; return markers;
} }
/** /**
@@ -250,7 +250,7 @@ public class SearchEngine {
score *= getTimeBoost(query, meanTimestamp); score *= getTimeBoost(query, meanTimestamp);
// Score the currently-selected track lower (user is already there, wouldn't be searching for it). // Score the currently-selected track lower (user is already there, wouldn't be searching for it).
if (track.getId() == query.currentTrackId) { if (track.getId().equals(query.currentTrackId)) {
score *= CURRENT_TRACK_DEMOTION; score *= CURRENT_TRACK_DEMOTION;
} }
@@ -258,40 +258,40 @@ public class SearchEngine {
} }
/** /**
* Scores a collection of waypoint results. * Scores a collection of marker results.
* *
* @param waypoints the results to score * @param markers the results to score
* @param query the query to score for * @param query the query to score for
* @param output the collection to fill with scored results * @param output the collection to fill with scored results
*/ */
private void scoreWaypointResults(Collection<Waypoint> waypoints, SearchQuery query, Collection<ScoredResult> output) { private void scoreMarkersResults(Collection<Marker> markers, SearchQuery query, Collection<ScoredResult> output) {
for (Waypoint waypoint : waypoints) { for (Marker marker : markers) {
// Calculate the score. // Calculate the score.
double score = scoreWaypointResult(query, waypoint); double score = scoreMarkerResult(query, marker);
// Add to the output. // Add to the output.
output.add(new ScoredResult(waypoint, score)); output.add(new ScoredResult(marker, score));
} }
} }
/** /**
* Scores a single waypoint result. * Scores a single marker result.
* *
* @param query the query to score for * @param query the query to score for
* @param waypoint the results to score * @param marker the results to score
* @return the score for the waypoint * @return the score for the marker
*/ */
private double scoreWaypointResult(SearchQuery query, Waypoint waypoint) { private double scoreMarkerResult(SearchQuery query, Marker marker) {
double score = 1.0; double score = 1.0;
Location location = waypoint.getLocation(); Location location = marker.getLocation();
score *= getTitleBoost(query, waypoint.getName(), waypoint.getDescription(), waypoint.getCategory()); score *= getTitleBoost(query, marker.getName(), marker.getDescription(), marker.getCategory());
score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude()); score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude());
score *= getTimeBoost(query, location.getTime()); score *= getTimeBoost(query, location.getTime());
// Score waypoints in the currently-selected track higher (searching inside the current track). // Score markers in the currently-selected track higher (searching inside the current track).
if (waypoint.getTrackId() != null && waypoint.getTrackId().equals(query.currentTrackId)) { if (marker.getTrackId() != null && marker.getTrackId().equals(query.currentTrackId)) {
score *= CURRENT_TRACK_WAYPOINT_PROMOTION; score *= CURRENT_TRACK_MARKER_PROMOTION;
} }
return score; return score;
@@ -301,9 +301,9 @@ public class SearchEngine {
* Calculates the boosting of the score due to the field(s) in which the match occured. * Calculates the boosting of the score due to the field(s) in which the match occured.
* *
* @param query the query to boost for * @param query the query to boost for
* @param name the name of the track or waypoint * @param name the name of the track or marker
* @param description the description of the track or waypoint * @param description the description of the track or marker
* @param category the category of the track or waypoint * @param category the category of the track or marker
* @return the total boost to be applied to the result * @return the total boost to be applied to the result
*/ */
private double getTitleBoost(SearchQuery query, String name, String description, String category) { private double getTitleBoost(SearchQuery query, String name, String description, String category) {
@@ -398,18 +398,18 @@ public class SearchEngine {
*/ */
public static class ScoredResult { public static class ScoredResult {
public final Track track; public final Track track;
public final Waypoint waypoint; public final Marker marker;
final double score; final double score;
ScoredResult(Track track, double score) { ScoredResult(Track track, double score) {
this.track = track; this.track = track;
this.waypoint = null; this.marker = null;
this.score = score; this.score = score;
} }
ScoredResult(Waypoint waypoint, double score) { ScoredResult(Marker marker, double score) {
this.track = null; this.track = null;
this.waypoint = waypoint; this.marker = marker;
this.score = score; this.score = score;
} }
@@ -418,7 +418,7 @@ public class SearchEngine {
public String toString() { public String toString() {
return "ScoredResult [" return "ScoredResult ["
+ (track != null ? ("trackId=" + track.getId().getId() + ", ") : "") + (track != null ? ("trackId=" + track.getId().getId() + ", ") : "")
+ (waypoint != null ? ("wptId=" + waypoint.getId().getId() + ", ") : "") + (marker != null ? ("wptId=" + marker.getId().getId() + ", ") : "")
+ "score=" + score + "]"; + "score=" + score + "]";
} }
} }
@@ -32,12 +32,12 @@ import java.util.Collections;
import java.util.Set; import java.util.Set;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; 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.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.LocationUtils;
@@ -58,14 +58,14 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* We may display more than this number of points. * We may display more than this number of points.
*/ */
@Deprecated @Deprecated
private static final int TARGET_DISPLAYED_TRACK_POINTS = 5000; private static final int TARGET_DISPLAYED_TRACKPOINTS = 5000;
/** /**
* Maximum number of waypoints to displayed. * Maximum number of markers to displayed.
*/ */
@VisibleForTesting @VisibleForTesting
@Deprecated @Deprecated
private static final int MAX_DISPLAYED_WAYPOINTS = 128; private static final int MAX_DISPLAYED_MARKERS = 128;
private static final String TAG = TrackDataHub.class.getSimpleName(); private static final String TAG = TrackDataHub.class.getSimpleName();
@@ -90,11 +90,11 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
// Registered listeners // Registered listeners
private ContentObserver tracksTableObserver; private ContentObserver tracksTableObserver;
private ContentObserver waypointsTableObserver; private ContentObserver markersTableObserver;
private ContentObserver trackPointsTableObserver; private ContentObserver trackPointsTableObserver;
public TrackDataHub(Context context) { public TrackDataHub(Context context) {
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACK_POINTS); this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACKPOINTS);
} }
@VisibleForTesting @VisibleForTesting
@@ -126,13 +126,13 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
}; };
contentResolver.registerContentObserver(TracksColumns.CONTENT_URI, false, tracksTableObserver); contentResolver.registerContentObserver(TracksColumns.CONTENT_URI, false, tracksTableObserver);
waypointsTableObserver = new ContentObserver(handler) { markersTableObserver = new ContentObserver(handler) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
notifyWaypointsTableUpdate(trackDataManager.getListenerWaypoints()); notifyMarkersTableUpdate(trackDataManager.getListenerMarkers());
} }
}; };
contentResolver.registerContentObserver(WaypointsColumns.CONTENT_URI, false, waypointsTableObserver); contentResolver.registerContentObserver(MarkerColumns.CONTENT_URI, false, markersTableObserver);
trackPointsTableObserver = new ContentObserver(handler) { trackPointsTableObserver = new ContentObserver(handler) {
@Override @Override
@@ -165,7 +165,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
//Unregister listeners //Unregister listeners
ContentResolver contentResolver = context.getContentResolver(); ContentResolver contentResolver = context.getContentResolver();
contentResolver.unregisterContentObserver(tracksTableObserver); contentResolver.unregisterContentObserver(tracksTableObserver);
contentResolver.unregisterContentObserver(waypointsTableObserver); contentResolver.unregisterContentObserver(markersTableObserver);
contentResolver.unregisterContentObserver(trackPointsTableObserver); contentResolver.unregisterContentObserver(trackPointsTableObserver);
if (handlerThread != null) { if (handlerThread != null) {
@@ -191,9 +191,9 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* *
* @param trackDataListener the track data listener * @param trackDataListener the track data listener
*/ */
public void registerTrackDataListener(final TrackDataListener trackDataListener, final boolean tracksTable, final boolean waypointsTable, final boolean trackPointsTable_SampleIn, final boolean trackPointsTable_SampleOut) { public void registerTrackDataListener(final TrackDataListener trackDataListener, final boolean tracksTable, final boolean markersTable, final boolean trackPointsTable_SampleIn, final boolean trackPointsTable_SampleOut) {
runInHandlerThread(() -> { runInHandlerThread(() -> {
trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, waypointsTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut); trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, markersTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut);
if (started) { if (started) {
loadDataForListener(trackDataListener); loadDataForListener(trackDataListener);
} }
@@ -250,7 +250,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
listener.clearTrackPoints(); listener.clearTrackPoints();
} }
notifyTrackPointsTableUpdate(true, trackDataManager.getListenerTrackPoints_SampledIn(), trackDataManager.getListenerTrackPoints_SampledOut()); notifyTrackPointsTableUpdate(true, trackDataManager.getListenerTrackPoints_SampledIn(), trackDataManager.getListenerTrackPoints_SampledOut());
notifyWaypointsTableUpdate(trackDataManager.getListenerWaypoints()); notifyMarkersTableUpdate(trackDataManager.getListenerMarkers());
} }
/** /**
@@ -277,8 +277,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners); notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
} }
if (trackDataManager.listensForWaypoints(trackDataListener)) { if (trackDataManager.listensForMarkers(trackDataListener)) {
notifyWaypointsTableUpdate(trackDataListeners); notifyMarkersTableUpdate(trackDataListeners);
} }
} }
@@ -298,36 +298,36 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
} }
/** /**
* Notifies waypoint table update. * Notifies marker table update.
* Currently, reloads all the waypoints up to {@link #MAX_DISPLAYED_WAYPOINTS}. To be run in the {@link #handler} thread. * Currently, reloads all the markers up to {@link #MAX_DISPLAYED_MARKERS}. To be run in the {@link #handler} thread.
* *
* @param trackDataListeners the track data listeners to notify * @param trackDataListeners the track data listeners to notify
*/ */
private void notifyWaypointsTableUpdate(Set<TrackDataListener> trackDataListeners) { private void notifyMarkersTableUpdate(Set<TrackDataListener> trackDataListeners) {
if (trackDataListeners.isEmpty()) { if (trackDataListeners.isEmpty()) {
return; return;
} }
for (TrackDataListener trackDataListener : trackDataListeners) { for (TrackDataListener trackDataListener : trackDataListeners) {
trackDataListener.clearWaypoints(); trackDataListener.clearMarkers();
} }
try (Cursor cursor = contentProviderUtils.getWaypointCursor(selectedTrackId, null, MAX_DISPLAYED_WAYPOINTS)) { try (Cursor cursor = contentProviderUtils.getMarkerCursor(selectedTrackId, null, MAX_DISPLAYED_MARKERS)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
do { do {
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor); Marker marker = contentProviderUtils.createMarker(cursor);
if (!LocationUtils.isValidLocation(waypoint.getLocation())) { if (!LocationUtils.isValidLocation(marker.getLocation())) {
continue; continue;
} }
for (TrackDataListener trackDataListener : trackDataListeners) { for (TrackDataListener trackDataListener : trackDataListeners) {
trackDataListener.onNewWaypoint(waypoint); trackDataListener.onNewMarker(marker);
} }
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
} }
for (TrackDataListener trackDataListener : trackDataListeners) { for (TrackDataListener trackDataListener : trackDataListeners) {
trackDataListener.onNewWaypointsDone(); trackDataListener.onNewMarkersDone();
} }
} }
@@ -16,9 +16,9 @@
package de.dennisguse.opentracks.content; package de.dennisguse.opentracks.content;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
/** /**
* Listener for track data changes. * Listener for track data changes.
@@ -60,20 +60,20 @@ public interface TrackDataListener {
void onNewTrackPointsDone(); void onNewTrackPointsDone();
/** /**
* Called to clear previously sent waypoints. * Called to clear previously sent markers.
*/ */
void clearWaypoints(); void clearMarkers();
/** /**
* Called when a new waypoint is read. * Called when a new marker is read.
* *
* @param waypoint the waypoint * @param marker the marker
*/ */
void onNewWaypoint(Waypoint waypoint); void onNewMarker(Marker marker);
/** /**
* Called when finish sending new waypoints. * Called when finish sending new markers.
* This gets called after every batch of calls to {@link #clearWaypoints()} and {@link #onNewWaypoint(Waypoint)}. * This gets called after every batch of calls to {@link #clearMarkers()} and {@link #onNewMarker(Marker)}.
*/ */
void onNewWaypointsDone(); void onNewMarkersDone();
} }
@@ -29,32 +29,32 @@ class TrackDataManager {
private static final String TAG = TrackDataManager.class.getSimpleName(); private static final String TAG = TrackDataManager.class.getSimpleName();
private final Set<TrackDataListener> dataListenerTracks = new HashSet<>(); private final Set<TrackDataListener> dataListenerTracks = new HashSet<>();
private final Set<TrackDataListener> dataListenerWaypoints = new HashSet<>(); private final Set<TrackDataListener> dataListenerMarkers = new HashSet<>();
private final Set<TrackDataListener> dataListenerTrackPoints_SampledIn = new HashSet<>(); private final Set<TrackDataListener> dataListenerTrackPoints_SampledIn = new HashSet<>();
private final Set<TrackDataListener> dataListenerTrackPoints_SampledOut = new HashSet<>(); private final Set<TrackDataListener> dataListenerTrackPoints_SampledOut = new HashSet<>();
void registerTrackDataListener(final TrackDataListener trackDataListener, boolean tracksTable, boolean waypointsTable, boolean trackPointsTable_SampleIn, boolean trackPointsTable_SampleOut) { void registerTrackDataListener(final TrackDataListener trackDataListener, boolean tracksTable, boolean markersTable, boolean trackPointsTable_SampleIn, boolean trackPointsTable_SampleOut) {
if (tracksTable) dataListenerTracks.add(trackDataListener); if (tracksTable) dataListenerTracks.add(trackDataListener);
if (waypointsTable) dataListenerWaypoints.add(trackDataListener); if (markersTable) dataListenerMarkers.add(trackDataListener);
if (trackPointsTable_SampleIn) dataListenerTrackPoints_SampledIn.add(trackDataListener); if (trackPointsTable_SampleIn) dataListenerTrackPoints_SampledIn.add(trackDataListener);
if (trackPointsTable_SampleOut) dataListenerTrackPoints_SampledOut.add(trackDataListener); if (trackPointsTable_SampleOut) dataListenerTrackPoints_SampledOut.add(trackDataListener);
} }
void unregisterTrackDataListener(TrackDataListener trackDataListener) { void unregisterTrackDataListener(TrackDataListener trackDataListener) {
dataListenerTracks.add(trackDataListener); dataListenerTracks.add(trackDataListener);
dataListenerWaypoints.add(trackDataListener); dataListenerMarkers.add(trackDataListener);
dataListenerTrackPoints_SampledIn.add(trackDataListener); dataListenerTrackPoints_SampledIn.add(trackDataListener);
dataListenerTrackPoints_SampledOut.add(trackDataListener); dataListenerTrackPoints_SampledOut.add(trackDataListener);
} }
boolean hasListeners() { boolean hasListeners() {
return dataListenerTracks.size() + dataListenerWaypoints.size() + dataListenerTrackPoints_SampledIn.size() + dataListenerTrackPoints_SampledOut.size() > 0; return dataListenerTracks.size() + dataListenerMarkers.size() + dataListenerTrackPoints_SampledIn.size() + dataListenerTrackPoints_SampledOut.size() > 0;
} }
int getNumberOfListeners() { int getNumberOfListeners() {
Set<TrackDataListener> listener = new HashSet<>(); Set<TrackDataListener> listener = new HashSet<>();
listener.addAll(dataListenerTracks); listener.addAll(dataListenerTracks);
listener.addAll(dataListenerWaypoints); listener.addAll(dataListenerMarkers);
listener.addAll(dataListenerTrackPoints_SampledIn); listener.addAll(dataListenerTrackPoints_SampledIn);
listener.addAll(dataListenerTrackPoints_SampledOut); listener.addAll(dataListenerTrackPoints_SampledOut);
@@ -65,8 +65,8 @@ class TrackDataManager {
return dataListenerTracks.contains(listener); return dataListenerTracks.contains(listener);
} }
boolean listensForWaypoints(TrackDataListener listener) { boolean listensForMarkers(TrackDataListener listener) {
return dataListenerWaypoints.contains(listener); return dataListenerMarkers.contains(listener);
} }
boolean listensForTrackPoints_SampledIn(TrackDataListener listener) { boolean listensForTrackPoints_SampledIn(TrackDataListener listener) {
@@ -81,8 +81,8 @@ class TrackDataManager {
return dataListenerTracks; return dataListenerTracks;
} }
Set<TrackDataListener> getListenerWaypoints() { Set<TrackDataListener> getListenerMarkers() {
return dataListenerWaypoints; return dataListenerMarkers;
} }
Set<TrackDataListener> getListenerTrackPoints_SampledIn() { Set<TrackDataListener> getListenerTrackPoints_SampledIn() {
@@ -30,12 +30,10 @@ import java.util.Objects;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
/** /**
* A waypoint.
*
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public final class Waypoint { public final class Marker {
private Id id; private Id id;
private String name = ""; private String name = "";
@@ -50,15 +48,15 @@ public final class Waypoint {
private String photoUrl = ""; private String photoUrl = "";
@VisibleForTesting @VisibleForTesting
public Waypoint(@NonNull TrackPoint trackPoint) { public Marker(@NonNull TrackPoint trackPoint) {
this.location = trackPoint.getLocation(); this.location = trackPoint.getLocation();
} }
public Waypoint(@NonNull Location location) { public Marker(@NonNull Location location) {
this.location = location; this.location = location;
} }
public Waypoint(String name, String description, String category, String icon, @NonNull Track.Id trackId, double length, long duration, @NonNull Location location, String photoUrl) { public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, double length, long duration, @NonNull Location location, String photoUrl) {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.category = category; this.category = category;
@@ -71,7 +69,7 @@ public final class Waypoint {
} }
/** /**
* May be null if the waypoint was not loaded from the database. * May be null if the it was not loaded from the database.
*/ */
public @Nullable public @Nullable
Id getId() { Id getId() {
@@ -167,16 +165,12 @@ public final class Waypoint {
this.id = id; this.id = id;
} }
public Id(@NonNull String id) {
this(Long.parseLong(id));
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider //TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() { public long getId() {
return id; return id;
} }
@Deprecated //TODO Use a Waypoint.Id of null instead @Deprecated //TODO Use a Id of null instead
public boolean isValid() { public boolean isValid() {
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT; return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
} }
@@ -22,11 +22,11 @@ import android.provider.BaseColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
/** /**
* Constants for waypoints table. * Constants for markers table.
* *
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
*/ */
public interface WaypointsColumns extends BaseColumns { public interface MarkerColumns extends BaseColumns {
String TABLE_NAME = "waypoints"; String TABLE_NAME = "waypoints";
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME); Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
@@ -36,10 +36,10 @@ public interface WaypointsColumns extends BaseColumns {
String DEFAULT_SORT_ORDER = _ID; String DEFAULT_SORT_ORDER = _ID;
// Columns // Columns
String NAME = "name"; // waypoint name String NAME = "name"; // marker name
String DESCRIPTION = "description"; // waypoint description String DESCRIPTION = "description"; // marker description
String CATEGORY = "category"; // waypoint category String CATEGORY = "category"; // marker category
String ICON = "icon"; // waypoint icon String ICON = "icon"; // marker icon
String TRACKID = "trackid"; // track id String TRACKID = "trackid"; // track id
String LENGTH = "length"; // length of the track (without smoothing) String LENGTH = "length"; // length of the track (without smoothing)
@@ -118,10 +118,6 @@ public class Track {
this.id = id; this.id = id;
} }
public Id(@NonNull String id) {
this(Long.parseLong(id));
}
//TOOD Limit visibility to TrackRecordingService / ContentProvider //TOOD Limit visibility to TrackRecordingService / ContentProvider
public long getId() { public long getId() {
return id; return id;
@@ -32,6 +32,7 @@ public class TrackPoint {
private Float heartRate_bpm = null; private Float heartRate_bpm = null;
private Float cyclingCadence_rpm = null; private Float cyclingCadence_rpm = null;
private Float power = null; private Float power = null;
private Float elevationGain = null;
public TrackPoint() { public TrackPoint() {
this.location = new Location(""); this.location = new Location("");
@@ -47,6 +48,8 @@ public class TrackPoint {
this.heartRate_bpm = trackPoint.getHeartRate_bpm(); this.heartRate_bpm = trackPoint.getHeartRate_bpm();
this.cyclingCadence_rpm = trackPoint.getCyclingCadence_rpm(); this.cyclingCadence_rpm = trackPoint.getCyclingCadence_rpm();
this.power = trackPoint.getPower(); this.power = trackPoint.getPower();
this.elevationGain = trackPoint.getElevationGain();
} }
public TrackPoint(double latitude, double longitude, Double altitude, long time) { public TrackPoint(double latitude, double longitude, Double altitude, long time) {
@@ -59,10 +62,12 @@ public class TrackPoint {
location.setTime(time); location.setTime(time);
} }
@Deprecated //See #316
public static TrackPoint createPause() { public static TrackPoint createPause() {
return createPauseWithTime(System.currentTimeMillis()); return createPauseWithTime(System.currentTimeMillis());
} }
@Deprecated //See #316
public static TrackPoint createPauseWithTime(long time) { public static TrackPoint createPauseWithTime(long time) {
Location pause = new Location(LocationManager.GPS_PROVIDER); Location pause = new Location(LocationManager.GPS_PROVIDER);
pause.setLongitude(0); pause.setLongitude(0);
@@ -71,10 +76,12 @@ public class TrackPoint {
return new TrackPoint(pause); return new TrackPoint(pause);
} }
@Deprecated //See #316
public static TrackPoint createResume() { public static TrackPoint createResume() {
return createResumeWithTime(System.currentTimeMillis()); return createResumeWithTime(System.currentTimeMillis());
} }
@Deprecated //See #316
public static TrackPoint createResumeWithTime(long time) { public static TrackPoint createResumeWithTime(long time) {
Location resume = new Location(LocationManager.GPS_PROVIDER); Location resume = new Location(LocationManager.GPS_PROVIDER);
resume.setLongitude(0); resume.setLongitude(0);
@@ -88,6 +95,18 @@ public class TrackPoint {
return location; return location;
} }
public boolean hasElevationGain() {
return elevationGain != null;
}
public float getElevationGain() {
return elevationGain;
}
public void setElevationGain(Float elevationGain) {
this.elevationGain = elevationGain;
}
public double getLatitude() { public double getLatitude() {
return location.getLatitude(); return location.getLatitude();
} }
@@ -51,6 +51,7 @@ public interface TrackPointsColumns extends BaseColumns {
String SENSOR_HEARTRATE = "sensor_heartrate"; String SENSOR_HEARTRATE = "sensor_heartrate";
String SENSOR_CADENCE = "sensor_cadence"; String SENSOR_CADENCE = "sensor_cadence";
String SENSOR_POWER = "sensor_power"; String SENSOR_POWER = "sensor_power";
String ELEVATION_GAIN = "elevation_gain";
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
@@ -64,7 +65,8 @@ public interface TrackPointsColumns extends BaseColumns {
+ BEARING + " FLOAT, " + BEARING + " FLOAT, "
+ SENSOR_HEARTRATE + " FLOAT, " + SENSOR_HEARTRATE + " FLOAT, "
+ SENSOR_CADENCE + " FLOAT, " + SENSOR_CADENCE + " FLOAT, "
+ SENSOR_POWER + " FLOAT)"; + SENSOR_POWER + " FLOAT, "
+ ELEVATION_GAIN + " FLOAT)";
String CREATE_TABLE_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + TRACKID + "_index ON " + TABLE_NAME + "(" + TRACKID + ")"; String CREATE_TABLE_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + TRACKID + "_index ON " + TABLE_NAME + "(" + TRACKID + ")";
} }
@@ -19,6 +19,7 @@ class CachedTrackPointsIndexes {
final int sensorHeartRateIndex; final int sensorHeartRateIndex;
final int sensorCadenceIndex; final int sensorCadenceIndex;
final int sensorPowerIndex; final int sensorPowerIndex;
final int elevationGainIndex;
CachedTrackPointsIndexes(Cursor cursor) { CachedTrackPointsIndexes(Cursor cursor) {
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID); idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
@@ -32,5 +33,6 @@ class CachedTrackPointsIndexes {
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE); sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE); sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER); sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
elevationGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_GAIN);
} }
} }
@@ -40,12 +40,12 @@ import java.util.UUID;
import de.dennisguse.opentracks.BuildConfig; import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.android.ContentResolverWrapper; import de.dennisguse.opentracks.android.ContentResolverWrapper;
import de.dennisguse.opentracks.android.IContentResolver; import de.dennisguse.opentracks.android.IContentResolver;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; 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.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.TrackUtils; import de.dennisguse.opentracks.util.TrackUtils;
@@ -69,8 +69,8 @@ public class ContentProviderUtils {
// The base URI for the app's content provider. // The base URI for the app's content provider.
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE; public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
// Maximum number of waypoints that will be loaded at one time. // Maximum number of markers that will be loaded at one time.
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000; public static final int MAX_LOADED_MARKERS = 10000;
private static final String ID_SEPARATOR = ","; private static final String ID_SEPARATOR = ",";
private final IContentResolver contentResolver; private final IContentResolver contentResolver;
@@ -89,13 +89,13 @@ public class ContentProviderUtils {
} }
/** /**
* Clears a track: removes waypoints and trackPoints. * Clears a track: removes markers and trackPoints.
* Only keeps the track id. * Only keeps the track id.
* *
* @param trackId the track id * @param trackId the track id
*/ */
public void clearTrack(Track.Id trackId) { public void clearTrack(Track.Id trackId) {
deleteTrackPointsAndWaypoints(trackId); deleteTrackPointsAndMarkers(trackId);
Track track = new Track(); Track track = new Track();
track.setId(trackId); track.setId(trackId);
updateTrack(track); updateTrack(track);
@@ -176,7 +176,7 @@ public class ContentProviderUtils {
@VisibleForTesting @VisibleForTesting
public void deleteAllTracks(Context context) { public void deleteAllTracks(Context context) {
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null); contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null);
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null); contentResolver.delete(MarkerColumns.CONTENT_URI, null, null);
// Delete tracks last since it triggers a database vaccum call // Delete tracks last since it triggers a database vaccum call
contentResolver.delete(TracksColumns.CONTENT_URI, null, null); contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
@@ -193,7 +193,7 @@ public class ContentProviderUtils {
if (trackId == null) { if (trackId == null) {
return; return;
} }
deleteTrackPointsAndWaypoints(trackId); deleteTrackPointsAndMarkers(trackId);
// Delete track folder resources. // Delete track folder resources.
FileUtils.deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId)); FileUtils.deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId));
@@ -202,16 +202,11 @@ public class ContentProviderUtils {
contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", new String[]{Long.toString(trackId.getId())}); contentResolver.delete(TracksColumns.CONTENT_URI, TracksColumns._ID + "=?", new String[]{Long.toString(trackId.getId())});
} }
/** private void deleteTrackPointsAndMarkers(@NonNull Track.Id trackId) {
* Deletes trackPoints and waypoints of a track.
*
* @param trackId the track id
*/
private void deleteTrackPointsAndWaypoints(@NonNull Track.Id trackId) {
String[] selectionArgs = new String[]{Long.toString(trackId.getId())}; String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs); contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs);
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?", selectionArgs); contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns.TRACKID + "=?", selectionArgs);
} }
@VisibleForTesting @VisibleForTesting
@@ -338,28 +333,22 @@ public class ContentProviderUtils {
return values; return values;
} }
public Marker createMarker(Cursor cursor) {
/** int idIndex = cursor.getColumnIndexOrThrow(MarkerColumns._ID);
* Creates a waypoint from a cursor. int nameIndex = cursor.getColumnIndexOrThrow(MarkerColumns.NAME);
* int descriptionIndex = cursor.getColumnIndexOrThrow(MarkerColumns.DESCRIPTION);
* @param cursor the cursor pointing to the waypoint int categoryIndex = cursor.getColumnIndexOrThrow(MarkerColumns.CATEGORY);
*/ int iconIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ICON);
public Waypoint createWaypoint(Cursor cursor) { int trackIdIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TRACKID);
int idIndex = cursor.getColumnIndexOrThrow(WaypointsColumns._ID); int lengthIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LENGTH);
int nameIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.NAME); int durationIndex = cursor.getColumnIndexOrThrow(MarkerColumns.DURATION);
int descriptionIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.DESCRIPTION); int longitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LONGITUDE);
int categoryIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.CATEGORY); int latitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.LATITUDE);
int iconIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ICON); int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
int trackIdIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TRACKID); int altitudeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ALTITUDE);
int lengthIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LENGTH); int accuracyIndex = cursor.getColumnIndexOrThrow(MarkerColumns.ACCURACY);
int durationIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.DURATION); int bearingIndex = cursor.getColumnIndexOrThrow(MarkerColumns.BEARING);
int longitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LONGITUDE); int photoUrlIndex = cursor.getColumnIndexOrThrow(MarkerColumns.PHOTOURL);
int latitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LATITUDE);
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
int altitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ALTITUDE);
int accuracyIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ACCURACY);
int bearingIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.BEARING);
int photoUrlIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.PHOTOURL);
Location location = new Location(""); Location location = new Location("");
if (!cursor.isNull(longitudeIndex) && !cursor.isNull(latitudeIndex)) { if (!cursor.isNull(longitudeIndex) && !cursor.isNull(latitudeIndex)) {
@@ -379,59 +368,56 @@ public class ContentProviderUtils {
location.setBearing(cursor.getFloat(bearingIndex)); location.setBearing(cursor.getFloat(bearingIndex));
} }
Waypoint waypoint = new Waypoint(location); Marker marker = new Marker(location);
if (!cursor.isNull(idIndex)) { if (!cursor.isNull(idIndex)) {
waypoint.setId(new Waypoint.Id(cursor.getLong(idIndex))); marker.setId(new Marker.Id(cursor.getLong(idIndex)));
} }
if (!cursor.isNull(nameIndex)) { if (!cursor.isNull(nameIndex)) {
waypoint.setName(cursor.getString(nameIndex)); marker.setName(cursor.getString(nameIndex));
} }
if (!cursor.isNull(descriptionIndex)) { if (!cursor.isNull(descriptionIndex)) {
waypoint.setDescription(cursor.getString(descriptionIndex)); marker.setDescription(cursor.getString(descriptionIndex));
} }
if (!cursor.isNull(categoryIndex)) { if (!cursor.isNull(categoryIndex)) {
waypoint.setCategory(cursor.getString(categoryIndex)); marker.setCategory(cursor.getString(categoryIndex));
} }
if (!cursor.isNull(iconIndex)) { if (!cursor.isNull(iconIndex)) {
waypoint.setIcon(cursor.getString(iconIndex)); marker.setIcon(cursor.getString(iconIndex));
} }
if (!cursor.isNull(trackIdIndex)) { if (!cursor.isNull(trackIdIndex)) {
waypoint.setTrackId(new Track.Id(cursor.getLong(trackIdIndex))); marker.setTrackId(new Track.Id(cursor.getLong(trackIdIndex)));
} }
if (!cursor.isNull(lengthIndex)) { if (!cursor.isNull(lengthIndex)) {
waypoint.setLength(cursor.getFloat(lengthIndex)); marker.setLength(cursor.getFloat(lengthIndex));
} }
if (!cursor.isNull(durationIndex)) { if (!cursor.isNull(durationIndex)) {
waypoint.setDuration(cursor.getLong(durationIndex)); marker.setDuration(cursor.getLong(durationIndex));
} }
if (!cursor.isNull(photoUrlIndex)) { if (!cursor.isNull(photoUrlIndex)) {
waypoint.setPhotoUrl(cursor.getString(photoUrlIndex)); marker.setPhotoUrl(cursor.getString(photoUrlIndex));
} }
return waypoint; return marker;
} }
public void deleteWaypoint(Context context, Waypoint.Id waypointId) { public void deleteMarker(Context context, Marker.Id markerId) {
final Waypoint waypoint = getWaypoint(waypointId); final Marker marker = getMarker(markerId);
deleteWaypointPhoto(context, waypoint); deleteMarkerPhoto(context, marker);
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId.getId())}); contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())});
} }
/** /**
* Gets the next waypoint number. * @return -1 if not able to get the next marker number.
*
* @param trackId the track id
* @return -1 if not able to get the next waypoint number.
*/ */
public int getNextWaypointNumber(Track.Id trackId) { public int getNextMarkerNumber(Track.Id trackId) {
if (!trackId.isValid()) { if (!trackId.isValid()) {
return -1; return -1;
} }
String[] projection = {WaypointsColumns._ID}; String[] projection = {MarkerColumns._ID};
String selection = WaypointsColumns.TRACKID + "=?"; String selection = MarkerColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId.getId())}; String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1)) { try (Cursor cursor = getMarkerCursor(projection, selection, selectionArgs, MarkerColumns._ID, -1)) {
if (cursor != null) { if (cursor != null) {
return cursor.getCount(); return cursor.getCount();
} }
@@ -439,99 +425,94 @@ public class ContentProviderUtils {
return -1; return -1;
} }
public Waypoint getWaypoint(Waypoint.Id waypointId) { public Marker getMarker(Marker.Id markerId) {
if (!waypointId.isValid()) { if (!markerId.isValid()) {
return null; return null;
} }
try (Cursor cursor = getWaypointCursor(null, WaypointsColumns._ID + "=?", try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?",
new String[]{Long.toString(waypointId.getId())}, WaypointsColumns._ID, 1)) { new String[]{Long.toString(markerId.getId())}, MarkerColumns._ID, 1)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor); return createMarker(cursor);
} }
} }
return null; return null;
} }
/** /**
* Gets a waypoint cursor. * The caller owns the returned cursor and is responsible for closing it.
* he caller owns the returned cursor and is responsible for closing it.
* *
* @param selection the selection. Can be null * @param selection the selection. Can be null
* @param selectionArgs the selection arguments. Can be null * @param selectionArgs the selection arguments. Can be null
* @param sortOrder the sort order. Can be null * @param sortOrder the sort order. Can be null
* @param maxWaypoints the maximum number of waypoints to return. -1 for no limit * @param maxCount the maximum number of markers to return. -1 for no limit
*/ */
public Cursor getWaypointCursor(String selection, String[] selectionArgs, String sortOrder, int maxWaypoints) { public Cursor getMarkerCursor(String selection, String[] selectionArgs, String sortOrder, int maxCount) {
return getWaypointCursor(null, selection, selectionArgs, sortOrder, maxWaypoints); return getMarkerCursor(null, selection, selectionArgs, sortOrder, maxCount);
} }
/** /**
* Gets a waypoint cursor for a track.
* The caller owns the returned cursor and is responsible for closing it. * The caller owns the returned cursor and is responsible for closing it.
* *
* @param trackId the track id * @param trackId the track id
* @param minWaypointId the minimum waypoint id. null to ignore * @param minMarkerId the minimum marker id. null to ignore
* @param maxWaypoints the maximum number of waypoints to return. -1 for no limit * @param maxCount the maximum number of markers to return. -1 for no limit
*/ */
public Cursor getWaypointCursor(Track.Id trackId, @Nullable Waypoint.Id minWaypointId, int maxWaypoints) { public Cursor getMarkerCursor(Track.Id trackId, @Nullable Marker.Id minMarkerId, int maxCount) {
if (!trackId.isValid()) { if (!trackId.isValid()) {
return null; return null;
} }
String selection; String selection;
String[] selectionArgs; String[] selectionArgs;
if (minWaypointId != null && minWaypointId.isValid()) { if (minMarkerId != null && minMarkerId.isValid()) {
selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + ">=?"; selection = MarkerColumns.TRACKID + "=? AND " + MarkerColumns._ID + ">=?";
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minWaypointId.getId())}; selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.getId())};
} else { } else {
selection = WaypointsColumns.TRACKID + "=?"; selection = MarkerColumns.TRACKID + "=?";
selectionArgs = new String[]{Long.toString(trackId.getId())}; selectionArgs = new String[]{Long.toString(trackId.getId())};
} }
return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints); return getMarkerCursor(null, selection, selectionArgs, MarkerColumns._ID, maxCount);
} }
@Deprecated //TODO Move to test package
@VisibleForTesting @VisibleForTesting
public List<Waypoint> getWaypoints(Track.Id trackId) { public List<Marker> getMarkers(Track.Id trackId) {
ArrayList<Waypoint> waypoints = new ArrayList<>(); ArrayList<Marker> markers = new ArrayList<>();
try (Cursor cursor = getWaypointCursor(trackId, null, -1)) { try (Cursor cursor = getMarkerCursor(trackId, null, -1)) {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
waypoints.add(createWaypoint(cursor)); markers.add(createMarker(cursor));
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
} }
return waypoints; return markers;
} }
public static Loader<Cursor> getWaypointsLoader(Context context, @Nullable Track.Id trackId) { public static Loader<Cursor> getMarkersLoader(Context context, @Nullable Track.Id trackId) {
final String[] PROJECTION = new String[]{WaypointsColumns._ID, final String[] PROJECTION = new String[]{MarkerColumns._ID,
WaypointsColumns.NAME, WaypointsColumns.DESCRIPTION, WaypointsColumns.CATEGORY, MarkerColumns.NAME, MarkerColumns.DESCRIPTION, MarkerColumns.CATEGORY,
WaypointsColumns.TIME, WaypointsColumns.PHOTOURL, MarkerColumns.TIME, MarkerColumns.PHOTOURL,
WaypointsColumns.LATITUDE, WaypointsColumns.LONGITUDE}; MarkerColumns.LATITUDE, MarkerColumns.LONGITUDE};
if (trackId != null) { if (trackId != null) {
return new CursorLoader(context, WaypointsColumns.CONTENT_URI, PROJECTION, return new CursorLoader(context, MarkerColumns.CONTENT_URI, PROJECTION,
WaypointsColumns.TRACKID + "=?", MarkerColumns.TRACKID + "=?",
new String[]{String.valueOf(trackId.getId())}, null); new String[]{String.valueOf(trackId.getId())}, null);
} else { } else {
return new CursorLoader(context, WaypointsColumns.CONTENT_URI, PROJECTION, null, null, null); return new CursorLoader(context, MarkerColumns.CONTENT_URI, PROJECTION, null, null, null);
} }
} }
/** @Deprecated //TODO Expose information via Cursor
* Gets the number of waypoints for a track. public int getMarkerCount(Track.Id trackId) {
*
* @param trackId the track id
*/
public int getWaypointCount(Track.Id trackId) {
if (!trackId.isValid()) { if (!trackId.isValid()) {
return 0; return 0;
} }
String[] projection = new String[]{"count(*) AS count"}; String[] projection = new String[]{"count(*) AS count"};
String selection = WaypointsColumns.TRACKID + "=?"; String selection = MarkerColumns.TRACKID + "=?";
String[] selectionArgs = new String[]{Long.toString(trackId.getId())}; String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
try (Cursor cursor = contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, WaypointsColumns._ID)) { try (Cursor cursor = contentResolver.query(MarkerColumns.CONTENT_URI, projection, selection, selectionArgs, MarkerColumns._ID)) {
if (cursor == null) { if (cursor == null) {
return 0; return 0;
} }
@@ -541,26 +522,17 @@ public class ContentProviderUtils {
} }
/** /**
* Inserts a waypoint. * @return the content provider URI of the inserted marker.
*
* @param waypoint the waypoint
* @return the content provider URI of the inserted waypoint.
*/ */
public Uri insertWaypoint(@NonNull Waypoint waypoint) { public Uri insertMarker(@NonNull Marker marker) {
waypoint.setId(null); marker.setId(null);
return contentResolver.insert(WaypointsColumns.CONTENT_URI, createContentValues(waypoint)); return contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker));
} }
/** private void deleteMarkerPhoto(Context context, Marker marker) {
* Delete waypoint's photo if any. if (marker != null && marker.hasPhoto()) {
* Uri uri = marker.getPhotoURI();
* @param context the context object. File file = FileUtils.getPhotoFileIfExists(context, marker.getTrackId(), uri);
* @param waypoint the waypoint object.
*/
private void deleteWaypointPhoto(Context context, Waypoint waypoint) {
if (waypoint != null && waypoint.hasPhoto()) {
Uri uri = waypoint.getPhotoURI();
File file = FileUtils.getPhotoFileIfExists(context, waypoint.getTrackId(), uri);
if (file.exists()) { if (file.exists()) {
File parent = file.getParentFile(); File parent = file.getParentFile();
file.delete(); file.delete();
@@ -572,71 +544,67 @@ public class ContentProviderUtils {
} }
/** /**
* Updates a waypoint. * @param updateMarker the marker with updated data.
* Returns true if successful. * @return true if successful.
*
* @param updatedWaypoint the waypoint with updated data.
*/ */
public boolean updateWaypoint(Context context, Waypoint updatedWaypoint) { public boolean updateMarker(Context context, Marker updateMarker) {
Waypoint savedWaypoint = getWaypoint(updatedWaypoint.getId()); Marker savedMarker = getMarker(updateMarker.getId());
if (!updatedWaypoint.hasPhoto()) { if (!updateMarker.hasPhoto()) {
deleteWaypointPhoto(context, savedWaypoint); deleteMarkerPhoto(context, savedMarker);
} }
int rows = contentResolver.update(WaypointsColumns.CONTENT_URI, createContentValues(updatedWaypoint), WaypointsColumns._ID + "=?", new String[]{Long.toString(updatedWaypoint.getId().getId())}); int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().getId())});
return rows == 1; return rows == 1;
} }
ContentValues createContentValues(@NonNull Waypoint waypoint) { ContentValues createContentValues(@NonNull Marker marker) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
if (waypoint.getId() != null && waypoint.getId().isValid()) { if (marker.getId() != null && marker.getId().isValid()) {
values.put(WaypointsColumns._ID, waypoint.getId().getId()); values.put(MarkerColumns._ID, marker.getId().getId());
} }
values.put(WaypointsColumns.NAME, waypoint.getName()); values.put(MarkerColumns.NAME, marker.getName());
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription()); values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory()); values.put(MarkerColumns.CATEGORY, marker.getCategory());
values.put(WaypointsColumns.ICON, waypoint.getIcon()); values.put(MarkerColumns.ICON, marker.getIcon());
if (waypoint.getTrackId() != null) { if (marker.getTrackId() != null) {
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId()); values.put(MarkerColumns.TRACKID, marker.getTrackId().getId());
} }
values.put(WaypointsColumns.LENGTH, waypoint.getLength()); values.put(MarkerColumns.LENGTH, marker.getLength());
values.put(WaypointsColumns.DURATION, waypoint.getDuration()); values.put(MarkerColumns.DURATION, marker.getDuration());
Location location = waypoint.getLocation(); Location location = marker.getLocation();
values.put(WaypointsColumns.LONGITUDE, (int) (location.getLongitude() * 1E6)); values.put(MarkerColumns.LONGITUDE, (int) (location.getLongitude() * 1E6));
values.put(WaypointsColumns.LATITUDE, (int) (location.getLatitude() * 1E6)); values.put(MarkerColumns.LATITUDE, (int) (location.getLatitude() * 1E6));
values.put(WaypointsColumns.TIME, location.getTime()); values.put(MarkerColumns.TIME, location.getTime());
if (location.hasAltitude()) { if (location.hasAltitude()) {
values.put(WaypointsColumns.ALTITUDE, location.getAltitude()); values.put(MarkerColumns.ALTITUDE, location.getAltitude());
} }
if (location.hasAccuracy()) { if (location.hasAccuracy()) {
values.put(WaypointsColumns.ACCURACY, location.getAccuracy()); values.put(MarkerColumns.ACCURACY, location.getAccuracy());
} }
if (location.hasBearing()) { if (location.hasBearing()) {
values.put(WaypointsColumns.BEARING, location.getBearing()); values.put(MarkerColumns.BEARING, location.getBearing());
} }
values.put(WaypointsColumns.PHOTOURL, waypoint.getPhotoUrl()); values.put(MarkerColumns.PHOTOURL, marker.getPhotoUrl());
return values; return values;
} }
/** /**
* Gets a waypoint cursor.
*
* @param projection the projection * @param projection the projection
* @param selection the selection * @param selection the selection
* @param selectionArgs the selection args * @param selectionArgs the selection args
* @param sortOrder the sort order * @param sortOrder the sort order
* @param maxWaypoints the maximum number of waypoints * @param maxCount the maximum number of markers
*/ */
private Cursor getWaypointCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder, int maxWaypoints) { private Cursor getMarkerCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder, int maxCount) {
if (sortOrder == null) { if (sortOrder == null) {
sortOrder = WaypointsColumns._ID; sortOrder = MarkerColumns._ID;
} }
if (maxWaypoints >= 0) { if (maxCount >= 0) {
sortOrder += " LIMIT " + maxWaypoints; sortOrder += " LIMIT " + maxCount;
} }
return contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder); return contentResolver.query(MarkerColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
} }
/** /**
@@ -680,6 +648,10 @@ public class ContentProviderUtils {
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex)); trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
} }
if (!cursor.isNull(indexes.elevationGainIndex)) {
trackPoint.setElevationGain(cursor.getFloat(indexes.elevationGainIndex));
}
return trackPoint; return trackPoint;
} }
@@ -836,7 +808,7 @@ public class ContentProviderUtils {
/** /**
* Creates the {@link ContentValues} for a {@link TrackPoint}. * Creates the {@link ContentValues} for a {@link TrackPoint}.
* *
* @param trackPoint the trackPoint * @param trackPoint the trackPointstats_pace_km#87
* @param trackId the track id * @param trackId the track id
*/ */
private ContentValues createContentValues(TrackPoint trackPoint, Track.Id trackId) { private ContentValues createContentValues(TrackPoint trackPoint, Track.Id trackId) {
@@ -868,6 +840,11 @@ public class ContentProviderUtils {
if (trackPoint.hasPower()) { if (trackPoint.hasPower()) {
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower()); values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower());
} }
if (trackPoint.hasElevationGain()) {
values.put(TrackPointsColumns.ELEVATION_GAIN, trackPoint.getElevationGain());
}
return values; return values;
} }
@@ -33,12 +33,12 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
/** /**
* A {@link ContentProvider} that handles access to track points, tracks, and waypoints tables. * A {@link ContentProvider} that handles access to track points, tracks, and markers tables.
* *
* @author Leif Hendrik Wilden * @author Leif Hendrik Wilden
*/ */
@@ -61,9 +61,9 @@ public class CustomContentProvider extends ContentProvider {
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal()); uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal()); uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath(), UrlType.WAYPOINTS.ordinal()); uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath(), UrlType.MARKERS.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath() + "/#", UrlType.WAYPOINTS_BY_ID.ordinal()); uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI.getPath() + "/#", UrlType.MARKERS_BY_ID.ordinal());
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.WAYPOINTS_BY_TRACKID.ordinal()); uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, MarkerColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.MARKERS_BY_TRACKID.ordinal());
} }
@Override @Override
@@ -100,8 +100,8 @@ public class CustomContentProvider extends ContentProvider {
table = TracksColumns.TABLE_NAME; table = TracksColumns.TABLE_NAME;
shouldVacuum = true; shouldVacuum = true;
break; break;
case WAYPOINTS: case MARKERS:
table = WaypointsColumns.TABLE_NAME; table = MarkerColumns.TABLE_NAME;
break; break;
default: default:
throw new IllegalArgumentException("Unknown URL " + url); throw new IllegalArgumentException("Unknown URL " + url);
@@ -138,11 +138,11 @@ public class CustomContentProvider extends ContentProvider {
return TracksColumns.CONTENT_TYPE; return TracksColumns.CONTENT_TYPE;
case TRACKS_BY_ID: case TRACKS_BY_ID:
return TracksColumns.CONTENT_ITEMTYPE; return TracksColumns.CONTENT_ITEMTYPE;
case WAYPOINTS: case MARKERS:
return WaypointsColumns.CONTENT_TYPE; return MarkerColumns.CONTENT_TYPE;
case WAYPOINTS_BY_ID: case MARKERS_BY_ID:
case WAYPOINTS_BY_TRACKID: case MARKERS_BY_TRACKID:
return WaypointsColumns.CONTENT_ITEMTYPE; return MarkerColumns.CONTENT_ITEMTYPE;
default: default:
throw new IllegalArgumentException("Unknown URL " + url); throw new IllegalArgumentException("Unknown URL " + url);
} }
@@ -213,17 +213,17 @@ public class CustomContentProvider extends ContentProvider {
queryBuilder.setTables(TracksColumns.TABLE_NAME); queryBuilder.setTables(TracksColumns.TABLE_NAME);
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")"); queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
break; break;
case WAYPOINTS: case MARKERS:
queryBuilder.setTables(WaypointsColumns.TABLE_NAME); queryBuilder.setTables(MarkerColumns.TABLE_NAME);
sortOrder = sort != null ? sort : WaypointsColumns.DEFAULT_SORT_ORDER; sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
break; break;
case WAYPOINTS_BY_ID: case MARKERS_BY_ID:
queryBuilder.setTables(WaypointsColumns.TABLE_NAME); queryBuilder.setTables(MarkerColumns.TABLE_NAME);
queryBuilder.appendWhere(WaypointsColumns._ID + "=" + ContentUris.parseId(url)); queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
break; break;
case WAYPOINTS_BY_TRACKID: case MARKERS_BY_TRACKID:
queryBuilder.setTables(WaypointsColumns.TABLE_NAME); queryBuilder.setTables(MarkerColumns.TABLE_NAME);
queryBuilder.appendWhere(WaypointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")"); queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
break; break;
default: default:
throw new IllegalArgumentException("Unknown url " + url); throw new IllegalArgumentException("Unknown url " + url);
@@ -261,13 +261,13 @@ public class CustomContentProvider extends ContentProvider {
whereClause += " AND (" + where + ")"; whereClause += " AND (" + where + ")";
} }
break; break;
case WAYPOINTS: case MARKERS:
table = WaypointsColumns.TABLE_NAME; table = MarkerColumns.TABLE_NAME;
whereClause = where; whereClause = where;
break; break;
case WAYPOINTS_BY_ID: case MARKERS_BY_ID:
table = WaypointsColumns.TABLE_NAME; table = MarkerColumns.TABLE_NAME;
whereClause = WaypointsColumns._ID + "=" + ContentUris.parseId(url); whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
if (!TextUtils.isEmpty(where)) { if (!TextUtils.isEmpty(where)) {
whereClause += " AND (" + where + ")"; whereClause += " AND (" + where + ")";
} }
@@ -316,8 +316,8 @@ public class CustomContentProvider extends ContentProvider {
return insertTrackPoint(url, contentValues); return insertTrackPoint(url, contentValues);
case TRACKS: case TRACKS:
return insertTrack(url, contentValues); return insertTrack(url, contentValues);
case WAYPOINTS: case MARKERS:
return insertWaypoint(url, contentValues); return insertMarker(url, contentValues);
default: default:
throw new IllegalArgumentException("Unknown url " + url); throw new IllegalArgumentException("Unknown url " + url);
} }
@@ -361,25 +361,14 @@ public class CustomContentProvider extends ContentProvider {
throw new SQLException("Failed to insert a track " + url); throw new SQLException("Failed to insert a track " + url);
} }
/** private Uri insertMarker(Uri url, ContentValues contentValues) {
* Inserts a waypoint. long rowId = db.insert(MarkerColumns.TABLE_NAME, MarkerColumns._ID, contentValues);
*
* @param url the content url
* @param contentValues the content values
*/
private Uri insertWaypoint(Uri url, ContentValues contentValues) {
long rowId = db.insert(WaypointsColumns.TABLE_NAME, WaypointsColumns._ID, contentValues);
if (rowId >= 0) { if (rowId >= 0) {
return ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), rowId).build(); return ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), rowId).build();
} }
throw new SQLException("Failed to insert a waypoint " + url); throw new SQLException("Failed to insert a marker " + url);
} }
/**
* Types of url.
*
* @author Jimmy Shih
*/
@VisibleForTesting @VisibleForTesting
enum UrlType { enum UrlType {
TRACKPOINTS, TRACKPOINTS,
@@ -387,8 +376,8 @@ public class CustomContentProvider extends ContentProvider {
TRACKPOINTS_BY_TRACKID, TRACKPOINTS_BY_TRACKID,
TRACKS, TRACKS,
TRACKS_BY_ID, TRACKS_BY_ID,
WAYPOINTS, MARKERS,
WAYPOINTS_BY_ID, MARKERS_BY_ID,
WAYPOINTS_BY_TRACKID MARKERS_BY_TRACKID
} }
} }
@@ -11,10 +11,10 @@ import androidx.annotation.VisibleForTesting;
import java.util.UUID; import java.util.UUID;
import de.dennisguse.opentracks.content.data.MarkerColumns;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPointsColumns; import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.util.UUIDUtils; import de.dennisguse.opentracks.util.UUIDUtils;
/** /**
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName(); private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
private static final int DATABASE_VERSION = 26; private static final int DATABASE_VERSION = 27;
@VisibleForTesting @VisibleForTesting
public static final String DATABASE_NAME = "database.db"; public static final String DATABASE_NAME = "database.db";
@@ -52,8 +52,8 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.execSQL(TracksColumns.CREATE_TABLE); db.execSQL(TracksColumns.CREATE_TABLE);
db.execSQL(TracksColumns.CREATE_TABLE_INDEX); db.execSQL(TracksColumns.CREATE_TABLE_INDEX);
db.execSQL(WaypointsColumns.CREATE_TABLE); db.execSQL(MarkerColumns.CREATE_TABLE);
db.execSQL(WaypointsColumns.CREATE_TABLE_INDEX); db.execSQL(MarkerColumns.CREATE_TABLE_INDEX);
} }
@Override @Override
@@ -70,6 +70,10 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 26: case 26:
upgradeFrom25to26(db); upgradeFrom25to26(db);
break; break;
case 27:
upgradeFrom26to27(db);
break;
default: default:
throw new RuntimeException("Not implemented: upgrade to " + toVersion); throw new RuntimeException("Not implemented: upgrade to " + toVersion);
@@ -91,6 +95,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 25: case 25:
downgradeFrom26to25(db); downgradeFrom26to25(db);
break; break;
case 26:
downgradeFrom27to26(db);
break;
default: default:
throw new RuntimeException("Not implemented: downgrade to " + toVersion); throw new RuntimeException("Not implemented: downgrade to " + toVersion);
@@ -166,7 +173,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex)); Track.Id trackId = new Track.Id(cursor.getLong(trackIdIndex));
ContentValues cv = new ContentValues(); ContentValues cv = new ContentValues();
cv.put("uuid", UUIDUtils.toBytes(UUID.randomUUID())); cv.put("uuid", UUIDUtils.toBytes(UUID.randomUUID()));
db.update("tracks", cv, "_id = ?", new String[]{String.valueOf(trackId)}); db.update("tracks", cv, "_id = ?", new String[]{String.valueOf(trackId.getId())});
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
} }
@@ -190,4 +197,27 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); db.endTransaction();
} }
private void upgradeFrom26to27(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("ALTER TABLE trackpoints ADD COLUMN elevation_gain FLOAT");
db.setTransactionSuccessful();
db.endTransaction();
}
private void downgradeFrom27to26(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
db.execSQL("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)");
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power FROM trackpoints_old");
db.execSQL("DROP TABLE trackpoints_old");
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
db.setTransactionSuccessful();
db.endTransaction();
}
} }
@@ -37,9 +37,9 @@ import de.dennisguse.opentracks.chart.ChartPoint;
import de.dennisguse.opentracks.chart.ChartView; import de.dennisguse.opentracks.chart.ChartView;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener; import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater; import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.LocationUtils;
@@ -242,21 +242,21 @@ public class ChartFragment extends Fragment implements TrackDataListener {
} }
@Override @Override
public void clearWaypoints() { public void clearMarkers() {
if (isResumed()) { if (isResumed()) {
chartView.clearWaypoints(); chartView.clearMarker();
} }
} }
@Override @Override
public void onNewWaypoint(Waypoint waypoint) { public void onNewMarker(Marker marker) {
if (isResumed() && waypoint != null && LocationUtils.isValidLocation(waypoint.getLocation())) { if (isResumed() && marker != null && LocationUtils.isValidLocation(marker.getLocation())) {
chartView.addWaypoint(waypoint); chartView.addMarker(marker);
} }
} }
@Override @Override
public void onNewWaypointsDone() { public void onNewMarkersDone() {
if (isResumed()) { if (isResumed()) {
runOnUiThread(updateChart); runOnUiThread(updateChart);
} }
@@ -22,6 +22,8 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import java.util.List; import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
@@ -76,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
} }
if (position == selected) { if (position == selected) {
imageView.setBackgroundResource(R.drawable.list_selector_background_transition_holo_dark); imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.colorAccent));
} else { } else {
imageView.setBackgroundColor(Color.TRANSPARENT); imageView.setBackgroundColor(Color.TRANSPARENT);
} }
@@ -26,7 +26,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.DialogUtils; import de.dennisguse.opentracks.util.DialogUtils;
@@ -42,9 +42,9 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
private DeleteMarkerCaller caller; private DeleteMarkerCaller caller;
public static void showDialog(FragmentManager fragmentManager, Waypoint.Id... waypointIds) { public static void showDialog(FragmentManager fragmentManager, Marker.Id... markerIds) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putParcelableArray(KEY_MARKER_IDS, waypointIds); bundle.putParcelableArray(KEY_MARKER_IDS, markerIds);
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment(); DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
deleteMarkerDialogFragment.setArguments(bundle); deleteMarkerDialogFragment.setArguments(bundle);
@@ -64,23 +64,23 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
@Override @Override
@NonNull @NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final Waypoint.Id[] waypointIds = (Waypoint.Id[]) getArguments().getParcelableArray(KEY_MARKER_IDS); final Marker.Id[] markerIds = (Marker.Id[]) getArguments().getParcelableArray(KEY_MARKER_IDS);
final FragmentActivity fragmentActivity = getActivity(); final FragmentActivity fragmentActivity = getActivity();
int titleId; int titleId;
int messageId; int messageId;
if (waypointIds == null) { if (markerIds == null) {
titleId = R.string.generic_delete_all_confirm_title; titleId = R.string.generic_delete_all_confirm_title;
messageId = R.string.marker_delete_all_confirm_message; messageId = R.string.marker_delete_all_confirm_message;
} else { } else {
titleId = waypointIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title; titleId = markerIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.marker_delete_one_confirm_title;
messageId = waypointIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message; messageId = markerIds.length > 1 ? R.string.marker_delete_multiple_confirm_message : R.string.marker_delete_one_confirm_message;
} }
return DialogUtils.createConfirmationDialog( return DialogUtils.createConfirmationDialog(
fragmentActivity, titleId, getString(messageId), (dialog, which) -> new Thread(() -> { fragmentActivity, titleId, getString(messageId), (dialog, which) -> new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
for (Waypoint.Id waypointId : waypointIds) { for (Marker.Id markerId : markerIds) {
contentProviderUtils.deleteWaypoint(getContext(), waypointId); contentProviderUtils.deleteMarker(getContext(), markerId);
} }
caller.onDeleteMarkerDone(); caller.onDeleteMarkerDone();
}).start()); }).start());
@@ -16,12 +16,12 @@
package de.dennisguse.opentracks.fragments; package de.dennisguse.opentracks.fragments;
import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@@ -38,6 +38,7 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns; import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.ExportFile;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter; import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.util.DialogUtils; import de.dennisguse.opentracks.util.DialogUtils;
@@ -208,33 +209,32 @@ public class ExportProgressDialogFragment extends DialogFragment {
private Boolean exportTrack(Context context, Track track) { private Boolean exportTrack(Context context, Track track) {
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context); TrackExporter trackExporter = trackFileFormat.newTrackExporter(context);
//TODO Move to helper function DocumentFile exportDocumentFile = ExportFile.getExportDocumentFile(
String fileName = track.getId() + "." + trackFileFormat.getExtension(); track.getId(),
trackFileFormat.getExtension(),
directory,
trackFileFormat.getMimeType()
);
// Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists. try (OutputStream outputStream = context.getContentResolver().openOutputStream(exportDocumentFile.getUri())) {
DocumentFile file = directory.findFile(fileName);
if (file == null) {
file = directory.createFile(trackFileFormat.getMimeType(), fileName);
}
try (OutputStream outputStream = context.getContentResolver().openOutputStream(file.getUri())) {
if (trackExporter.writeTrack(track, outputStream)) { if (trackExporter.writeTrack(track, outputStream)) {
return true; return true;
} else { } else {
if (!file.delete()) { if (!exportDocumentFile.delete()) {
Log.e(TAG, "Unable to delete file"); Log.e(TAG, "Unable to delete exportDocumentFile");
} }
Log.e(TAG, "Unable to export track"); Log.e(TAG, "Unable to export track");
return false; return false;
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Log.e(TAG, "Unable to open file " + file.getName(), e); Log.e(TAG, "Unable to open exportDocumentFile " + exportDocumentFile.getName(), e);
return false; return false;
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "Unable to close file output stream", e); Log.e(TAG, "Unable to close exportDocumentFile output stream", e);
return false; return false;
} }
} }
} }
public interface DismissCallback { public interface DismissCallback {
@@ -16,12 +16,12 @@
package de.dennisguse.opentracks.fragments; package de.dennisguse.opentracks.fragments;
import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
@@ -0,0 +1,225 @@
package de.dennisguse.opentracks.fragments;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.UnitConversions;
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
/**
* A fragment to display the intervals from recorded track.
*/
public class IntervalsFragment extends Fragment {
private static final String TAG = IntervalsFragment.class.getSimpleName();
private static final String TRACK_ID_KEY = "trackId";
private IntervalStatisticsModel viewModel;
private ListView intervalListView;
protected IntervalStatisticsAdapter.StackMode stackModeListView;
private IntervalStatisticsModel.IntervalOption selectedInterval;
private String intervalUnit;
private IntervalStatisticsAdapter adapter;
protected Spinner spinnerIntervals;
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
private Track.Id trackId;
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> {
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
intervalUnit = PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile);
if (adapter != null) {
adapter.notifyDataSetChanged();
spinnerAdapter.notifyDataSetChanged();
intervalChanged();
}
}
};
public static Fragment newInstance(Track.Id trackId) {
Bundle bundle = new Bundle();
bundle.putParcelable(TRACK_ID_KEY, trackId);
Fragment fragment = new IntervalsFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.interval_list_view, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
intervalUnit = PreferencesUtils.isMetricUnits(getContext()) ? getContext().getString(R.string.unit_kilometer) : getContext().getString(R.string.unit_mile);
if (savedInstanceState != null) {
trackId = savedInstanceState.getParcelable(TRACK_ID_KEY);
} else {
trackId = getArguments().getParcelable(TRACK_ID_KEY);
}
intervalListView = view.findViewById(R.id.interval_list);
intervalListView.setEmptyView(view.findViewById(R.id.interval_list_empty_view));
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP;
viewModel = new ViewModelProvider(this).get(IntervalStatisticsModel.class);
spinnerIntervals = view.findViewById(R.id.spinner_intervals);
spinnerAdapter = new ArrayAdapter<IntervalStatisticsModel.IntervalOption>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
TextView v = (TextView) super.getView(position, convertView, parent);
v.setText(v.getText() + " " + intervalUnit);
return v;
}
@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
TextView v = (TextView) super.getDropDownView(position, convertView, parent);
v.setText(v.getText() + " " + intervalUnit);
return v;
}
};
spinnerIntervals.setAdapter(spinnerAdapter);
spinnerIntervals.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
intervalChanged();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
intervalChanged();
}
@Override
public void onDestroy() {
super.onDestroy();
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
intervalListView = null;
adapter = null;
spinnerIntervals = null;
viewModel = null;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(TRACK_ID_KEY, trackId);
}
/**
* Update intervals through {@link IntervalStatisticsModel} view model.
*/
public void intervalChanged() {
if (viewModel == null || intervalListView == null) {
return;
}
LiveData<IntervalStatistics> liveData = viewModel.getIntervalStats(trackId, selectedInterval);
liveData.observe(getActivity(), intervalStatistics -> {
if (intervalStatistics != null) {
adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList(), stackModeListView);
intervalListView.setAdapter(adapter);
}
});
}
public void setTrackId(Track.Id trackId) {
this.trackId = trackId;
}
public static class IntervalsRecordingFragment extends IntervalsFragment implements TrackRecordingActivity.OnTrackIdListener {
// Refreshing intervals stats it's not so demanding so 5 seconds is enough to balance performance and user experience.
private static final long UI_UPDATE_INTERVAL = 5 * UnitConversions.ONE_SECOND_MS;
private Handler intervalHandler;
private final Runnable intervalRunner = new Runnable() {
@Override
public void run() {
if (isResumed()) {
updateIntervals();
intervalHandler.postDelayed(intervalRunner, UI_UPDATE_INTERVAL);
}
}
};
public static Fragment newInstance(Track.Id trackId) {
Bundle bundle = new Bundle();
bundle.putParcelable(TRACK_ID_KEY, trackId);
Fragment fragment = new IntervalsRecordingFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
((TrackRecordingActivity) getActivity()).setTrackIdListener(this);
intervalHandler = new Handler();
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
}
@Override
public void onResume() {
super.onResume();
intervalHandler.post(intervalRunner);
}
@Override
public void onPause() {
super.onPause();
intervalHandler.removeCallbacks(intervalRunner);
}
private void updateIntervals() {
intervalChanged();
}
@Override
public void onTrackId(Track.Id trackId) {
setTrackId(trackId);
}
}
}
@@ -40,7 +40,7 @@ import androidx.fragment.app.FragmentActivity;
import de.dennisguse.opentracks.MarkerEditActivity; import de.dennisguse.opentracks.MarkerEditActivity;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Waypoint; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils; import de.dennisguse.opentracks.util.ListItemUtils;
@@ -66,8 +66,8 @@ public class MarkerDetailFragment extends Fragment {
private Handler handler; private Handler handler;
private ImageView photoView; private ImageView photoView;
private ImageView textGradient; private ImageView textGradient;
private LinearLayout waypointInfo; private LinearLayout markerInfo;
private Waypoint waypoint; private Marker marker;
private final Runnable hideText = new Runnable() { private final Runnable hideText = new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -85,17 +85,17 @@ public class MarkerDetailFragment extends Fragment {
@Override @Override
public void onAnimationEnd(Animation anim) { public void onAnimationEnd(Animation anim) {
textGradient.setVisibility(View.GONE); textGradient.setVisibility(View.GONE);
waypointInfo.setVisibility(View.GONE); markerInfo.setVisibility(View.GONE);
} }
}); });
textGradient.startAnimation(animation); textGradient.startAnimation(animation);
waypointInfo.startAnimation(animation); markerInfo.startAnimation(animation);
} }
}; };
public static MarkerDetailFragment newInstance(Waypoint.Id waypointId) { public static MarkerDetailFragment newInstance(Marker.Id markerId) {
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putParcelable(KEY_MARKER_ID, waypointId); bundle.putParcelable(KEY_MARKER_ID, markerId);
MarkerDetailFragment fragment = new MarkerDetailFragment(); MarkerDetailFragment fragment = new MarkerDetailFragment();
fragment.setArguments(bundle); fragment.setArguments(bundle);
@@ -106,8 +106,8 @@ public class MarkerDetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Waypoint.Id waypointId = getArguments().getParcelable(KEY_MARKER_ID); Marker.Id markerId = getArguments().getParcelable(KEY_MARKER_ID);
if (waypointId == null) { if (markerId == null) {
Log.d(TAG, "invalid marker id"); Log.d(TAG, "invalid marker id");
getParentFragmentManager().popBackStack(); getParentFragmentManager().popBackStack();
return; return;
@@ -121,15 +121,15 @@ public class MarkerDetailFragment extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.marker_detail_fragment, container, false); View view = inflater.inflate(R.layout.marker_detail_fragment, container, false);
photoView = view.findViewById(R.id.marker_detail_waypoint_photo); photoView = view.findViewById(R.id.marker_detail_marker_photo);
textGradient = view.findViewById(R.id.marker_detail_waypoint_text_gradient); textGradient = view.findViewById(R.id.marker_detail_marker_text_gradient);
waypointInfo = view.findViewById(R.id.marker_detail_waypoint_info); markerInfo = view.findViewById(R.id.marker_detail_marker_info);
photoView.setOnClickListener(v -> { photoView.setOnClickListener(v -> {
handler.removeCallbacks(hideText); handler.removeCallbacks(hideText);
int visibility = waypointInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE; int visibility = markerInfo.getVisibility() == View.GONE ? View.VISIBLE : View.GONE;
textGradient.setVisibility(visibility); textGradient.setVisibility(visibility);
waypointInfo.setVisibility(visibility); markerInfo.setVisibility(visibility);
if (visibility == View.VISIBLE) { if (visibility == View.VISIBLE) {
handler.postDelayed(hideText, HIDE_TEXT_DELAY); handler.postDelayed(hideText, HIDE_TEXT_DELAY);
} }
@@ -141,8 +141,8 @@ public class MarkerDetailFragment extends Fragment {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
// Need to update the waypoint in case returning after an edit // Need to update the marker in case returning after an edit
updateWaypoint(true); updateMarker(true);
updateUi(); updateUi();
updateMenuItems(); updateMenuItems();
} }
@@ -171,64 +171,59 @@ public class MarkerDetailFragment extends Fragment {
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.marker_detail, menu); inflater.inflate(R.menu.marker_detail, menu);
shareMarkerImageMenuItem = menu.findItem(R.id.marker_detail_share); shareMarkerImageMenuItem = menu.findItem(R.id.marker_detail_share);
updateWaypoint(false); updateMarker(false);
updateMenuItems(); updateMenuItems();
} }
private void updateMenuItems() { private void updateMenuItems() {
if (shareMarkerImageMenuItem != null) if (shareMarkerImageMenuItem != null)
shareMarkerImageMenuItem.setVisible(waypoint.hasPhoto()); shareMarkerImageMenuItem.setVisible(marker.hasPhoto());
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
Waypoint.Id waypointId = getArguments().getParcelable(KEY_MARKER_ID); //TODO Should only happen in onCreate? Marker.Id markerId = getArguments().getParcelable(KEY_MARKER_ID); //TODO Should only happen in onCreate?
FragmentActivity fragmentActivity = getActivity(); FragmentActivity fragmentActivity = getActivity();
Intent intent; Intent intent;
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.marker_detail_show_on_map: case R.id.marker_detail_show_on_map:
IntentUtils.showCoordinateOnMap(getContext(), waypoint); IntentUtils.showCoordinateOnMap(getContext(), marker);
return true; return true;
case R.id.marker_detail_edit: case R.id.marker_detail_edit:
intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class) intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointId); .putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerId);
startActivity(intent); startActivity(intent);
return true; return true;
case R.id.marker_detail_share: case R.id.marker_detail_share:
if (waypoint.hasPhoto()) { if (marker.hasPhoto()) {
intent = IntentUtils.newShareImageIntent(getContext(), waypoint.getPhotoURI()); intent = IntentUtils.newShareImageIntent(getContext(), marker.getPhotoURI());
intent = Intent.createChooser(intent, null); intent = Intent.createChooser(intent, null);
startActivity(intent); startActivity(intent);
} }
return true; return true;
case R.id.marker_detail_delete: case R.id.marker_detail_delete:
DeleteMarkerDialogFragment.showDialog(getChildFragmentManager(), waypointId); DeleteMarkerDialogFragment.showDialog(getChildFragmentManager(), markerId);
return true; return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
} }
/** private void updateMarker(boolean refresh) {
* Updates the waypoint. if (refresh || marker == null) {
* marker = contentProviderUtils.getMarker(getArguments().getParcelable(KEY_MARKER_ID)); //TODO Should only happen in onCreate?
* @param refresh true to always update if (marker == null) {
*/ Log.d(TAG, "marker is null");
private void updateWaypoint(boolean refresh) {
if (refresh || waypoint == null) {
waypoint = contentProviderUtils.getWaypoint(getArguments().getParcelable(KEY_MARKER_ID)); //TODO Should only happen in onCreate?
if (waypoint == null) {
Log.d(TAG, "waypoint is null");
getParentFragmentManager().popBackStack(); getParentFragmentManager().popBackStack();
} }
} }
} }
private void updateUi() { private void updateUi() {
boolean hasPhoto = waypoint.hasPhoto(); boolean hasPhoto = marker.hasPhoto();
if (hasPhoto) { if (hasPhoto) {
handler.removeCallbacks(hideText); handler.removeCallbacks(hideText);
photoView.setImageURI(waypoint.getPhotoURI()); photoView.setImageURI(marker.getPhotoURI());
handler.postDelayed(hideText, HIDE_TEXT_DELAY); handler.postDelayed(hideText, HIDE_TEXT_DELAY);
} else { } else {
photoView.setImageResource(MarkerUtils.ICON_ID); photoView.setImageResource(MarkerUtils.ICON_ID);
@@ -236,23 +231,23 @@ public class MarkerDetailFragment extends Fragment {
setName(hasPhoto); setName(hasPhoto);
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category); TextView category = getView().findViewById(R.id.marker_detail_marker_category);
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto); ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(marker.getCategory()), hasPhoto);
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description); TextView description = getView().findViewById(R.id.marker_detail_marker_description);
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto); ListItemUtils.setTextView(getActivity(), description, marker.getDescription(), hasPhoto);
setLocation(hasPhoto); setLocation(hasPhoto);
} }
private void setName(boolean addShadow) { private void setName(boolean addShadow) {
TextView textView = getView().findViewById(R.id.marker_detail_waypoint_name); TextView textView = getView().findViewById(R.id.marker_detail_marker_name);
ListItemUtils.setTextView(getActivity(), textView, waypoint.getName(), addShadow); ListItemUtils.setTextView(getActivity(), textView, marker.getName(), addShadow);
} }
private void setLocation(boolean addShadow) { private void setLocation(boolean addShadow) {
TextView textView = getView().findViewById(R.id.marker_detail_waypoint_location); TextView textView = getView().findViewById(R.id.marker_detail_marker_location);
Location location = waypoint.getLocation(); Location location = marker.getLocation();
String value; String value;
if (location == null) { if (location == null) {
value = null; value = null;
@@ -19,25 +19,21 @@ package de.dennisguse.opentracks.fragments;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.util.Pair; import android.util.Pair;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Spinner; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackRecordedActivity;
import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
@@ -49,10 +45,14 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
* @author Sandor Dornbush * @author Sandor Dornbush
* @author Rodrigo Damazio * @author Rodrigo Damazio
*/ */
public class StatisticsRecordedFragment extends Fragment implements TrackDataListener { public class StatisticsRecordedFragment extends Fragment {
private TrackStatistics trackStatistics = null; private static final String TRACK_ID_KEY = "trackId";
private TrackStatistics trackStatistics;
private String category = ""; private String category = "";
private Track.Id trackId;
private ContentProviderUtils contentProviderUtils;
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> { private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> {
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) { if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
@@ -71,12 +71,11 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
return inflater.inflate(R.layout.statistics_recorded, container, false); return inflater.inflate(R.layout.statistics_recorded, container, false);
} }
/* Views */
private TextView totalTimeValueView; private TextView totalTimeValueView;
private TextView distanceValue; private TextView distanceValue;
private TextView distanceUnit; private TextView distanceUnit;
private View activityLabel; private View activityLabel;
private Spinner activitySpinner; private ImageView activitySpinner;
private TextView movingTimeValue; private TextView movingTimeValue;
private TextView speedAvgLabel; private TextView speedAvgLabel;
private TextView speedAvgValue; private TextView speedAvgValue;
@@ -88,6 +87,27 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
private TextView speedMovingValue; private TextView speedMovingValue;
private TextView speedMovingUnit; private TextView speedMovingUnit;
private Group elevationGroup;
private TextView elevationTotalGainValue;
private TextView elevationTotalGainUnit;
public static StatisticsRecordedFragment newInstance(Track.Id trackId) {
Bundle bundle = new Bundle();
bundle.putParcelable(TRACK_ID_KEY, trackId);
StatisticsRecordedFragment fragment = new StatisticsRecordedFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
trackId = getArguments().getParcelable(TRACK_ID_KEY);
contentProviderUtils = new ContentProviderUtils(getContext());
}
@Override @Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
@@ -113,26 +133,10 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
speedMovingLabel = view.findViewById(R.id.stats_moving_speed_label); speedMovingLabel = view.findViewById(R.id.stats_moving_speed_label);
speedMovingValue = view.findViewById(R.id.stats_moving_speed_value); speedMovingValue = view.findViewById(R.id.stats_moving_speed_value);
speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit); speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit);
}
@Override elevationGroup = view.findViewById(R.id.stats_elevation_group);
public void onActivityCreated(Bundle savedInstanceState) { elevationTotalGainValue = view.findViewById(R.id.stats_elevation_gain_value);
super.onActivityCreated(savedInstanceState); elevationTotalGainUnit = view.findViewById(R.id.stats_elevation_gain_unit);
Spinner activityTypeIcon = getView().findViewById(R.id.stats_activity_type_icon);
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(getActivity(), ""));
activityTypeIcon.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP) {
((TrackRecordedActivity) getActivity()).chooseActivityType(category);
}
return true;
});
activityTypeIcon.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
((TrackRecordedActivity) getActivity()).chooseActivityType(category);
}
return true;
});
} }
@Override @Override
@@ -140,23 +144,15 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
super.onResume(); super.onResume();
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener); PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
TrackDataHub trackDataHub = ((TrackRecordedActivity) getActivity()).getTrackDataHub(); loadStatistics();
trackDataHub.registerTrackDataListener(this, true, false, true, true);
}
@Override
public void onPause() {
super.onPause();
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
TrackDataHub trackDataHub = ((TrackRecordedActivity) getActivity()).getTrackDataHub();
trackDataHub.unregisterTrackDataListener(this);
} }
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
totalTimeValueView = null; totalTimeValueView = null;
distanceValue = null; distanceValue = null;
@@ -178,13 +174,17 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
speedMovingLabel = null; speedMovingLabel = null;
speedMovingValue = null; speedMovingValue = null;
speedMovingUnit = null; speedMovingUnit = null;
elevationGroup = null;
elevationTotalGainValue = null;
elevationTotalGainUnit = null;
} }
@Override public void loadStatistics() {
public void onTrackUpdated(final Track track) {
if (isResumed()) { if (isResumed()) {
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
if (isResumed()) { if (isResumed()) {
Track track = contentProviderUtils.getTrack(trackId);
trackStatistics = track != null ? track.getTrackStatistics() : null; trackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : ""; category = track != null ? track.getCategory() : "";
updateUI(); updateUI();
@@ -193,44 +193,7 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
} }
} }
@Override
public void clearTrackPoints() {
// We don't care.
}
@Override
public void onSampledInTrackPoint(TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onSampledOutTrackPoint(TrackPoint trackPoint) {
// We don't care.
}
@Override
public void onNewTrackPointsDone() {
// We don't care.
}
@Override
public void clearWaypoints() {
// We don't care.
}
@Override
public void onNewWaypoint(Waypoint wpt) {
// We don't care.
}
@Override
public void onNewWaypointsDone() {
// We don't care.
}
private void updateUI() { private void updateUI() {
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext()); boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category); boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
@@ -245,12 +208,14 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
// Set activity type // Set activity type
{ {
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
activityLabel.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE); activityLabel.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
activitySpinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE); activitySpinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
activitySpinner.setEnabled(false); activitySpinner.setEnabled(false);
if (trackIconValue != null) { if (trackIconValue != null) {
TrackIconUtils.setIconSpinner(activitySpinner, trackIconValue); activitySpinner.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
} }
} }
@@ -291,5 +256,18 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
speedMovingValue.setText(parts.first); speedMovingValue.setText(parts.first);
speedMovingUnit.setText(parts.second); speedMovingUnit.setText(parts.second);
} }
// Set elevation gain
{
// Make elevation visible?
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
Float elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : null;
Pair<String, String> parts = StringUtils.formatElevation(getContext(), elevationGain_m, metricUnits);
elevationTotalGainValue.setText(parts.first);
elevationTotalGainUnit.setText(parts.second);
}
} }
} }
@@ -22,9 +22,9 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackRecordingActivity; import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.content.TrackDataHub; import de.dennisguse.opentracks.content.TrackDataHub;
import de.dennisguse.opentracks.content.TrackDataListener; import de.dennisguse.opentracks.content.TrackDataListener;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.sensor.SensorDataCycling; import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate; import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
import de.dennisguse.opentracks.content.sensor.SensorDataSet; import de.dennisguse.opentracks.content.sensor.SensorDataSet;
@@ -55,13 +55,17 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection(); private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
private TrackPoint lastTrackPoint = null; private TrackPoint lastTrackPoint;
private TrackStatistics lastTrackStatistics = null; private TrackStatistics lastTrackStatistics;
private String category = ""; private String category = "";
@Deprecated //TODO This should be handled somewhere else; not in the UI. @Deprecated //TODO This should be handled somewhere else; not in the UI.
private int recordingGpsAccuracy; private int recordingGpsAccuracy;
public static Fragment newInstance() {
return new StatisticsRecordingFragment();
}
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() { private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
@@ -128,12 +132,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
private TextView speedMovingLabel; private TextView speedMovingLabel;
private TextView speedMovingValue; private TextView speedMovingValue;
private TextView speedMovingUnit; private TextView speedMovingUnit;
private Group elevationGroup;
private TextView speedLabel; private TextView speedLabel;
private TextView speedValue; private TextView speedValue;
private TextView speedUnit; private TextView speedUnit;
private TextView elevationValue; private Group elevationGroup;
private TextView elevationUnit; private TextView elevationCurrentValue;
private TextView elevationCurrentUnit;
private TextView elevationTotalGainValue;
private TextView elevationTotalGainUnit;
private Group coordinateGroup; private Group coordinateGroup;
private TextView latitudeValue; private TextView latitudeValue;
private TextView longitudeValue; private TextView longitudeValue;
@@ -174,14 +180,16 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
speedMovingValue = view.findViewById(R.id.stats_moving_speed_value); speedMovingValue = view.findViewById(R.id.stats_moving_speed_value);
speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit); speedMovingUnit = view.findViewById(R.id.stats_moving_speed_unit);
elevationGroup = view.findViewById(R.id.stats_elevation_current_group);
speedLabel = view.findViewById(R.id.stats_speed_label); speedLabel = view.findViewById(R.id.stats_speed_label);
speedValue = view.findViewById(R.id.stats_speed_value); speedValue = view.findViewById(R.id.stats_speed_value);
speedUnit = view.findViewById(R.id.stats_speed_unit); speedUnit = view.findViewById(R.id.stats_speed_unit);
elevationValue = view.findViewById(R.id.stats_elevation_current_value); elevationGroup = view.findViewById(R.id.stats_elevation_group);
elevationUnit = view.findViewById(R.id.stats_elevation_current_unit); elevationCurrentValue = view.findViewById(R.id.stats_elevation_current_value);
elevationCurrentUnit = view.findViewById(R.id.stats_elevation_current_unit);
elevationTotalGainValue = view.findViewById(R.id.stats_elevation_gain_value);
elevationTotalGainUnit = view.findViewById(R.id.stats_elevation_gain_unit);
coordinateGroup = view.findViewById(R.id.stats_coordinate_group); coordinateGroup = view.findViewById(R.id.stats_coordinate_group);
@@ -193,7 +201,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
recordingGpsAccuracy = Integer.parseInt(getContext().getResources().getString(R.string.recording_gps_accuracy_default)); recordingGpsAccuracy = Integer.parseInt(getResources().getString(R.string.recording_gps_accuracy_default));
handlerUpdateUI = new Handler(); handlerUpdateUI = new Handler();
@@ -272,14 +280,16 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
speedMovingValue = null; speedMovingValue = null;
speedMovingUnit = null; speedMovingUnit = null;
elevationGroup = null;
speedLabel = null; speedLabel = null;
speedValue = null; speedValue = null;
speedUnit = null; speedUnit = null;
elevationValue = null; elevationGroup = null;
elevationUnit = null; elevationCurrentValue = null;
elevationCurrentUnit = null;
elevationTotalGainValue = null;
elevationTotalGainUnit = null;
coordinateGroup = null; coordinateGroup = null;
@@ -346,17 +356,17 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
} }
@Override @Override
public void clearWaypoints() { public void clearMarkers() {
// We don't care. // We don't care.
} }
@Override @Override
public void onNewWaypoint(Waypoint wpt) { public void onNewMarker(Marker marker) {
// We don't care. // We don't care.
} }
@Override @Override
public void onNewWaypointsDone() { public void onNewMarkersDone() {
// We don't care. // We don't care.
} }
@@ -402,20 +412,25 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
/** /**
* Tries to fetch most recent {@link SensorDataSet} from {@link de.dennisguse.opentracks.services.TrackRecordingService}. * Tries to fetch most recent {@link SensorDataSet} from {@link de.dennisguse.opentracks.services.TrackRecordingService}.
* Also sets elevation gain.
*/ */
private void updateSensorDataUI() { private void updateSensorDataUI() {
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound(); TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
SensorDataSet sensorDataSet = null; SensorDataSet sensorDataSet = null;
Float elevationGain_m = null;
if (trackRecordingService == null) { if (trackRecordingService == null) {
Log.d(TAG, "Cannot get the track recording service."); Log.d(TAG, "Cannot get the track recording service.");
} else { } else {
sensorDataSet = trackRecordingService.getSensorData(); sensorDataSet = trackRecordingService.getSensorData();
elevationGain_m = trackRecordingService.getElevationGain_m();
} }
setHeartRateSensorData(sensorDataSet); setHeartRateSensorData(sensorDataSet);
setCadenceSensorData(sensorDataSet); setCadenceSensorData(sensorDataSet);
setSpeedSensorData(sensorDataSet, isSelectedTrackRecording()); setSpeedSensorData(sensorDataSet, isSelectedTrackRecording());
setTotalElevationGain(elevationGain_m);
} }
private void setHeartRateSensorData(SensorDataSet sensorDataSet) { private void setHeartRateSensorData(SensorDataSet sensorDataSet) {
@@ -464,6 +479,26 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
cadenceValueView.setText(sensorValue); cadenceValueView.setText(sensorValue);
} }
// Set elevation gain
private void setTotalElevationGain(Float elevationGain_m) {
//TODO Check if we can distribute the total elevation gain via trackStatistics instead of doing some computation in the UI layer.
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
Float totalElevationGain = elevationGain_m;
if (lastTrackStatistics != null && lastTrackStatistics.hasTotalElevationGain()) {
if (elevationGain_m == null) {
totalElevationGain = lastTrackStatistics.getTotalElevationGain();
} else {
totalElevationGain += lastTrackStatistics.getTotalElevationGain();
}
}
Pair<String, String> parts = StringUtils.formatElevation(getContext(), totalElevationGain, metricUnits);
elevationTotalGainValue.setText(parts.first);
elevationTotalGainUnit.setText(parts.second);
}
/** /**
* If cadence and hear rate groups are invisible then sensor horizontal line hast to be invisible too. * If cadence and hear rate groups are invisible then sensor horizontal line hast to be invisible too.
*/ */
@@ -471,7 +506,6 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
if (cadenceGroup.getVisibility() != View.VISIBLE && heartRateGroup.getVisibility() != View.VISIBLE) { if (cadenceGroup.getVisibility() != View.VISIBLE && heartRateGroup.getVisibility() != View.VISIBLE) {
sensorHorizontalLine.setVisibility(View.GONE); sensorHorizontalLine.setVisibility(View.GONE);
} }
} }
private void setSpeedSensorData(SensorDataSet sensorDataSet, boolean isRecording) { private void setSpeedSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
@@ -551,8 +585,9 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
speedMovingUnit.setText(parts.second); speedMovingUnit.setText(parts.second);
} }
// Make elevation visible? // Set elevation (gain)
{ {
// Make elevation visible?
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext()); boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
} }
@@ -593,11 +628,11 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE); elevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
if (showElevation) { if (showElevation) {
double altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? lastTrackPoint.getAltitude() : Double.NaN; // Current elevation
Float altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? (float) lastTrackPoint.getAltitude() : null;
Pair<String, String> parts = StringUtils.formatElevation(getContext(), altitude, metricUnits); Pair<String, String> parts = StringUtils.formatElevation(getContext(), altitude, metricUnits);
elevationCurrentValue.setText(parts.first);
elevationValue.setText(parts.first); elevationCurrentUnit.setText(parts.second);
elevationUnit.setText(parts.second);
} }
// Set coordinate // Set coordinate
@@ -0,0 +1,21 @@
package de.dennisguse.opentracks.io.file;
import androidx.documentfile.provider.DocumentFile;
import de.dennisguse.opentracks.content.data.Track;
public class ExportFile {
public static DocumentFile getExportDocumentFile(Track.Id trackId, String trackFileFormatExtension, DocumentFile directory, String mimeType) {
String fileName = getExportFileNameByTrackId(trackId, trackFileFormatExtension);
DocumentFile file = directory.findFile(fileName);
if (file == null) {
file = directory.createFile(mimeType, fileName);
}
return file;
}
private static String getExportFileNameByTrackId(Track.Id trackId, String trackFileFormatExtension) {
return trackId.getId() + "." + trackFileFormatExtension;
}
}
@@ -23,9 +23,9 @@ import androidx.annotation.NonNull;
import java.io.OutputStream; import java.io.OutputStream;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.LocationUtils;
@@ -64,8 +64,8 @@ public class FileTrackExporter implements TrackExporter {
try { try {
trackWriter.prepare(outputStream); trackWriter.prepare(outputStream);
trackWriter.writeHeader(tracks); trackWriter.writeHeader(tracks);
for (Track track1 : tracks) { for (Track track : tracks) {
writeWaypoints(track1); writeMarkers(track);
} }
boolean hasMultipleTracks = tracks.length > 1; boolean hasMultipleTracks = tracks.length > 1;
if (hasMultipleTracks) { if (hasMultipleTracks) {
@@ -89,34 +89,31 @@ public class FileTrackExporter implements TrackExporter {
} }
} }
/** private void writeMarkers(Track track) throws InterruptedException {
* Writes the waypoints.
*/
private void writeWaypoints(Track track) throws InterruptedException {
/* /*
* TODO: Stream through the waypoints in chunks. * TODO: Stream through the markers in chunks.
* I am leaving the number of waypoints very high which should not be a problem, because we don't try to load them into objects all at the same time. * I am leaving the number of markers very high which should not be a problem, because we don't try to load them into objects all at the same time.
*/ */
boolean hasWaypoints = false; boolean hasMarkers = false;
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), null, ContentProviderUtils.MAX_LOADED_WAYPOINTS_POINTS)) { try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, ContentProviderUtils.MAX_LOADED_MARKERS)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
if (Thread.interrupted()) { if (Thread.interrupted()) {
throw new InterruptedException(); throw new InterruptedException();
} }
if (!hasWaypoints) { if (!hasMarkers) {
trackWriter.writeBeginWaypoints(track); trackWriter.writeBeginMarkers(track);
hasWaypoints = true; hasMarkers = true;
} }
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor); Marker marker = contentProviderUtils.createMarker(cursor);
trackWriter.writeWaypoint(waypoint); trackWriter.writeMarker(marker);
cursor.moveToNext(); cursor.moveToNext();
} }
} }
} }
if (hasWaypoints) { if (hasMarkers) {
trackWriter.writeEndWaypoints(); trackWriter.writeEndMarkers();
} }
} }
@@ -23,9 +23,9 @@ import java.io.PrintWriter;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.util.Locale; import java.util.Locale;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
/** /**
@@ -33,7 +33,7 @@ import de.dennisguse.opentracks.util.StringUtils;
* *
* @author Sandor Dornbush * @author Sandor Dornbush
*/ */
//TODO Export waypoints //TODO Export markers
public class GpxTrackWriter implements TrackWriter { public class GpxTrackWriter implements TrackWriter {
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US); private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
@@ -120,27 +120,27 @@ public class GpxTrackWriter implements TrackWriter {
} }
@Override @Override
public void writeBeginWaypoints(Track track) { public void writeBeginMarkers(Track track) {
// Do nothing // Do nothing
} }
@Override @Override
public void writeEndWaypoints() { public void writeEndMarkers() {
// Do nothing // Do nothing
} }
@Override @Override
public void writeWaypoint(Waypoint waypoint) { public void writeMarker(Marker marker) {
if (printWriter != null) { if (printWriter != null) {
Location location = waypoint.getLocation(); Location location = marker.getLocation();
printWriter.println("<wpt " + formatLocation(location) + ">"); printWriter.println("<wpt " + formatLocation(location) + ">");
if (location.hasAltitude()) { if (location.hasAltitude()) {
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>"); printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
} }
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>"); printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
printWriter.println("<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>"); printWriter.println("<desc>" + StringUtils.formatCData(marker.getDescription()) + "</desc>");
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>"); printWriter.println("<type>" + StringUtils.formatCData(marker.getCategory()) + "</type>");
printWriter.println("</wpt>"); printWriter.println("</wpt>");
} }
} }
@@ -26,9 +26,9 @@ import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.DescriptionGenerator; import de.dennisguse.opentracks.content.DescriptionGenerator;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
@@ -40,7 +40,7 @@ import de.dennisguse.opentracks.util.StringUtils;
*/ */
public class KmlTrackWriter implements TrackWriter { public class KmlTrackWriter implements TrackWriter {
public static final String WAYPOINT_STYLE = "waypoint"; public static final String MARKER_STYLE = "waypoint";
private static final String START_STYLE = "start"; private static final String START_STYLE = "start";
private static final String END_STYLE = "end"; private static final String END_STYLE = "end";
private static final String TRACK_STYLE = "track"; private static final String TRACK_STYLE = "track";
@@ -50,8 +50,9 @@ public class KmlTrackWriter implements TrackWriter {
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence"; public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate"; public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
public static final String EXTENDED_DATA_TYPE_POWER = "power"; public static final String EXTENDED_DATA_TYPE_POWER = "power";
public static final String EXTENDED_DATA_TYPE_ELEVATION_GAIN = "elevation_gain";
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png"; private static final String MARKER_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png"; private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
private static final String END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png"; private static final String END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png"; private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
@@ -68,12 +69,13 @@ public class KmlTrackWriter implements TrackWriter {
private final List<Float> powerList = new ArrayList<>(); private final List<Float> powerList = new ArrayList<>();
private final List<Float> cadenceList = new ArrayList<>(); private final List<Float> cadenceList = new ArrayList<>();
private final List<Float> heartRateList = new ArrayList<>(); private final List<Float> heartRateList = new ArrayList<>();
private final List<Float> elevationGainList = new ArrayList<>();
private TrackPoint startTrackPoint; private TrackPoint startTrackPoint;
/** /**
* @param context the context * @param context the context
* @param exportTrackDetail should detailed information about the track be exported (e.g., title, description, waypoints, timing)? * @param exportTrackDetail should detailed information about the track be exported (e.g., title, description, markers, timing)?
* @param exportSensorData should {@link TrackPoint}'s sensor data be exported? * @param exportSensorData should {@link TrackPoint}'s sensor data be exported?
* @param exportPhotos should pictures be exported (if true: exports to KMZ)? * @param exportPhotos should pictures be exported (if true: exports to KMZ)?
*/ */
@@ -122,7 +124,7 @@ public class KmlTrackWriter implements TrackWriter {
writeTrackStyle(); writeTrackStyle();
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1); writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1); writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2); writePlacemarkerStyle(MARKER_STYLE, MARKER_ICON, 20, 2);
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">"); printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
writeSimpleArrayStyle(EXTENDED_DATA_TYPE_SPEED, context.getString(R.string.description_speed_ms)); writeSimpleArrayStyle(EXTENDED_DATA_TYPE_SPEED, context.getString(R.string.description_speed_ms));
@@ -145,7 +147,7 @@ public class KmlTrackWriter implements TrackWriter {
} }
@Override @Override
public void writeBeginWaypoints(Track track) { public void writeBeginMarkers(Track track) {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("<Folder>"); printWriter.println("<Folder>");
if (exportTrackDetail) { if (exportTrackDetail) {
@@ -156,21 +158,21 @@ public class KmlTrackWriter implements TrackWriter {
} }
@Override @Override
public void writeEndWaypoints() { public void writeEndMarkers() {
if (printWriter != null) { if (printWriter != null) {
printWriter.println("</Folder>"); printWriter.println("</Folder>");
} }
} }
@Override @Override
public void writeWaypoint(Waypoint waypoint) { public void writeMarker(Marker marker) {
if (printWriter != null && exportTrackDetail) { if (printWriter != null && exportTrackDetail) {
boolean existsPhoto = FileUtils.getPhotoFileIfExists(context, waypoint.getTrackId(), waypoint.getPhotoURI()) != null; boolean existsPhoto = FileUtils.getPhotoFileIfExists(context, marker.getTrackId(), marker.getPhotoURI()) != null;
if (waypoint.hasPhoto() && exportPhotos && existsPhoto) { if (marker.hasPhoto() && exportPhotos && existsPhoto) {
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation()); float heading = getHeading(marker.getTrackId(), marker.getLocation());
writePhotoOverlay(waypoint, heading); writePhotoOverlay(marker, heading);
} else { } else {
writePlacemark(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), WAYPOINT_STYLE, waypoint.getLocation()); writePlacemark(marker.getName(), marker.getCategory(), marker.getDescription(), MARKER_STYLE, marker.getLocation());
} }
} }
} }
@@ -257,6 +259,9 @@ public class KmlTrackWriter implements TrackWriter {
if (heartRateList.size() > 0) { if (heartRateList.size() > 0) {
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE); writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
} }
if (elevationGainList.size() > 0) {
writeSimpleArrayData(elevationGainList, EXTENDED_DATA_TYPE_ELEVATION_GAIN);
}
} }
printWriter.println("</SchemaData>"); printWriter.println("</SchemaData>");
printWriter.println("</ExtendedData>"); printWriter.println("</ExtendedData>");
@@ -287,6 +292,9 @@ public class KmlTrackWriter implements TrackWriter {
if (trackPoint.hasPower()) { if (trackPoint.hasPower()) {
powerList.add(trackPoint.getPower()); powerList.add(trackPoint.getPower());
} }
if (trackPoint.hasElevationGain()) {
elevationGainList.add(trackPoint.getElevationGain());
}
} }
} }
} }
@@ -329,30 +337,24 @@ public class KmlTrackWriter implements TrackWriter {
} }
} }
/** private void writePhotoOverlay(Marker marker, float heading) {
* Writes a photo overlay from waypoint.
*
* @param waypoint Waypoint object.
* @param heading the heading.
*/
private void writePhotoOverlay(Waypoint waypoint, float heading) {
if (exportTrackDetail) { if (exportTrackDetail) {
printWriter.println("<PhotoOverlay>"); printWriter.println("<PhotoOverlay>");
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>"); printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
printWriter.println("<description>" + StringUtils.formatCData(waypoint.getDescription()) + "</description>"); printWriter.println("<description>" + StringUtils.formatCData(marker.getDescription()) + "</description>");
printWriter.print("<Camera>"); printWriter.print("<Camera>");
printWriter.print("<longitude>" + waypoint.getLocation().getLongitude() + "</longitude>"); printWriter.print("<longitude>" + marker.getLocation().getLongitude() + "</longitude>");
printWriter.print("<latitude>" + waypoint.getLocation().getLatitude() + "</latitude>"); printWriter.print("<latitude>" + marker.getLocation().getLatitude() + "</latitude>");
printWriter.print("<altitude>20</altitude>"); printWriter.print("<altitude>20</altitude>");
printWriter.print("<heading>" + heading + "</heading>"); printWriter.print("<heading>" + heading + "</heading>");
printWriter.print("<tilt>90</tilt>"); printWriter.print("<tilt>90</tilt>");
printWriter.println("</Camera>"); printWriter.println("</Camera>");
printWriter.println("<TimeStamp><when>" + getTime(waypoint.getLocation()) + "</when></TimeStamp>"); printWriter.println("<TimeStamp><when>" + getTime(marker.getLocation()) + "</when></TimeStamp>");
printWriter.println("<styleUrl>#" + KmlTrackWriter.WAYPOINT_STYLE + "</styleUrl>"); printWriter.println("<styleUrl>#" + KmlTrackWriter.MARKER_STYLE + "</styleUrl>");
writeCategory(waypoint.getCategory()); writeCategory(marker.getCategory());
if (exportPhotos) { if (exportPhotos) {
printWriter.println("<Icon><href>" + KmzTrackExporter.buildKmzImageFilePath(waypoint) + "</href></Icon>"); printWriter.println("<Icon><href>" + KmzTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>");
} }
printWriter.print("<ViewVolume>"); printWriter.print("<ViewVolume>");
@@ -363,7 +365,7 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.print("<topFov>45</topFov>"); printWriter.print("<topFov>45</topFov>");
printWriter.println("</ViewVolume>"); printWriter.println("</ViewVolume>");
printWriter.println("<Point>"); printWriter.println("<Point>");
printWriter.println("<coordinates>" + getCoordinates(waypoint.getLocation(), ",") + "</coordinates>"); printWriter.println("<coordinates>" + getCoordinates(marker.getLocation(), ",") + "</coordinates>");
printWriter.println("</Point>"); printWriter.println("</Point>");
printWriter.println("</PhotoOverlay>"); printWriter.println("</PhotoOverlay>");
} }
@@ -31,8 +31,8 @@ import java.io.OutputStream;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
@@ -76,10 +76,7 @@ public class KmzTrackExporter implements TrackExporter {
@Override @Override
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) { public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
ZipOutputStream zipOutputStream = null; try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
try {
zipOutputStream = new ZipOutputStream(outputStream);
// Add kml file // Add kml file
ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE); ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE);
zipOutputStream.putNextEntry(zipEntry); zipOutputStream.putNextEntry(zipEntry);
@@ -97,31 +94,23 @@ public class KmzTrackExporter implements TrackExporter {
} catch (InterruptedException | IOException e) { } catch (InterruptedException | IOException e) {
Log.e(TAG, "Unable to write track", e); Log.e(TAG, "Unable to write track", e);
return false; return false;
} finally {
if (zipOutputStream != null) {
try {
zipOutputStream.close();
} catch (IOException e) {
Log.e(TAG, "Unable to close zip input stream", e);
}
}
} }
} }
private void addImages(Context context, Track[] tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException { private void addImages(Context context, Track[] tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
for (Track track : tracks) { for (Track track : tracks) {
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), null, -1)) { try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; i < cursor.getCount(); i++) { for (int i = 0; i < cursor.getCount(); i++) {
if (Thread.interrupted()) { if (Thread.interrupted()) {
throw new InterruptedException(); throw new InterruptedException();
} }
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor); Marker marker = contentProviderUtils.createMarker(cursor);
if (waypoint.hasPhoto()) { if (marker.hasPhoto()) {
Uri uriPhoto = waypoint.getPhotoURI(); Uri uriPhoto = marker.getPhotoURI();
boolean existsPhoto = FileUtils.getPhotoFileIfExists(context, track.getId(), uriPhoto) != null; boolean existsPhoto = FileUtils.getPhotoFileIfExists(context, track.getId(), uriPhoto) != null;
if (existsPhoto) { if (existsPhoto) {
addImage(context, zipOutputStream, uriPhoto, waypoint); addImage(context, zipOutputStream, uriPhoto, marker);
} }
} }
@@ -132,9 +121,9 @@ public class KmzTrackExporter implements TrackExporter {
} }
} }
private void addImage(Context context, ZipOutputStream zipOutputStream, Uri uri, Waypoint waypoint) throws IOException { private void addImage(Context context, ZipOutputStream zipOutputStream, Uri uri, Marker marker) throws IOException {
try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) { try (InputStream inputStream = context.getContentResolver().openInputStream(uri)) {
ZipEntry zipEntry = new ZipEntry(buildKmzImageFilePath(waypoint)); ZipEntry zipEntry = new ZipEntry(buildKmzImageFilePath(marker));
zipOutputStream.putNextEntry(zipEntry); zipOutputStream.putNextEntry(zipEntry);
if (inputStream == null) throw new FileNotFoundException(); if (inputStream == null) throw new FileNotFoundException();
@@ -157,13 +146,11 @@ public class KmzTrackExporter implements TrackExporter {
} }
/** /**
* Builds and returns the path for the image that will be saved inside KMZ_IMAGES_DIR for the waypoint. * Builds and returns the path for the image that will be saved inside KMZ_IMAGES_DIR for the marker.
*
* @param waypoint Waypoint object.
*/ */
public static String buildKmzImageFilePath(Waypoint waypoint) { public static String buildKmzImageFilePath(Marker marker) {
String ext = FileUtils.getExtension(waypoint.getPhotoUrl()); String ext = FileUtils.getExtension(marker.getPhotoUrl());
ext = ext == null ? "" : "." + ext; ext = ext == null ? "" : "." + ext;
return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(waypoint.getId() + ext); return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(marker.getId().getId() + ext);
} }
} }
@@ -17,9 +17,9 @@ package de.dennisguse.opentracks.io.file.exporter;
import java.io.OutputStream; import java.io.OutputStream;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
/** /**
* Interface for writing tracks to a file. The expected sequence of calls is: * Interface for writing tracks to a file. The expected sequence of calls is:
@@ -28,10 +28,10 @@ import de.dennisguse.opentracks.content.data.Waypoint;
* {@link #prepare(OutputStream)} * {@link #prepare(OutputStream)}
* {@link #writeHeader(Track[])} * {@link #writeHeader(Track[])}
* For each track: * For each track:
* {@link #writeBeginWaypoints(Track)} * {@link #writeBeginMarkers(Track)}
* For each waypoint: * For each marker:
* {@link #writeWaypoint(Waypoint)} * {@link #writeMarker(Marker)}
* {@link #writeEndWaypoints()} * {@link #writeEndMarkers()}
* {@link #writeMultiTrackBegin()} * {@link #writeMultiTrackBegin()}
* For each track: * For each track:
* {@link #writeBeginTrack(Track, TrackPoint)} * {@link #writeBeginTrack(Track, TrackPoint)}
@@ -50,11 +50,6 @@ import de.dennisguse.opentracks.content.data.Waypoint;
*/ */
public interface TrackWriter { public interface TrackWriter {
/**
* Prepares the output stream.
*
* @param outputStream the output stream
*/
void prepare(OutputStream outputStream); void prepare(OutputStream outputStream);
/** /**
@@ -62,77 +57,27 @@ public interface TrackWriter {
*/ */
void close(); void close();
/**
* Writes the header
*
* @param tracks the tracks
*/
void writeHeader(Track[] tracks); void writeHeader(Track[] tracks);
/**
* Writes the footer.
*/
void writeFooter(); void writeFooter();
/** void writeBeginMarkers(Track track);
* Writes the beginning of the waypoints.
*
* @param track the track
*/
void writeBeginWaypoints(Track track);
/** void writeEndMarkers();
* Writes the end of the waypoints.
*/
void writeEndWaypoints();
/** void writeMarker(Marker marker);
* Writes a waypoint.
*
* @param waypoint the waypoint
*/
void writeWaypoint(Waypoint waypoint);
/**
* Writes the beginning of the tracks.
*/
void writeMultiTrackBegin(); void writeMultiTrackBegin();
/**
* Writes the end of the tracks,
*/
void writeMultiTrackEnd(); void writeMultiTrackEnd();
/**
* Writes the beginning of a track.
*
* @param track the track
* @param startTrackPoint the start location
*/
void writeBeginTrack(Track track, TrackPoint startTrackPoint); void writeBeginTrack(Track track, TrackPoint startTrackPoint);
/**
* Writes the end of a track.
*
* @param track the track
* @param endTrackPoint the end location
*/
void writeEndTrack(Track track, TrackPoint endTrackPoint); void writeEndTrack(Track track, TrackPoint endTrackPoint);
/**
* Writes open segment.
*/
void writeOpenSegment(); void writeOpenSegment();
/**
* Writes close segment.
*/
void writeCloseSegment(); void writeCloseSegment();
/**
* Writes a trackPoint.
*
* @param trackPoint the trackPoint
*/
void writeTrackPoint(TrackPoint trackPoint); void writeTrackPoint(TrackPoint trackPoint);
} }
@@ -39,9 +39,9 @@ import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.content.provider.TrackPointIterator;
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater; import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
@@ -70,7 +70,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
private Track.Id importTrackId; private Track.Id importTrackId;
private final List<Track.Id> trackIds = new ArrayList<>(); private final List<Track.Id> trackIds = new ArrayList<>();
private final List<Waypoint> waypoints = new ArrayList<>(); private final List<Marker> markers = new ArrayList<>();
// The current element content // The current element content
//TODO Should be made private and getter be used by child classes. //TODO Should be made private and getter be used by child classes.
@@ -88,7 +88,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
protected String heartrate; protected String heartrate;
protected String cadence; protected String cadence;
protected String power; protected String power;
protected String waypointType; protected String markerType;
protected String photoUrl; protected String photoUrl;
protected String uuid; protected String uuid;
@@ -148,7 +148,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
* On file end. * On file end.
*/ */
protected void onFileEnd() { protected void onFileEnd() {
// Add waypoints to the last imported track // Add markers to the last imported track
int size = trackIds.size(); int size = trackIds.size();
if (size == 0) { if (size == 0) {
return; return;
@@ -159,8 +159,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
return; return;
} }
int waypointPosition = -1; int markerPosition = -1;
Waypoint waypoint = null; Marker marker = null;
TrackPoint trackPoint = null; TrackPoint trackPoint = null;
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics().getStartTime_ms()); TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics().getStartTime_ms());
// TODO Should not be necessary anymore? // TODO Should not be necessary anymore?
@@ -169,18 +169,18 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false)) { try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false)) {
while (true) { while (true) {
if (waypoint == null) { if (marker == null) {
waypointPosition++; markerPosition++;
waypoint = waypointPosition < waypoints.size() ? waypoints.get(waypointPosition) : null; marker = markerPosition < markers.size() ? markers.get(markerPosition) : null;
if (waypoint == null) { if (marker == null) {
// No more waypoints // No more markers
return; return;
} }
} }
if (trackPoint == null) { if (trackPoint == null) {
if (!trackPointIterator.hasNext()) { if (!trackPointIterator.hasNext()) {
// No more track points. Ignore the rest of the waypoints. // No more track points. Ignore the rest of the markers.
return; return;
} }
trackPoint = trackPointIterator.next(); trackPoint = trackPointIterator.next();
@@ -188,13 +188,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
markerTrackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval); markerTrackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
} }
if (waypoint.getLocation().getTime() > trackPoint.getTime()) { if (marker.getLocation().getTime() > trackPoint.getTime()) {
trackPoint = null; trackPoint = null;
} else if (waypoint.getLocation().getTime() < trackPoint.getTime()) { } else if (marker.getLocation().getTime() < trackPoint.getTime()) {
Log.w(TAG, "Ignoring waypoint: current trackPoint was after waypoint."); Log.w(TAG, "Ignoring marker: current trackPoint was after marker.");
waypoint = null; marker = null;
} else { } else {
// The waypoint trackPoint time matches the track point time // The marker trackPoint time matches the track point time
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) { if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
// Invalid trackPoint, load the next trackPoint // Invalid trackPoint, load the next trackPoint
trackPoint = null; trackPoint = null;
@@ -202,19 +202,19 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
} }
// Valid trackPoint // Valid trackPoint
if (trackPoint.getLatitude() == waypoint.getLocation().getLatitude() && trackPoint.getLongitude() == waypoint.getLocation().getLongitude()) { if (trackPoint.getLatitude() == marker.getLocation().getLatitude() && trackPoint.getLongitude() == marker.getLocation().getLongitude()) {
String waypointDescription = waypoint.getDescription(); String markerDescription = marker.getDescription();
String icon = context.getString(R.string.marker_waypoint_icon_url); String icon = context.getString(R.string.marker_icon_url);
double length = trackStatisticsUpdater.getTrackStatistics().getTotalDistance(); double length = trackStatisticsUpdater.getTrackStatistics().getTotalDistance();
long duration = trackStatisticsUpdater.getTrackStatistics().getTotalTime(); long duration = trackStatisticsUpdater.getTrackStatistics().getTotalTime();
// Insert waypoint // Insert marker
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), length, duration, trackPoint.getLocation(), waypoint.getPhotoUrl()); Marker newMarker = new Marker(marker.getName(), markerDescription, marker.getCategory(), icon, track.getId(), length, duration, trackPoint.getLocation(), marker.getPhotoUrl());
contentProviderUtils.insertWaypoint(newWaypoint); contentProviderUtils.insertMarker(newMarker);
} }
// Load the next waypoint // Load the next marker
waypoint = null; marker = null;
} }
} }
} }
@@ -231,7 +231,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
trackId = new Track.Id(Long.parseLong(uri.getLastPathSegment())); trackId = new Track.Id(Long.parseLong(uri.getLastPathSegment()));
} else { } else {
if (trackIds.size() > 0) { if (trackIds.size() > 0) {
throw new SAXException(createErrorMessage("Cannot import more than one track to an existing track " + importTrackId)); throw new SAXException(createErrorMessage("Cannot import more than one track to an existing track " + importTrackId.getId()));
} }
trackId = importTrackId; trackId = importTrackId;
contentProviderUtils.clearTrack(trackId); contentProviderUtils.clearTrack(trackId);
@@ -300,11 +300,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
trackData.lastLocationInCurrentSegment = null; trackData.lastLocationInCurrentSegment = null;
} }
/** protected void addMarker() throws SAXException {
* Adds a waypoint. // Markers must have a time, else cannot match to the track points
*/
protected void addWaypoint() throws SAXException {
// Waypoint must have a time, else cannot match to the track points
if (time == null) { if (time == null) {
return; return;
} }
@@ -314,22 +311,22 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
if (!LocationUtils.isValidLocation(trackPoint.getLocation())) { if (!LocationUtils.isValidLocation(trackPoint.getLocation())) {
throw new SAXException(createErrorMessage("Invalid location detected: " + trackPoint)); throw new SAXException(createErrorMessage("Invalid location detected: " + trackPoint));
} }
Waypoint waypoint = new Waypoint(trackPoint.getLocation()); Marker marker = new Marker(trackPoint.getLocation());
if (name != null) { if (name != null) {
waypoint.setName(name); marker.setName(name);
} }
if (description != null) { if (description != null) {
waypoint.setDescription(description); marker.setDescription(description);
} }
if (category != null) { if (category != null) {
waypoint.setCategory(category); marker.setCategory(category);
} }
if (photoUrl != null) { if (photoUrl != null) {
waypoint.setPhotoUrl(photoUrl); marker.setPhotoUrl(photoUrl);
} }
waypoints.add(waypoint); markers.add(marker);
} }
/** /**
@@ -21,7 +21,7 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); configureDirectoryChooserIntent(intent);
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE); startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
} }
@@ -33,19 +33,23 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
Uri directoryUri = resultData.getData(); Uri directoryUri = resultData.getData();
startActivity(createIntent(directoryUri)); startActivity(createNextActivityIntent(directoryUri));
} }
finish(); finish();
} }
} }
protected abstract Intent createIntent(Uri directoryUri); protected void configureDirectoryChooserIntent(Intent intent) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
protected abstract Intent createNextActivityIntent(Uri directoryUri);
public static class ImportDirectoryChooserActivity extends DirectoryChooserActivity { public static class ImportDirectoryChooserActivity extends DirectoryChooserActivity {
@Override @Override
protected Intent createIntent(Uri directoryUri) { protected Intent createNextActivityIntent(Uri directoryUri) {
Intent intent = IntentUtils.newIntent(this, ImportActivity.class); Intent intent = IntentUtils.newIntent(this, ImportActivity.class);
intent.putExtra(ImportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri); intent.putExtra(ImportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
return intent; return intent;
@@ -55,11 +59,17 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
public static class ExportDirectoryChooserActivity extends DirectoryChooserActivity { public static class ExportDirectoryChooserActivity extends DirectoryChooserActivity {
@Override @Override
protected Intent createIntent(Uri directoryUri) { protected Intent createNextActivityIntent(Uri directoryUri) {
Intent intent = IntentUtils.newIntent(this, ExportActivity.class); Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri); intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this)); intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this));
return intent; return intent;
} }
@Override
protected void configureDirectoryChooserIntent(Intent intent) {
super.configureDirectoryChooserIntent(intent);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} }
} }
@@ -44,7 +44,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
private static final String TAG_TRACK_POINT = "trkpt"; private static final String TAG_TRACK_POINT = "trkpt";
private static final String TAG_TRACK_SEGMENT = "trkseg"; private static final String TAG_TRACK_SEGMENT = "trkseg";
private static final String TAG_TYPE = "type"; private static final String TAG_TYPE = "type";
private static final String TAG_WAYPOINT = "wpt"; private static final String TAG_MARKER = "wpt";
private static final String TAG_ID = "opentracks:trackid"; private static final String TAG_ID = "opentracks:trackid";
private static final String ATTRIBUTE_LAT = "lat"; private static final String ATTRIBUTE_LAT = "lat";
@@ -71,8 +71,8 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
@Override @Override
public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException { public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException {
switch (tag) { switch (tag) {
case TAG_WAYPOINT: case TAG_MARKER:
onWaypointStart(attributes); onMarkerStart(attributes);
break; break;
case TAG_TRACK: case TAG_TRACK:
onTrackStart(); onTrackStart();
@@ -92,8 +92,8 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
case TAG_GPX: case TAG_GPX:
onFileEnd(); onFileEnd();
break; break;
case TAG_WAYPOINT: case TAG_MARKER:
onWaypointEnd(); onMarkerEnd();
break; break;
case TAG_TRACK: case TAG_TRACK:
onTrackEnd(); onTrackEnd();
@@ -128,7 +128,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
break; break;
case TAG_COMMENT: case TAG_COMMENT:
if (content != null) { if (content != null) {
waypointType = content.trim(); markerType = content.trim();
} }
break; break;
case TAG_EXTENSION_SPEED: case TAG_EXTENSION_SPEED:
@@ -187,12 +187,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
insertTrackPoint(trackPoint); insertTrackPoint(trackPoint);
} }
/** private void onMarkerStart(Attributes attributes) {
* On waypoint start.
*
* @param attributes the attributes
*/
private void onWaypointStart(Attributes attributes) {
name = null; name = null;
description = null; description = null;
category = null; category = null;
@@ -201,13 +196,10 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
longitude = attributes.getValue(ATTRIBUTE_LON); longitude = attributes.getValue(ATTRIBUTE_LON);
altitude = null; altitude = null;
time = null; time = null;
waypointType = null; markerType = null;
} }
/** private void onMarkerEnd() throws SAXException {
* On waypoint end. addMarker();
*/
private void onWaypointEnd() throws SAXException {
addWaypoint();
} }
} }
@@ -39,7 +39,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private static final String TAG = KmlFileTrackImporter.class.getSimpleName(); private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
private static final String WAYPOINT_STYLE = "#" + KmlTrackWriter.WAYPOINT_STYLE; private static final String MARKER_STYLE = "#" + KmlTrackWriter.MARKER_STYLE;
private static final String TAG_COORDINATES = "coordinates"; private static final String TAG_COORDINATES = "coordinates";
private static final String TAG_DESCRIPTION = "description"; private static final String TAG_DESCRIPTION = "description";
@@ -63,11 +63,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private boolean trackStarted = false; private boolean trackStarted = false;
private String extendedDataType; private String extendedDataType;
private ArrayList<TrackPoint> trackPoints; private ArrayList<TrackPoint> trackPoints = new ArrayList<>();
private ArrayList<Float> speedList; private ArrayList<Float> speedList = new ArrayList<>();
private ArrayList<Float> cadenceList; private ArrayList<Float> cadenceList = new ArrayList<>();
private ArrayList<Float> heartRateList; private ArrayList<Float> heartRateList = new ArrayList<>();
private ArrayList<Float> powerList; private ArrayList<Float> powerList = new ArrayList<>();
private ArrayList<Float> elevationGainList = new ArrayList<>();
public KmlFileTrackImporter(Context context) { public KmlFileTrackImporter(Context context) {
this(context, new ContentProviderUtils(context)); this(context, new ContentProviderUtils(context));
@@ -83,8 +84,8 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
switch (tag) { switch (tag) {
case TAG_PLACEMARK: case TAG_PLACEMARK:
case TAG_PHOTO_OVERLAY: case TAG_PHOTO_OVERLAY:
// Note that a track is contained in a Placemark, calling onWaypointStart will clear various track variables like name, category, and description. // Note that a track is contained in a Placemark, calling onMarkerStart will clear various track variables like name, category, and description.
onWaypointStart(); onMarkerStart();
break; break;
case TAG_GX_MULTI_TRACK: case TAG_GX_MULTI_TRACK:
trackStarted = true; trackStarted = true;
@@ -110,11 +111,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
break; break;
case TAG_PLACEMARK: case TAG_PLACEMARK:
case TAG_PHOTO_OVERLAY: case TAG_PHOTO_OVERLAY:
// Note that a track is contained in a Placemark, calling onWaypointend is save since waypointType is not set for a track. // Note that a track is contained in a Placemark, calling onMarkerEnd is save since markerType is not set for a track.
onWaypointEnd(); onMarkerEnd();
break; break;
case TAG_COORDINATES: case TAG_COORDINATES:
onWaypointLocationEnd(); onMarkerLocationEnd();
break; break;
case TAG_GX_MULTI_TRACK: case TAG_GX_MULTI_TRACK:
onTrackEnd(); onTrackEnd();
@@ -160,7 +161,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
break; break;
case TAG_STYLE_URL: case TAG_STYLE_URL:
if (content != null) { if (content != null) {
waypointType = content.trim(); markerType = content.trim();
} }
break; break;
case TAG_HREF: case TAG_HREF:
@@ -174,10 +175,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
content = null; content = null;
} }
/** private void onMarkerStart() {
* On waypoint start.
*/
private void onWaypointStart() {
// Reset all Placemark variables // Reset all Placemark variables
name = null; name = null;
icon = null; icon = null;
@@ -188,27 +186,21 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
longitude = null; longitude = null;
altitude = null; altitude = null;
time = null; time = null;
waypointType = null; markerType = null;
} }
/** private void onMarkerEnd() throws SAXException {
* On waypoint end. if (!MARKER_STYLE.equals(markerType)) {
*/
private void onWaypointEnd() throws SAXException {
if (!WAYPOINT_STYLE.equals(waypointType)) {
return; return;
} }
// If there is photoUrl it has to be changed because that url in kml file is a relative path to the internal kmz file. // If there is photoUrl it has to be changed because that url in kml file is a relative path to the internal kmz file.
photoUrl = getInternalPhotoUrl(photoUrl); photoUrl = getInternalPhotoUrl(photoUrl);
addWaypoint(); addMarker();
} }
/** private void onMarkerLocationEnd() {
* On waypoint location end.
*/
private void onWaypointLocationEnd() {
if (content != null) { if (content != null) {
String[] parts = content.trim().split(","); String[] parts = content.trim().split(",");
if (parts.length != 2 && parts.length != 3) { if (parts.length != 2 && parts.length != 3) {
@@ -223,11 +215,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
@Override @Override
protected void onTrackSegmentStart() { protected void onTrackSegmentStart() {
super.onTrackSegmentStart(); super.onTrackSegmentStart();
trackPoints = new ArrayList<>(); trackPoints.clear();
speedList = new ArrayList<>(); speedList.clear();
heartRateList = new ArrayList<>(); heartRateList.clear();
cadenceList = new ArrayList<>(); cadenceList.clear();
powerList = new ArrayList<>(); powerList.clear();
elevationGainList.clear();
} }
/** /**
@@ -250,6 +243,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
if (i < powerList.size()) { if (i < powerList.size()) {
trackPoint.setPower(powerList.get(i)); trackPoint.setPower(powerList.get(i));
} }
if (i < elevationGainList.size()) {
trackPoint.setElevationGain(elevationGainList.get(i));
}
insertTrackPoint(trackPoint); insertTrackPoint(trackPoint);
} }
@@ -316,6 +312,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE: case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE:
cadenceList.add(value); cadenceList.add(value);
break; break;
case KmlTrackWriter.EXTENDED_DATA_TYPE_ELEVATION_GAIN:
elevationGainList.add(value);
break;
default: default:
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported."); Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
} }
@@ -32,8 +32,8 @@ import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; 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.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter; import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
@@ -191,20 +191,20 @@ public class KmzTrackImporter implements TrackImporter {
} }
/** /**
* Deletes all images that remained in external storage that doesn't have waypoint (marker) associated. * Deletes all images that remained in external storage that doesn't have a marker associated.
* *
* @param context the Context object. * @param context the Context object.
* @param trackId the id of the Track. * @param trackId the id of the Track.
*/ */
private void deleteOrphanImages(Context context, Track.Id trackId) { private void deleteOrphanImages(Context context, Track.Id trackId) {
if (!trackId.isValid()) { if (!trackId.isValid()) {
// 1.- Gets all photo names in the waypoints of the track identified by id. // 1.- Gets all photo names in the markers of the track identified by id.
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context); ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
List<Waypoint> waypoints = contentProviderUtils.getWaypoints(trackId); List<Marker> markers = contentProviderUtils.getMarkers(trackId);
List<String> photosName = new ArrayList<>(); List<String> photosName = new ArrayList<>();
for (Waypoint w : waypoints) { for (Marker marker : markers) {
if (w.hasPhoto()) { if (marker.hasPhoto()) {
String photoUrl = Uri.decode(w.getPhotoUrl()); String photoUrl = Uri.decode(marker.getPhotoUrl());
photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1)); photosName.add(photoUrl.substring(photoUrl.lastIndexOf(File.separatorChar) + 1));
} }
} }
@@ -39,9 +39,9 @@ import java.util.List;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackListActivity; import de.dennisguse.opentracks.TrackListActivity;
import de.dennisguse.opentracks.TrackRecordingActivity; import de.dennisguse.opentracks.TrackRecordingActivity;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.CustomContentProvider; import de.dennisguse.opentracks.content.provider.CustomContentProvider;
import de.dennisguse.opentracks.content.provider.TrackPointIterator; import de.dennisguse.opentracks.content.provider.TrackPointIterator;
@@ -49,6 +49,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
import de.dennisguse.opentracks.services.handlers.HandlerServer; import de.dennisguse.opentracks.services.handlers.HandlerServer;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.services.sensors.ElevationSumManager;
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory; import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor; import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
@@ -115,6 +116,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
// The following variables are set when recording: // The following variables are set when recording:
private WakeLock wakeLock; private WakeLock wakeLock;
private BluetoothRemoteSensorManager remoteSensorManager; private BluetoothRemoteSensorManager remoteSensorManager;
private ElevationSumManager elevationSumManager;
private TrackStatisticsUpdater trackStatisticsUpdater; private TrackStatisticsUpdater trackStatisticsUpdater;
private TrackPoint lastTrackPoint; private TrackPoint lastTrackPoint;
@@ -174,6 +176,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
remoteSensorManager = null; remoteSensorManager = null;
} }
if (elevationSumManager != null) {
elevationSumManager.stop(this);
elevationSumManager = null;
}
// Reverse order from onCreate // Reverse order from onCreate
showNotification(false); //TODO Why? showNotification(false); //TODO Why?
@@ -215,6 +222,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return trackStatisticsUpdater.getTrackStatistics(); return trackStatisticsUpdater.getTrackStatistics();
} }
//TODO Throw exception, when not recording.
public long getTotalTime() { public long getTotalTime() {
if (trackStatisticsUpdater == null) { if (trackStatisticsUpdater == null) {
return 0; return 0;
@@ -225,43 +233,38 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
return trackStatisticsUpdater.getTrackStatistics().getTotalTime(); return trackStatisticsUpdater.getTrackStatistics().getTotalTime();
} }
/** public Marker.Id insertMarker(String name, String category, String description, String photoUrl) {
* Inserts a waypoint.
*
* @return the waypoint id
*/
public Waypoint.Id insertWaypoint(String name, String category, String description, String photoUrl) {
if (!isRecording() || isPaused()) { if (!isRecording() || isPaused()) {
return null; return null;
} }
if (name == null) { if (name == null) {
int nextWaypointNumber = contentProviderUtils.getNextWaypointNumber(recordingTrackId); int nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(recordingTrackId);
if (nextWaypointNumber == -1) { if (nextMarkerNumber == -1) {
nextWaypointNumber = 1; nextMarkerNumber = 1;
} }
name = getString(R.string.marker_name_format, nextWaypointNumber + 1); name = getString(R.string.marker_name_format, nextMarkerNumber + 1);
} }
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(recordingTrackId); TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(recordingTrackId);
if (trackPoint == null) { if (trackPoint == null) {
Log.i(TAG, "Could not create a waypoint as trackPoint is unknown."); Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
return null; return null;
} }
category = category != null ? category : ""; category = category != null ? category : "";
description = description != null ? description : ""; description = description != null ? description : "";
String icon = getString(R.string.marker_waypoint_icon_url); String icon = getString(R.string.marker_icon_url);
photoUrl = photoUrl != null ? photoUrl : ""; photoUrl = photoUrl != null ? photoUrl : "";
TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics(); TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics();
double length = stats.getTotalDistance(); double length = stats.getTotalDistance();
long duration = stats.getTotalTime(); long duration = stats.getTotalTime();
// Insert waypoint // Insert marker
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, length, duration, trackPoint.getLocation(), photoUrl); Marker marker = new Marker(name, description, category, icon, recordingTrackId, length, duration, trackPoint.getLocation(), photoUrl);
Uri uri = contentProviderUtils.insertWaypoint(waypoint); Uri uri = contentProviderUtils.insertMarker(marker);
return new Waypoint.Id(ContentUris.parseId(uri)); return new Marker.Id(ContentUris.parseId(uri));
} }
/** /**
@@ -308,7 +311,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
void resumeTrack(Track.Id trackId) { void resumeTrack(Track.Id trackId) {
Track track = contentProviderUtils.getTrack(trackId); Track track = contentProviderUtils.getTrack(trackId);
if (track == null) { if (track == null) {
Log.e(TAG, "Ignore resumeTrack. Track " + trackId + " does not exists."); Log.e(TAG, "Ignore resumeTrack. Track " + trackId.getId() + " does not exists.");
return; return;
} }
@@ -332,7 +335,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
Track track = contentProviderUtils.getTrack(recordingTrackId); Track track = contentProviderUtils.getTrack(recordingTrackId);
if (track == null) { if (track == null) {
if (isRecording()) { if (isRecording()) {
Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId); Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId.getId());
updateRecordingState(null, true); updateRecordingState(null, true);
} }
showNotification(false); showNotification(false);
@@ -375,6 +378,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
// Update instance variables // Update instance variables
remoteSensorManager = new BluetoothRemoteSensorManager(this); remoteSensorManager = new BluetoothRemoteSensorManager(this);
remoteSensorManager.start(); remoteSensorManager.start();
elevationSumManager = new ElevationSumManager();
elevationSumManager.start(this);
lastTrackPoint = null; lastTrackPoint = null;
isIdle = false; isIdle = false;
@@ -460,6 +467,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
remoteSensorManager.stop(); remoteSensorManager.stop();
remoteSensorManager = null; remoteSensorManager = null;
} }
if (elevationSumManager != null) {
elevationSumManager.stop(this);
elevationSumManager = null;
}
lastTrackPoint = null; lastTrackPoint = null;
handlerServer.stop(this); handlerServer.stop(this);
@@ -560,15 +572,17 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
insertTrackPoint(track, TrackPoint.createPause()); insertTrackPoint(track, TrackPoint.createPause());
insertTrackPoint(track, trackPoint); insertTrackPoint(track, trackPoint);
isIdle = false;
isIdle = false;
lastTrackPoint = trackPoint; lastTrackPoint = trackPoint;
return; return;
} }
if (trackPoint.hasSensorData() || distanceToLastTrackLocation >= recordingDistanceInterval) { if (trackPoint.hasSensorData() || distanceToLastTrackLocation >= recordingDistanceInterval) {
insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPointIfNewer(track, lastTrackPoint);
insertTrackPoint(track, trackPoint); insertTrackPoint(track, trackPoint);
isIdle = false; isIdle = false;
lastTrackPoint = trackPoint; lastTrackPoint = trackPoint;
@@ -577,7 +591,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
if (!isIdle && !TrackPointUtils.isMoving(trackPoint)) { if (!isIdle && !TrackPointUtils.isMoving(trackPoint)) {
insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPointIfNewer(track, lastTrackPoint);
insertTrackPoint(track, trackPoint); insertTrackPoint(track, trackPoint);
isIdle = true; isIdle = true;
lastTrackPoint = trackPoint; lastTrackPoint = trackPoint;
@@ -586,7 +602,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
if (isIdle && TrackPointUtils.isMoving(trackPoint)) { if (isIdle && TrackPointUtils.isMoving(trackPoint)) {
insertTrackPointIfNewer(track, lastTrackPoint); insertTrackPointIfNewer(track, lastTrackPoint);
insertTrackPoint(track, trackPoint); insertTrackPoint(track, trackPoint);
isIdle = false; isIdle = false;
lastTrackPoint = trackPoint; lastTrackPoint = trackPoint;
@@ -636,6 +654,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
*/ */
private void insertTrackPoint(@NonNull Track track, @NonNull TrackPoint trackPoint) { private void insertTrackPoint(@NonNull Track track, @NonNull TrackPoint trackPoint) {
try { try {
if (elevationSumManager != null) {
trackPoint.setElevationGain(elevationSumManager.getElevationGain_m());
elevationSumManager.reset();
}
contentProviderUtils.insertTrackPoint(trackPoint, track.getId()); contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval); trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
updateTrackTotalTime(track); updateTrackTotalTime(track);
@@ -676,6 +698,17 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
} }
} }
/**
* Returns the relative elevation gain (since last trackpoint).
*/
Float getElevationGain_m() {
if (elevationSumManager == null || !elevationSumManager.isConnected()) {
return null;
}
return elevationSumManager.getElevationGain_m();
}
private void showNotification(boolean isGpsStarted) { private void showNotification(boolean isGpsStarted) {
if (isRecording()) { if (isRecording()) {
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class) Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
@@ -2,9 +2,9 @@ package de.dennisguse.opentracks.services;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.sensor.SensorDataSet; import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
@@ -90,8 +90,8 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
} }
@Override @Override
public Waypoint.Id insertWaypoint(String name, String category, String description, String photoUrl) { public Marker.Id insertMarker(String name, String category, String description, String photoUrl) {
return trackRecordingService.insertWaypoint(name, category, description, photoUrl); return trackRecordingService.insertMarker(name, category, description, photoUrl);
} }
@Override @Override
@@ -99,6 +99,11 @@ class TrackRecordingServiceBinder extends android.os.Binder implements TrackReco
return trackRecordingService.getSensorDataSet(); return trackRecordingService.getSensorDataSet();
} }
@Override
public Float getElevationGain_m() {
return trackRecordingService.getElevationGain_m();
}
@VisibleForTesting @VisibleForTesting
@Override @Override
public void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager) { public void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager) {
@@ -31,8 +31,8 @@ import androidx.annotation.NonNull;
import de.dennisguse.opentracks.BuildConfig; import de.dennisguse.opentracks.BuildConfig;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackEditActivity; import de.dennisguse.opentracks.TrackEditActivity;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.util.IntentUtils; import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -181,16 +181,16 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
* *
* @return the id of the marker or null if none could be created. * @return the id of the marker or null if none could be created.
*/ */
public Waypoint.Id addMarker(Context context, String name, String category, String description, String photoUrl) { public Marker.Id addMarker(Context context, String name, String category, String description, String photoUrl) {
TrackRecordingServiceInterface trackRecordingService = getServiceIfBound(); TrackRecordingServiceInterface trackRecordingService = getServiceIfBound();
if (trackRecordingService == null) { if (trackRecordingService == null) {
Log.d(TAG, "Unable to add marker, no track recording service"); Log.d(TAG, "Unable to add marker, no track recording service");
} else { } else {
try { try {
Waypoint.Id waypoint = trackRecordingService.insertWaypoint(name, category, description, photoUrl); Marker.Id marker = trackRecordingService.insertMarker(name, category, description, photoUrl);
if (waypoint != null) { if (marker != null) {
Toast.makeText(context, R.string.marker_add_success, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.marker_add_success, Toast.LENGTH_SHORT).show();
return waypoint; return marker;
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.e(TAG, "Unable to add marker.", e); Log.e(TAG, "Unable to add marker.", e);
@@ -201,12 +201,6 @@ public class TrackRecordingServiceConnection implements ServiceConnection, Death
return null; return null;
} }
/**
* Stops the recording.
*
* @param context the context
* @param showEditor true to show the editor
*/
public void stopRecording(@NonNull Context context, boolean showEditor) { public void stopRecording(@NonNull Context context, boolean showEditor) {
TrackRecordingServiceInterface trackRecordingService = getServiceIfBound(); TrackRecordingServiceInterface trackRecordingService = getServiceIfBound();
if (trackRecordingService == null) { if (trackRecordingService == null) {
@@ -17,9 +17,9 @@ package de.dennisguse.opentracks.services;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.sensor.SensorDataSet; import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.services.handlers.GpsStatusValue; import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager; import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
@@ -30,46 +30,20 @@ import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
*/ */
public interface TrackRecordingServiceInterface { public interface TrackRecordingServiceInterface {
/**
* Starts gps.
*/
void startGps(); void startGps();
/**
* Stops gps.
*/
void stopGps(); void stopGps();
/**
* Starts recording a new track.
*
* @return the track ID of the new track.
*/
Track.Id startNewTrack(); Track.Id startNewTrack();
/**
* Resumes the track identified by trackId.
*/
void resumeTrack(Track.Id trackId);
/**
* Pauses the current recording track.
*/
void pauseCurrentTrack(); void pauseCurrentTrack();
/**
* Resumes the current recording track.
*/
void resumeCurrentTrack(); void resumeCurrentTrack();
/** void resumeTrack(Track.Id trackId);
* Ends the current recording track.
*/
void endCurrentTrack(); void endCurrentTrack();
/**
* Returns true if currently recording a track.
*/
boolean isRecording(); boolean isRecording();
/** /**
@@ -88,20 +62,22 @@ public interface TrackRecordingServiceInterface {
//TODO milliseconds? //TODO milliseconds?
long getTotalTime(); long getTotalTime();
/** Marker.Id insertMarker(String name, String category, String description, String photoUrl);
* Inserts a waypoint in the current recording track.
*
* @return the ID of the inserted waypoint
*/
Waypoint.Id insertWaypoint(String name, String category, String description, String photoUrl);
/** /**
* Gets the current sensor data. Returns null if there is no data. * Gets the current sensor data.
* *
* @return SensorDataSet object. * @return SensorDataSet object or null.
*/ */
SensorDataSet getSensorData(); SensorDataSet getSensorData();
/**
* Gets the current elevation gain.
*
* @return elevation gain in m or null.
*/
Float getElevationGain_m();
@VisibleForTesting @VisibleForTesting
void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager); void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager);

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