Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 16c20287ca |
@@ -1,13 +1,5 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,5 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<JetCodeStyleSettings>
|
|
||||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
|
||||||
<value>
|
|
||||||
<package name="java.util" alias="false" withSubpackages="false" />
|
|
||||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
|
||||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
|
||||||
<value>
|
|
||||||
<package name="" alias="false" withSubpackages="true" />
|
|
||||||
<package name="java" alias="false" withSubpackages="true" />
|
|
||||||
<package name="javax" alias="false" withSubpackages="true" />
|
|
||||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
|
||||||
<package name="" alias="true" withSubpackages="true" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
</JetCodeStyleSettings>
|
|
||||||
<MarkdownNavigatorCodeStyleSettings>
|
|
||||||
<option name="RIGHT_MARGIN" value="72" />
|
|
||||||
</MarkdownNavigatorCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
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.
|
||||||
@@ -2,50 +2,34 @@
|
|||||||
|
|
||||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
||||||
|
|
||||||
[](https://github.com/humanetech-community/awesome-humane-tech)
|
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Free</th>
|
<th>Free</th>
|
||||||
|
<th>Paid (same as free)</th>
|
||||||
<th>Donations</th>
|
<th>Donations</th>
|
||||||
<th>Translations</th>
|
<th>Translations</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<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">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td align="center">
|
<td>
|
||||||
<a href="https://liberapay.com/OpenTracks/donate">
|
|
||||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
|
||||||
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/svg-badge.svg" alt="Translation status" />
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<a href="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc">
|
|
||||||
<img alt="bitcoin:bc1qckpls3lpjlcpyxzm66hcy65hnesas9zgacy6yc" src="promotion/donations-bitcoin.png" height="90" align="middle">
|
|
||||||
</a>
|
|
||||||
</td align="center">
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td align="center">
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
||||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td>
|
||||||
|
<a href="https://liberapay.com/OpenTracks/donate">
|
||||||
|
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="https://www.transifex.com/opentracks/opentracks/">
|
||||||
|
Transifex
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -55,7 +39,6 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
* __Bluetooth LE sensors:__
|
* __Bluetooth LE sensors:__
|
||||||
* heart rate
|
* heart rate
|
||||||
* cycling: speed and cadence
|
* cycling: speed and cadence
|
||||||
* cycling: power meter
|
|
||||||
* __Export data:__ export tracks either as KMZ (incl. photos), KML or GPX
|
* __Export data:__ export tracks either as KMZ (incl. photos), KML or GPX
|
||||||
* __No Internet access:__ Internet is not used
|
* __No Internet access:__ Internet is not used
|
||||||
* __Voice announcements__
|
* __Voice announcements__
|
||||||
@@ -71,31 +54,25 @@ __Only required permissions:__
|
|||||||
An overview of Bluetooth LE sensors that are known to work with OpenTracks is in [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md).
|
An overview of Bluetooth LE sensors that are known to work with OpenTracks is in [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md).
|
||||||
|
|
||||||
_Please note:_
|
_Please note:_
|
||||||
_OpenTracks_ does not contain a _map_ (as this would require internet access).
|
_OpenTracks_ does not contain a _map_ (as this would required Internet access).
|
||||||
|
|
||||||
## Custom Dashboards (incl. Map)
|
## Custom Dashboards (incl. Map)
|
||||||
As of v3.3.1 OpenTracks enables to use custom dashboards.
|
As of v3.3.1 OpenTracks enables to use custom dashboards.
|
||||||
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
|
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)).
|
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)).
|
||||||
However, this is rather slow and does not provide updates while recording.
|
However, this is rather slow and does not provide updates while recording.
|
||||||
|
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
<div>
|
<div>
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_1.png">
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_1.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_2.png">
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_2.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_3.png">
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_3.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_4.png">
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_4.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_5.png">
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_5.png">
|
||||||
</div>
|
<img width="15%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_6.png">
|
||||||
<div>
|
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_6.png">
|
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_7.png">
|
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_8.png">
|
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_9.png">
|
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_10.png">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Project history
|
## Project history
|
||||||
|
|||||||
@@ -34,4 +34,4 @@ Requirement: `gem install fastlane`
|
|||||||
|
|
||||||
## Translations (localization)
|
## Translations (localization)
|
||||||
|
|
||||||
Translations of the OpenTracks are handled in Weblate: [https://weblate.bubu1.eu/projects/OpenTracks/](https://weblate.bubu1.eu/projects/OpenTracks/).
|
Translations of the OpenTracks are handled in Transifex: [https://www.transifex.com/opentracks](https://www.transifex.com/opentracks/).
|
||||||
@@ -1,42 +1,20 @@
|
|||||||
# Overview of tested Bluetooth LE sensors
|
# Overview of tested Bluetooth LE sensors
|
||||||
|
|
||||||
## HRS: Heartrate Service
|
## HRS: Heartrate Service
|
||||||
|
|
||||||
* Decathlon|Geonaute|Kalenji Dual HRM Belt
|
* Decathlon|Geonaute|Kalenji Dual HRM Belt
|
||||||
* moofit heart rate monitor
|
|
||||||
* Polar H7
|
* Polar H7
|
||||||
* Polar H10
|
|
||||||
* Polar OH1
|
* Polar OH1
|
||||||
Has updatable firmware that requires a user account at polar.com
|
Has updatable firmware that requires a user account at polar.com
|
||||||
* Wahoo Tickr (Model: WFBTHR02)
|
* Wahoo Tickr (Model: WFBTHR02)
|
||||||
|
|
||||||
## CSCP: Cycling Cadence and Speed Service
|
## CSCP: Cycling Cadence and Speed Service
|
||||||
|
|
||||||
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
|
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
|
||||||
However, often only one value is provided.
|
However, often only one value is provided.
|
||||||
|
|
||||||
### Speed
|
### Speed
|
||||||
|
|
||||||
* Garmin Speed Sensor 2
|
|
||||||
* Wahoo Speed (Model: WFRPMSPD)
|
* Wahoo Speed (Model: WFRPMSPD)
|
||||||
|
|
||||||
### Cadence
|
### Cadence
|
||||||
|
|
||||||
* Garmin Cadence Sensor 2
|
|
||||||
* Wahoo Cadence (Model: WFPODCAD2)
|
* Wahoo Cadence (Model: WFPODCAD2)
|
||||||
This sensor reports cadence data as speed.
|
This sensor reports cadence data as speed.
|
||||||
A workaround is in place.
|
A workaround is in place.
|
||||||
|
|
||||||
## Cycling Power Measurement
|
|
||||||
|
|
||||||
We only support _Instantaneous Power_.
|
|
||||||
|
|
||||||
_WARNING_: we only support physically movements with valid GPS coordinates.
|
|
||||||
Using a device indoors (no GPS available) will only show the current power values, but won't store anything.
|
|
||||||
Also the distance is not computed.
|
|
||||||
|
|
||||||
* Assioma Duo Powermeter
|
|
||||||
* Elite drivo II
|
|
||||||
* QUARQ Red DZero Powermeter
|
|
||||||
* Tacx Satori Smart
|
|
||||||
* Wahoo Kickr v4.0
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,14 +45,10 @@ android {
|
|||||||
|
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding = true
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 3740
|
versionCode 3544
|
||||||
versionName "v3.13.2"
|
versionName "v3.9.3"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -86,17 +82,17 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||||
|
|
||||||
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.1'
|
implementation 'com.google.android.material:material:1.2.0'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
|
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.6.0'
|
androidTestImplementation 'org.mockito:mockito-android:3.4.6'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,15 @@ endlegend
|
|||||||
|
|
||||||
[*] --> TrackListActivity
|
[*] --> TrackListActivity
|
||||||
TrackListActivity --> TrackRecordingActivity : Start recording
|
TrackListActivity --> TrackRecordingActivity : Start recording
|
||||||
TrackListActivity --> TrackRecordedActivity : Show track
|
TrackListActivity --> SearchListActivity
|
||||||
TrackListActivity --> MarkerListActivity : Show all markers
|
TrackListActivity --> MarkerListActivity : Show all markers
|
||||||
TrackListActivity --> AggregatedStatisticsActivity : Aggregated stats
|
TrackListActivity --> AggregatedStatisticsActivity : Aggregated stats
|
||||||
|
TrackListActivity --> TrackRecordedActivity : Show track
|
||||||
TrackListActivity --> TrackDeleteActivity
|
TrackListActivity --> TrackDeleteActivity
|
||||||
|
|
||||||
|
SearchListActivity --> MarkerDetailActivity : Search result
|
||||||
|
SearchListActivity --> TrackRecordedActivity : Search result
|
||||||
|
|
||||||
TrackRecordedActivity --> TrackDeleteActivity
|
TrackRecordedActivity --> TrackDeleteActivity
|
||||||
TrackRecordedActivity --> TrackEditActivity
|
TrackRecordedActivity --> TrackEditActivity
|
||||||
TrackRecordedActivity --> MarkerListActivity
|
TrackRecordedActivity --> MarkerListActivity
|
||||||
@@ -42,14 +46,10 @@ MarkerDetailActivity --> CoordinateOnMapIntent : URI
|
|||||||
|
|
||||||
SettingsActivity --> AboutActivity
|
SettingsActivity --> AboutActivity
|
||||||
SettingsActivity --> HelpActivity
|
SettingsActivity --> HelpActivity
|
||||||
SettingsActivity --> ImportActivity
|
|
||||||
SettingsActivity --> ExportActivity
|
SettingsActivity --> ExportActivity
|
||||||
SettingsActivity --> DirectoryChooserActivity.ImportDirectoryChooserActivity
|
SettingsActivity --> DirectoryChooserActivity
|
||||||
SettingsActivity --> DirectoryChooserActivity.ExportDirectoryChooserActivity
|
SettingsActivity --> DirectoryChooserActivity
|
||||||
SettingsActivity --> DirectoryChooserActivity.DefaultTrackExportDirectoryChooserActivity
|
DirectoryChooserActivity$ImportDirectoryChooserActivity --> ImportActivity
|
||||||
DirectoryChooserActivity.ImportDirectoryChooserActivity --> ImportActivity
|
DirectoryChooserActivity$ExportDirectoryChooserActivity --> ExportActivity
|
||||||
DirectoryChooserActivity.ExportDirectoryChooserActivity --> ExportActivity
|
|
||||||
|
|
||||||
FileImportIntent --> ImportActivity
|
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ LocationManager --> TrackRecordingService
|
|||||||
ElevationSumManager --> TrackRecordingService
|
ElevationSumManager --> TrackRecordingService
|
||||||
|
|
||||||
TrackRecordingService --> TrackRecordingServiceConnection
|
TrackRecordingService --> TrackRecordingServiceConnection
|
||||||
TrackRecordingService --> CustomContentProvider
|
TrackRecordingService --> Database
|
||||||
CustomContentProvider --> Database
|
|
||||||
|
|
||||||
Database --> CustomContentProvider
|
Database --> CustomContentProvider
|
||||||
CustomContentProvider --> TrackDataHub
|
CustomContentProvider --> TrackDataHub
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class Track {
|
|||||||
-icon
|
-icon
|
||||||
}
|
}
|
||||||
Track o-- TrackPoint
|
Track o-- TrackPoint
|
||||||
Track o-- Marker
|
Track o-- Waypoint
|
||||||
Track "1" - "1" TripStatistics
|
Track "1" - "1" TripStatistics
|
||||||
|
|
||||||
class TripStatistics {
|
class TripStatistics {
|
||||||
@@ -21,12 +21,7 @@ class TripStatistics {
|
|||||||
- totalElevationGain
|
- totalElevationGain
|
||||||
}
|
}
|
||||||
|
|
||||||
note left of TripStatistics
|
class TrackPoint extends android.location.Location {
|
||||||
The time is based upon device time.
|
|
||||||
end note
|
|
||||||
|
|
||||||
|
|
||||||
class TrackPoint {
|
|
||||||
- id
|
- id
|
||||||
- trackId
|
- trackId
|
||||||
- longitude
|
- longitude
|
||||||
@@ -41,32 +36,7 @@ class TrackPoint {
|
|||||||
- sensor_power
|
- sensor_power
|
||||||
}
|
}
|
||||||
|
|
||||||
note right of TrackPoint
|
class Waypoint {
|
||||||
TODO
|
|
||||||
TrackPoints are mixing two concepts:
|
|
||||||
1. a GPS measurement provided by the GPS hardware
|
|
||||||
2. and also segment markers.
|
|
||||||
These are marked by PAUSE / RESUME TrackPoints.
|
|
||||||
|
|
||||||
While exporting PAUSE/RESUME are not used to create these segments and these are partly recreated when imported.
|
|
||||||
However, the timestamp of PAUSE/RESUME is not exported and can therefore not be restored.
|
|
||||||
This prevents us from restoring the exact TripStatistics.
|
|
||||||
It is also noteworthy that neither PAUSE/RESUME TrackPoints are used for computing the TrackStatistics.
|
|
||||||
Only the timestamp to _create_ those is used.
|
|
||||||
end note
|
|
||||||
|
|
||||||
note left of TrackPoint
|
|
||||||
TODO
|
|
||||||
The time stored in TrackPoints is either:
|
|
||||||
1. created via GPS: provided by the GPS hardware
|
|
||||||
2. created by the user (pause/resume): device time.
|
|
||||||
|
|
||||||
We know that the GPS time and device time are often not in sync.
|
|
||||||
end note
|
|
||||||
|
|
||||||
TrackPoint "1" - "1" android.location.Location
|
|
||||||
|
|
||||||
class Marker {
|
|
||||||
- id
|
- id
|
||||||
- trackId
|
- trackId
|
||||||
- name
|
- name
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ Funktionen & Features:
|
|||||||
* zeichnet Sport und andere Outdooraktivitäten auf
|
* zeichnet Sport und andere Outdooraktivitäten auf
|
||||||
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
|
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
|
||||||
* zeichnet deine Herzfrequenz auf (Bluetooth LE)
|
* zeichnet deine Herzfrequenz auf (Bluetooth LE)
|
||||||
* nutze beim Radfahren deine Trittfrequenz- und Geschwindigkeitssensoren sowie Leistungsmesser (Bluetooth LE)
|
* nutze beim Radfahren deine Trittfrequenz- und Geschwindigkeitssensoren (Bluetooth LE)
|
||||||
* exportiert deine Daten als KML oder GPX
|
* exportiert deine Daten als KML oder GPX
|
||||||
* funktioniert ganz ohne Internet
|
* funktioniert ganz ohne Internet
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
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)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
v3.11.1: OpenTracks
|
|
||||||
|
|
||||||
Bugfix:
|
|
||||||
- Voice announcements reported wrong statistics
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
v3.12.0: OpenTracks
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
- Bluetooth sensor addresses
|
|
||||||
- Intervals: show elevation gain
|
|
||||||
- Export: default export directory
|
|
||||||
- Export: automatic export after finishing a track
|
|
||||||
- Manual import/export: new user interface
|
|
||||||
- Manual export: option to overwrite on manual export
|
|
||||||
|
|
||||||
Faster:
|
|
||||||
- Track lists loading
|
|
||||||
- Deleting multiple tracks
|
|
||||||
|
|
||||||
Bugfix:
|
|
||||||
- Fixed intent filters for import
|
|
||||||
- KMZ could not be parsed
|
|
||||||
- Export: fix crash on Android 11
|
|
||||||
|
|
||||||
Developer:
|
|
||||||
- Use ViewBindings
|
|
||||||
- Database enforces foreign key and uses cascading deletes
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
v3.13.0: OpenTracks
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
- Sensor: support for powermeter (BLE)
|
|
||||||
- Search by track and marker (in lists)
|
|
||||||
- Combined search removed
|
|
||||||
|
|
||||||
Bugfix:
|
|
||||||
- Import crashed in UI
|
|
||||||
- Images of markers were not imported
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
v3.13.1: OpenTracks
|
|
||||||
|
|
||||||
Bugfix:
|
|
||||||
- Crash on record start: minify/shrink removed a drawable
|
|
||||||
- Automatic export failed silently (mainly remote destinations)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
v3.13.2: OpenTracks
|
|
||||||
|
|
||||||
Bugfix:
|
|
||||||
- Crash in landscape mode
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
(versionName): OpenTracks
|
(tbd): OpenTracks
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
-
|
-
|
||||||
|
|||||||
@@ -6,6 +6,5 @@ Features:
|
|||||||
* Supports Bluetooth LE sensors:
|
* Supports Bluetooth LE sensors:
|
||||||
* heart rate
|
* heart rate
|
||||||
* cycling: speed and cadence
|
* cycling: speed and cadence
|
||||||
* cycling: power meter
|
|
||||||
* Export data: export tracks either as KML or GPX
|
* Export data: export tracks either as KML or GPX
|
||||||
* No Internet access: Internet is not used
|
* No Internet access: Internet is not used
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 159 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 144 KiB |
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ esac
|
|||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
@@ -129,6 +130,7 @@ fi
|
|||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%*
|
|||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -19,16 +19,13 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -63,16 +60,16 @@ public class ChartValueSeriesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInitialConditions() {
|
public void testInitialConditions() {
|
||||||
assertEquals(1, series.getInterval());
|
Assert.assertEquals(1, series.getInterval());
|
||||||
assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(5, series.getMaxMarkerValue());
|
Assert.assertEquals(5, series.getMaxMarkerValue());
|
||||||
assertTrue(series.isEnabled());
|
Assert.assertTrue(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEnabled() {
|
public void testEnabled() {
|
||||||
series.setEnabled(false);
|
series.setEnabled(false);
|
||||||
assertFalse(series.isEnabled());
|
Assert.assertFalse(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,9 +77,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();
|
||||||
assertEquals(100, series.getInterval());
|
Assert.assertEquals(100, series.getInterval());
|
||||||
assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(500, series.getMaxMarkerValue());
|
Assert.assertEquals(500, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -90,9 +87,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();
|
||||||
assertEquals(1000, series.getInterval());
|
Assert.assertEquals(1000, series.getInterval());
|
||||||
assertEquals(0, series.getMinMarkerValue());
|
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||||
assertEquals(5000, series.getMaxMarkerValue());
|
Assert.assertEquals(5000, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -100,8 +97,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();
|
||||||
assertEquals(100, series.getInterval());
|
Assert.assertEquals(100, series.getInterval());
|
||||||
assertEquals(200, series.getMinMarkerValue());
|
Assert.assertEquals(200, series.getMinMarkerValue());
|
||||||
assertEquals(700, series.getMaxMarkerValue());
|
Assert.assertEquals(700, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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;
|
||||||
@@ -30,8 +31,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -86,7 +85,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>";
|
||||||
|
|
||||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -120,7 +119,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>";
|
||||||
|
|
||||||
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -131,7 +130,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>");
|
||||||
assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
Assert.assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,7 +140,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>");
|
||||||
assertEquals("Total time: 00:01<br>", builder.toString());
|
Assert.assertEquals("Total time: 00:01<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,7 +150,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");
|
||||||
assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
Assert.assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +160,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>");
|
||||||
assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
Assert.assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,6 +170,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");
|
||||||
assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
Assert.assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
import androidx.test.rule.provider.ProviderTestRule;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||||
|
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||||
|
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.CustomContentProvider;
|
||||||
|
import de.dennisguse.opentracks.content.provider.CustomSQLiteOpenHelper;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SearchEngine}.
|
||||||
|
* These are not meant to be quality tests, but instead feature-by-feature tests.
|
||||||
|
* In other words, they don't test the mixing of different score boostings, just each boosting separately.
|
||||||
|
*
|
||||||
|
* @author Rodrigo Damazio
|
||||||
|
*/
|
||||||
|
public class SearchEngineTest {
|
||||||
|
|
||||||
|
private static final String TAG = SearchEngineTest.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final Location HERE = new Location("gps");
|
||||||
|
private static final long NOW = SearchEngine.OLDEST_ALLOWED_TIMESTAMP + 1000;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ProviderTestRule sqliteContentProviderRule = new ProviderTestRule.Builder(CustomContentProvider.class, ContentProviderUtils.AUTHORITY_PACKAGE).setPrefix(TAG).build();
|
||||||
|
|
||||||
|
private ContentProviderUtils providerUtils;
|
||||||
|
private SearchEngine engine;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
providerUtils = new ContentProviderUtils(sqliteContentProviderRule.getResolver());
|
||||||
|
|
||||||
|
engine = new SearchEngine(providerUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase(TAG + CustomSQLiteOpenHelper.DATABASE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track.Id insertTrack(String title, String description, String category, long hoursAgo) {
|
||||||
|
Track track = new Track();
|
||||||
|
track.setName(title);
|
||||||
|
track.setDescription(description);
|
||||||
|
track.setCategory(category);
|
||||||
|
|
||||||
|
TrackStatistics stats = track.getTrackStatistics();
|
||||||
|
if (hoursAgo > 0) {
|
||||||
|
// Started twice hoursAgo, so the average time is hoursAgo.
|
||||||
|
stats.setStartTime_ms(NOW - hoursAgo * 1000L * 60L * 60L * 2);
|
||||||
|
stats.setStopTime_ms(NOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uri = providerUtils.insertTrack(track);
|
||||||
|
return new Track.Id(ContentUris.parseId(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track.Id insertTrack(String title, String description, String category) {
|
||||||
|
return insertTrack(title, description, category, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Track.Id insertTrack(String title, long hoursAgo) {
|
||||||
|
return insertTrack(title, "", "", hoursAgo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waypoint.Id insertWaypoint(String title, String description, String category, double distance, long hoursAgo, Track.Id trackId) {
|
||||||
|
Location location = new Location(HERE);
|
||||||
|
location.setLatitude(location.getLatitude() + distance);
|
||||||
|
location.setLongitude(location.getLongitude() + distance);
|
||||||
|
if (hoursAgo >= 0) {
|
||||||
|
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
|
||||||
|
}
|
||||||
|
Waypoint waypoint = new Waypoint(location);
|
||||||
|
waypoint.setName(title);
|
||||||
|
waypoint.setDescription(description);
|
||||||
|
waypoint.setCategory(category);
|
||||||
|
waypoint.setTrackId(trackId);
|
||||||
|
|
||||||
|
Uri uri = providerUtils.insertWaypoint(waypoint);
|
||||||
|
return new Waypoint.Id(ContentUris.parseId(uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waypoint.Id insertWaypoint(String title, String description, String category) {
|
||||||
|
return insertWaypoint(title, description, category, 0.0, -1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waypoint.Id insertWaypoint(String title, double distance) {
|
||||||
|
return insertWaypoint(title, "", "", distance, -1, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waypoint.Id insertWaypoint(String title, long hoursAgo) {
|
||||||
|
return insertWaypoint(title, "", "", 0.0, hoursAgo, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Waypoint.Id insertWaypoint(String title, long hoursAgo, Track.Id trackId) {
|
||||||
|
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchText() {
|
||||||
|
// Insert 7 tracks (purposefully out of result order):
|
||||||
|
// - one which won't match
|
||||||
|
// - one which will match the description
|
||||||
|
// - one which will match the category
|
||||||
|
// - one which will match the title
|
||||||
|
// - one which will match in title and category
|
||||||
|
// - one which will match in title and description
|
||||||
|
// - one which will match in all fields
|
||||||
|
insertTrack("bb", "cc", "dd");
|
||||||
|
Track.Id descriptionMatchId = insertTrack("bb", "aa", "cc");
|
||||||
|
Track.Id categoryMatchId = insertTrack("bb", "cc", "aa");
|
||||||
|
Track.Id titleMatchId = insertTrack("aa", "bb", "cc");
|
||||||
|
Track.Id titleCategoryMatchId = insertTrack("aa", "bb", "ca");
|
||||||
|
Track.Id titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
|
||||||
|
Track.Id allMatchId = insertTrack("aa", "ba", "ca");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
assertTrackResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWaypointText() {
|
||||||
|
// Insert 7 waypoints (purposefully out of result order):
|
||||||
|
// - one which won't match
|
||||||
|
// - one which will match the description
|
||||||
|
// - one which will match the category
|
||||||
|
// - one which will match the title
|
||||||
|
// - one which will match in title and category
|
||||||
|
// - one which will match in title and description
|
||||||
|
// - one which will match in all fields
|
||||||
|
insertWaypoint("bb", "cc", "dd");
|
||||||
|
Waypoint.Id descriptionMatchId = insertWaypoint("bb", "aa", "cc");
|
||||||
|
Waypoint.Id categoryMatchId = insertWaypoint("bb", "cc", "aa");
|
||||||
|
Waypoint.Id titleMatchId = insertWaypoint("aa", "bb", "cc");
|
||||||
|
Waypoint.Id titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
|
||||||
|
Waypoint.Id titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
|
||||||
|
Waypoint.Id allMatchId = insertWaypoint("aa", "ba", "ca");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
assertWaypointResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchMixedText() {
|
||||||
|
// Insert 5 entries (purposefully out of result order):
|
||||||
|
// - one waypoint which will match by description
|
||||||
|
// - one waypoint which won't match
|
||||||
|
// - one waypoint which will match by title
|
||||||
|
// - one track which won't match
|
||||||
|
// - one track which will match by title
|
||||||
|
Waypoint.Id descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
|
||||||
|
insertWaypoint("bb", "cc", "dd");
|
||||||
|
Waypoint.Id titleWaypointId = insertWaypoint("aa", "bb", "cc");
|
||||||
|
insertTrack("bb", "cc", "dd");
|
||||||
|
Track.Id trackId = insertTrack("aa", "bb", "cc");
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Title > Description > Category.
|
||||||
|
Assert.assertEquals(results.toString(), 3, results.size());
|
||||||
|
assertTrackResult(trackId, results.get(0));
|
||||||
|
assertWaypointResult(titleWaypointId, results.get(1));
|
||||||
|
assertWaypointResult(descriptionWaypointId, results.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWaypointDistance() {
|
||||||
|
// All results match text, but they're at difference distances from the user.
|
||||||
|
Waypoint.Id farFarAwayId = insertWaypoint("aa", 0.3);
|
||||||
|
Waypoint.Id nearId = insertWaypoint("ab", 0.1);
|
||||||
|
Waypoint.Id farId = insertWaypoint("ac", 0.2);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", HERE, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Distance order.
|
||||||
|
assertWaypointResults(results, nearId, farId, farFarAwayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSearchTrackRecent() {
|
||||||
|
// All results match text, but they're were recorded at different times.
|
||||||
|
Track.Id oldestId = insertTrack("aa", 3);
|
||||||
|
Track.Id recentId = insertTrack("ab", 1);
|
||||||
|
Track.Id oldId = insertTrack("ac", 2);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Reverse time order.
|
||||||
|
assertTrackResults(results, recentId, oldId, oldestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWaypointRecent() {
|
||||||
|
// All results match text, but they're were recorded at different times.
|
||||||
|
Waypoint.Id oldestId = insertWaypoint("aa", 2);
|
||||||
|
Waypoint.Id recentId = insertWaypoint("ab", 0);
|
||||||
|
Waypoint.Id oldId = insertWaypoint("ac", 1);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, null, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Reverse time order.
|
||||||
|
assertWaypointResults(results, recentId, oldId, oldestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchCurrentTrack() {
|
||||||
|
// All results match text, but one of them is the current track.
|
||||||
|
Track.Id currentId = insertTrack("ab", 1);
|
||||||
|
Track.Id otherId = insertTrack("aa", 1);
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Current track should be demoted.
|
||||||
|
assertTrackResults(results, otherId, currentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchCurrentTrackWaypoint() {
|
||||||
|
// All results match text, but one of them is in the current track.
|
||||||
|
Waypoint.Id otherId = insertWaypoint("aa", 1, new Track.Id(456));
|
||||||
|
Waypoint.Id currentId = insertWaypoint("ab", 1, new Track.Id(123));
|
||||||
|
|
||||||
|
SearchQuery query = new SearchQuery("a", null, new Track.Id(123), NOW);
|
||||||
|
ArrayList<ScoredResult> results = new ArrayList<>(engine.search(query));
|
||||||
|
|
||||||
|
// Waypoint in current track should be promoted.
|
||||||
|
assertWaypointResults(results, currentId, otherId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackResult(Track.Id trackId, ScoredResult result) {
|
||||||
|
Assert.assertNotNull("Not a track", result.track);
|
||||||
|
Assert.assertNull("Ambiguous result", result.waypoint);
|
||||||
|
Assert.assertEquals(trackId, result.track.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackResults(List<ScoredResult> results, Track.Id... trackIds) {
|
||||||
|
long[] longTrackIds = new long[trackIds.length];
|
||||||
|
for (int i = 0; i < trackIds.length; i++) {
|
||||||
|
longTrackIds[i] = trackIds[i].getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
String errMsg = "Expected IDs=" + Arrays.toString(longTrackIds) + "; results=" + results;
|
||||||
|
Assert.assertEquals(results.size(), trackIds.length);
|
||||||
|
for (int i = 0; i < results.size(); i++) {
|
||||||
|
ScoredResult result = results.get(i);
|
||||||
|
Assert.assertNotNull(errMsg, result.track);
|
||||||
|
Assert.assertNull(errMsg, result.waypoint);
|
||||||
|
Assert.assertEquals(errMsg, trackIds[i], result.track.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertWaypointResult(Waypoint.Id waypointId, ScoredResult result) {
|
||||||
|
Assert.assertNotNull("Not a waypoint", result.waypoint);
|
||||||
|
Assert.assertNull("Ambiguous result", result.track);
|
||||||
|
Assert.assertEquals(waypointId, result.waypoint.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertWaypointResults(List<ScoredResult> results, Waypoint.Id... waypointIds) {
|
||||||
|
long[] longWaypointIds = new long[waypointIds.length];
|
||||||
|
for (int i = 0; i < waypointIds.length; i++) {
|
||||||
|
longWaypointIds[i] = waypointIds[i].getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
String errMsg = "Expected IDs=" + Arrays.toString(longWaypointIds) + "; results=" + results;
|
||||||
|
Assert.assertEquals(results.size(), waypointIds.length);
|
||||||
|
for (int i = 0; i < results.size(); i++) {
|
||||||
|
ScoredResult result = results.get(i);
|
||||||
|
Assert.assertNotNull(errMsg, result.waypoint);
|
||||||
|
Assert.assertNull(errMsg, result.track);
|
||||||
|
Assert.assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@ public class TestDataUtil {
|
|||||||
public static final double INITIAL_LATITUDE = 37.0;
|
public static final double INITIAL_LATITUDE = 37.0;
|
||||||
public static final double INITIAL_LONGITUDE = -57.0;
|
public static final double INITIAL_LONGITUDE = -57.0;
|
||||||
public static final double ALTITUDE_INTERVAL = 2.5;
|
public static final double ALTITUDE_INTERVAL = 2.5;
|
||||||
public static final float ELEVATION_GAIN = 3;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a track without any trackPoints.
|
* Create a track without any trackPoints.
|
||||||
@@ -69,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(ELEVATION_GAIN);
|
|
||||||
return trackPoint;
|
return trackPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +93,12 @@ public class TestDataUtil {
|
|||||||
* @param location The location.
|
* @param location The location.
|
||||||
* @return the Waypoint created.
|
* @return the Waypoint created.
|
||||||
*/
|
*/
|
||||||
public static Marker createWaypointWithPhoto(Context context, Track.Id trackId, Location location) throws IOException {
|
public static Waypoint 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 Marker("Waypoint name", "Waypoint description", "Waypoint category", "", trackId, 0.0, 0, location, photoUrl);
|
return new Waypoint("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(MarkerColumns.CONTENT_TYPE, customContentProvider.getType(MarkerColumns.CONTENT_URI));
|
assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||||
assertEquals(MarkerColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(MarkerColumns.CONTENT_URI.buildUpon(), 1).build()));
|
assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ 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;
|
||||||
@@ -38,22 +39,17 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,13 +122,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();
|
||||||
assertNotNull(loc);
|
Assert.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.
|
||||||
assertEquals(descending ? lastPointId - locations.size() + 1
|
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
|
||||||
: lastPointId - numPoints + locations.size(), it.getTrackPointId());
|
: lastPointId - numPoints + locations.size(), it.getTrackPointId());
|
||||||
}
|
}
|
||||||
assertEquals(numPoints, locations.size());
|
Assert.assertEquals(numPoints, locations.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +138,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);
|
||||||
assertNotNull(track);
|
Assert.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) {
|
||||||
@@ -166,8 +162,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(numPoints == 0 || lastPointId > 0);
|
Assert.assertTrue(numPoints == 0 || lastPointId > 0);
|
||||||
assertEquals(numPoints, counter);
|
Assert.assertEquals(numPoints, counter);
|
||||||
|
|
||||||
return lastPointId;
|
return lastPointId;
|
||||||
}
|
}
|
||||||
@@ -199,8 +195,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);
|
||||||
assertEquals(trackId, track.getId());
|
Assert.assertEquals(trackId, track.getId());
|
||||||
assertEquals(name, track.getName());
|
Assert.assertEquals(name, track.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,25 +208,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);
|
||||||
|
|
||||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
contentProviderUtils.insertMarker(waypoint);
|
contentProviderUtils.insertWaypoint(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);
|
||||||
assertEquals(1, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(10, tracksPointsCursor.getCount());
|
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(1, waypointCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(0, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(0, tracksPointsCursor.getCount());
|
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -243,32 +239,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);
|
||||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
contentProviderUtils.insertMarker(waypoint);
|
contentProviderUtils.insertWaypoint(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);
|
||||||
assertEquals(1, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(10, tracksPointsCursor.getCount());
|
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(1, waypointCursor.getCount());
|
Assert.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.
|
||||||
assertTrue(waypoint.hasPhoto());
|
Assert.assertTrue(waypoint.hasPhoto());
|
||||||
File dir = FileUtils.getPhotoDir(context, trackId);
|
File dir = FileUtils.getPhotoDir(context, trackId);
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
assertTrue(dir.exists());
|
Assert.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);
|
||||||
assertEquals(0, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(0, tracksPointsCursor.getCount());
|
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
assertFalse(dir.exists());
|
Assert.assertFalse(dir.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -287,25 +283,26 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
||||||
|
|
||||||
Marker waypoint = new Marker(trackId1, contentProviderUtils.getLastValidTrackPoint(trackId2));
|
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId2));
|
||||||
contentProviderUtils.insertMarker(waypoint);
|
waypoint.setTrackId(trackId1);
|
||||||
|
contentProviderUtils.insertWaypoint(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);
|
||||||
assertEquals(3, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(1, waypointCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(2, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,41 +322,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);
|
||||||
Marker waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId1, trackPoint1.getLocation());
|
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId1, trackPoint1.getLocation());
|
||||||
contentProviderUtils.insertMarker(waypoint1);
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
||||||
|
|
||||||
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
|
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
|
||||||
Marker waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId2, trackPoint2.getLocation());
|
Waypoint waypoint2 = TestDataUtil.createWaypointWithPhoto(context, trackId2, trackPoint2.getLocation());
|
||||||
contentProviderUtils.insertMarker(waypoint2);
|
contentProviderUtils.insertWaypoint(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);
|
||||||
assertEquals(3, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(30, tracksPointsCursor.getCount());
|
Assert.assertEquals(30, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(2, waypointCursor.getCount());
|
Assert.assertEquals(2, waypointCursor.getCount());
|
||||||
assertTrue(waypoint1.hasPhoto());
|
Assert.assertTrue(waypoint1.hasPhoto());
|
||||||
assertTrue(dir1.isDirectory());
|
Assert.assertTrue(dir1.isDirectory());
|
||||||
assertEquals(1, dir1.list().length);
|
Assert.assertEquals(1, dir1.list().length);
|
||||||
assertTrue(dir1.exists());
|
Assert.assertTrue(dir1.exists());
|
||||||
assertTrue(dir2.isDirectory());
|
Assert.assertTrue(dir2.isDirectory());
|
||||||
assertEquals(1, dir2.list().length);
|
Assert.assertEquals(1, dir2.list().length);
|
||||||
assertTrue(dir2.exists());
|
Assert.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);
|
||||||
assertEquals(2, tracksCursor.getCount());
|
Assert.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);
|
||||||
assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
assertEquals(1, waypointCursor.getCount());
|
Assert.assertEquals(1, waypointCursor.getCount());
|
||||||
assertFalse(dir1.exists());
|
Assert.assertFalse(dir1.exists());
|
||||||
assertTrue(dir2.exists());
|
Assert.assertTrue(dir2.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -376,8 +373,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
List<Track> allTracks = contentProviderUtils.getTracks();
|
List<Track> allTracks = contentProviderUtils.getTracks();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(initialTrackNumber + 1, allTracks.size());
|
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
|
||||||
assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -387,7 +384,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));
|
||||||
assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
|
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -400,7 +397,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
|
contentProviderUtils.insertTrack(TestDataUtil.createTrack(trackId));
|
||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
assertNotNull(contentProviderUtils.getTrack(trackId));
|
Assert.assertNotNull(contentProviderUtils.getTrack(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -414,7 +411,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
|
Assert.assertNotNull(contentProviderUtils.getTrack(track.getUuid()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -431,14 +428,14 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
|
Assert.assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
|
||||||
track.setName(nameNew);
|
track.setName(nameNew);
|
||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
|
Assert.assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#createContentValues(Marker)}.
|
* Tests the method {@link ContentProviderUtils#createContentValues(Waypoint)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreateContentValues_waypoint() {
|
public void testCreateContentValues_waypoint() {
|
||||||
@@ -454,7 +451,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.0f);
|
statistics.setTotalElevationGain(50.0);
|
||||||
statistics.setMaxSpeed(60.0);
|
statistics.setMaxSpeed(60.0);
|
||||||
statistics.setMaxElevation(1250.0);
|
statistics.setMaxElevation(1250.0);
|
||||||
statistics.setMinElevation(1200.0);
|
statistics.setMinElevation(1200.0);
|
||||||
@@ -462,30 +459,30 @@ public class CustomContentProviderUtilsTest {
|
|||||||
track.first.setTrackStatistics(statistics);
|
track.first.setTrackStatistics(statistics);
|
||||||
contentProviderUtils.insertTrack(track.first);
|
contentProviderUtils.insertTrack(track.first);
|
||||||
|
|
||||||
Marker waypoint = new Marker(trackId, track.second[0]);
|
Waypoint waypoint = new Waypoint(track.second[0]);
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
contentProviderUtils.insertMarker(waypoint);
|
contentProviderUtils.insertWaypoint(waypoint);
|
||||||
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
||||||
|
|
||||||
Marker.Id waypointId = new Marker.Id(System.currentTimeMillis());
|
Waypoint.Id waypointId = new Waypoint.Id(System.currentTimeMillis());
|
||||||
waypoint.setId(waypointId);
|
waypoint.setId(waypointId);
|
||||||
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
|
ContentValues contentValues = contentProviderUtils.createContentValues(waypoint);
|
||||||
assertEquals(waypointId.getId(), contentValues.get(MarkerColumns._ID));
|
Assert.assertEquals(waypointId.getId(), contentValues.get(WaypointsColumns._ID));
|
||||||
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
Assert.assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(WaypointsColumns.LONGITUDE));
|
||||||
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#createMarker(Cursor)}.
|
* Tests the method {@link ContentProviderUtils#createWaypoint(Cursor)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWaypoint() {
|
public void testCreateWaypoint() {
|
||||||
int startColumnIndex = 1;
|
int startColumnIndex = 1;
|
||||||
int columnIndex = startColumnIndex;
|
int columnIndex = startColumnIndex;
|
||||||
when(cursorMock.getColumnIndexOrThrow(MarkerColumns._ID)).thenReturn(columnIndex++);
|
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)).thenReturn(columnIndex++);
|
||||||
when(cursorMock.getColumnIndexOrThrow(MarkerColumns.NAME)).thenReturn(columnIndex++);
|
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).thenReturn(columnIndex++);
|
||||||
when(cursorMock.getColumnIndexOrThrow(MarkerColumns.TRACKID)).thenReturn(columnIndex++);
|
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).thenReturn(columnIndex++);
|
||||||
columnIndex = startColumnIndex;
|
columnIndex = startColumnIndex;
|
||||||
// Id
|
// Id
|
||||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||||
@@ -504,15 +501,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
long trackId = 11L;
|
long trackId = 11L;
|
||||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||||
|
|
||||||
Marker waypoint = contentProviderUtils.createMarker(cursorMock);
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock);
|
||||||
assertEquals(id, waypoint.getId().getId());
|
Assert.assertEquals(id, waypoint.getId().getId());
|
||||||
assertEquals(name, waypoint.getName());
|
Assert.assertEquals(name, waypoint.getName());
|
||||||
assertEquals(trackId, waypoint.getTrackId().getId());
|
Assert.assertEquals(trackId, waypoint.getTrackId().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method
|
* Tests the method
|
||||||
* {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)}
|
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)}
|
||||||
* when there is only one waypoint in the track.
|
* when there is only one waypoint in the track.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@@ -521,25 +518,26 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
waypoint1.setDescription(TEST_DESC);
|
waypoint1.setDescription(TEST_DESC);
|
||||||
contentProviderUtils.insertMarker(waypoint1);
|
waypoint1.setTrackId(trackId);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
|
|
||||||
// Check insert was done.
|
// Check insert was done.
|
||||||
assertEquals(contentProviderUtils.getMarkerCount(trackId), 1);
|
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
|
||||||
|
|
||||||
// Get waypoint id that needs to delete.
|
// Get waypoint id that needs to delete.
|
||||||
Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
|
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
contentProviderUtils.deleteMarker(context, waypoint1Id);
|
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
|
||||||
|
|
||||||
assertNull(contentProviderUtils.getMarker(waypoint1Id));
|
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method
|
* Tests the method
|
||||||
* {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)}
|
* {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)}
|
||||||
* when there is only one waypoint in the track.
|
* when there is only one waypoint in the track.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@@ -549,32 +547,32 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
Waypoint waypoint1 = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
contentProviderUtils.insertMarker(waypoint1);
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
|
|
||||||
// Check insert was done.
|
// Check insert was done.
|
||||||
assertEquals(contentProviderUtils.getMarkerCount(trackId), 1);
|
Assert.assertEquals(contentProviderUtils.getWaypointCount(trackId), 1);
|
||||||
|
|
||||||
// Get waypoint id that needs to delete.
|
// Get waypoint id that needs to delete.
|
||||||
Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
|
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
|
||||||
|
|
||||||
// Check waypoint has photo and it's in the external storage.
|
// Check waypoint has photo and it's in the external storage.
|
||||||
assertTrue(waypoint1.hasPhoto());
|
Assert.assertTrue(waypoint1.hasPhoto());
|
||||||
File dir = FileUtils.getPhotoDir(context, trackId);
|
File dir = FileUtils.getPhotoDir(context, trackId);
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
contentProviderUtils.deleteMarker(context, waypoint1Id);
|
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
|
||||||
|
|
||||||
// Check waypoint doesn't exists and photo folder was deleted.
|
// Check waypoint doesn't exists and photo folder was deleted.
|
||||||
assertNull(contentProviderUtils.getMarker(waypoint1Id));
|
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
assertFalse(dir.exists());
|
Assert.assertFalse(dir.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#deleteMarker(Context, Marker.Id)} when there is more than one waypoint in the track.
|
* Tests the method {@link ContentProviderUtils#deleteWaypoint(Context, Waypoint.Id)} when there is more than one waypoint in the track.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteWaypoint_hasNextWayPoint() {
|
public void testDeleteWaypoint_hasNextWayPoint() {
|
||||||
@@ -598,60 +596,67 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
waypoint1.setDescription(MOCK_DESC);
|
waypoint1.setDescription(MOCK_DESC);
|
||||||
Marker.Id waypoint1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint1)));
|
waypoint1.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypoint1Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1)));
|
||||||
|
|
||||||
Marker waypoint2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
waypoint2.setDescription(MOCK_DESC);
|
waypoint2.setDescription(MOCK_DESC);
|
||||||
Marker.Id waypoint2Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint2)));
|
waypoint2.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypoint2Id = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2)));
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
assertNotNull(contentProviderUtils.getMarker(waypoint1Id));
|
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
contentProviderUtils.deleteMarker(context, waypoint1Id);
|
contentProviderUtils.deleteWaypoint(context, waypoint1Id);
|
||||||
assertNull(contentProviderUtils.getMarker(waypoint1Id));
|
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
|
|
||||||
assertEquals(MOCK_DESC, contentProviderUtils.getMarker(waypoint2Id).getDescription());
|
Assert.assertEquals(MOCK_DESC, contentProviderUtils.getWaypoint(waypoint2Id).getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#getNextMarkerNumber(Track.Id)}.
|
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(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);
|
||||||
|
|
||||||
Marker waypoint1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint1 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
Marker waypoint2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
waypoint1.setTrackId(trackId);
|
||||||
Marker waypoint3 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint2 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
Marker waypoint4 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
waypoint2.setTrackId(trackId);
|
||||||
contentProviderUtils.insertMarker(waypoint1);
|
Waypoint waypoint3 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
contentProviderUtils.insertMarker(waypoint2);
|
waypoint3.setTrackId(trackId);
|
||||||
contentProviderUtils.insertMarker(waypoint3);
|
Waypoint waypoint4 = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
contentProviderUtils.insertMarker(waypoint4);
|
waypoint4.setTrackId(trackId);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint2);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint3);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint4);
|
||||||
|
|
||||||
assertEquals(4, contentProviderUtils.getNextMarkerNumber(trackId));
|
Assert.assertEquals(4, contentProviderUtils.getNextWaypointNumber(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#insertMarker(Marker)} and
|
* Tests the method {@link ContentProviderUtils#insertWaypoint(Waypoint)} and
|
||||||
* {@link ContentProviderUtils#getMarker(Marker.Id)}.
|
* {@link ContentProviderUtils#getWaypoint(Waypoint.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);
|
||||||
|
|
||||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
waypoint.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
|
||||||
|
|
||||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(waypointId).getDescription());
|
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
|
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWaypoint() {
|
public void testUpdateWaypoint() {
|
||||||
@@ -659,20 +664,21 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Marker waypoint = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
Waypoint waypoint = new Waypoint(contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
waypoint.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
waypoint = contentProviderUtils.getMarker(waypointId);
|
waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
waypoint.setDescription(TEST_DESC_NEW);
|
waypoint.setDescription(TEST_DESC_NEW);
|
||||||
contentProviderUtils.updateMarker(context, waypoint);
|
contentProviderUtils.updateWaypoint(context, waypoint);
|
||||||
|
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
|
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
|
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWaypoint_withPhoto() throws IOException {
|
public void testUpdateWaypoint_withPhoto() throws IOException {
|
||||||
@@ -683,31 +689,32 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
waypoint.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
|
||||||
|
|
||||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
waypoint = contentProviderUtils.getMarker(waypointId);
|
waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
waypoint.setName(TEST_NAME_NEW);
|
waypoint.setName(TEST_NAME_NEW);
|
||||||
waypoint.setDescription(TEST_DESC_NEW);
|
waypoint.setDescription(TEST_DESC_NEW);
|
||||||
contentProviderUtils.updateMarker(context, waypoint);
|
contentProviderUtils.updateWaypoint(context, waypoint);
|
||||||
|
|
||||||
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(waypointId).getName());
|
Assert.assertEquals(TEST_NAME_NEW, contentProviderUtils.getWaypoint(waypointId).getName());
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
|
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||||
assertTrue(waypoint.hasPhoto());
|
Assert.assertTrue(waypoint.hasPhoto());
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
|
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWaypoint_delPhotoAndDir() throws IOException {
|
public void testUpdateWaypoint_delPhotoAndDir() throws IOException {
|
||||||
@@ -718,30 +725,31 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
waypoint.setTrackId(trackId);
|
||||||
|
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
|
||||||
|
|
||||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
waypoint = contentProviderUtils.getMarker(waypointId);
|
waypoint = contentProviderUtils.getWaypoint(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.updateMarker(context, waypoint);
|
contentProviderUtils.updateWaypoint(context, waypoint);
|
||||||
|
|
||||||
assertEquals(TEST_NAME_NEW, contentProviderUtils.getMarker(waypointId).getName());
|
Assert.assertEquals(TEST_NAME_NEW, contentProviderUtils.getWaypoint(waypointId).getName());
|
||||||
assertEquals(TEST_DESC_NEW, contentProviderUtils.getMarker(waypointId).getDescription());
|
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||||
assertFalse(waypoint.hasPhoto());
|
Assert.assertFalse(waypoint.hasPhoto());
|
||||||
assertFalse(dir.exists());
|
Assert.assertFalse(dir.exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#updateMarker(Context, Marker)}.
|
* Tests the method {@link ContentProviderUtils#updateWaypoint(Context, Waypoint)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWaypoint_delPhotoNotDir() throws IOException {
|
public void testUpdateWaypoint_delPhotoNotDir() throws IOException {
|
||||||
@@ -752,28 +760,30 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert two waypoints with photos.
|
// Insert two waypoints with photos.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
Waypoint waypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
Marker otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
waypoint.setTrackId(trackId);
|
||||||
|
Waypoint otherWaypoint = TestDataUtil.createWaypointWithPhoto(context, trackId, trackPoint.getLocation());
|
||||||
otherWaypoint.setDescription(TEST_DESC);
|
otherWaypoint.setDescription(TEST_DESC);
|
||||||
Marker.Id waypointId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(waypoint)));
|
otherWaypoint.setTrackId(trackId);
|
||||||
contentProviderUtils.insertMarker(otherWaypoint);
|
Waypoint.Id waypointId = new Waypoint.Id(ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint)));
|
||||||
|
contentProviderUtils.insertWaypoint(otherWaypoint);
|
||||||
|
|
||||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.getId());
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(2, dir.list().length);
|
Assert.assertEquals(2, dir.list().length);
|
||||||
|
|
||||||
// Update one waypoint deleting photo.
|
// Update one waypoint deleting photo.
|
||||||
waypoint = contentProviderUtils.getMarker(waypointId);
|
waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
waypoint.setPhotoUrl(null);
|
waypoint.setPhotoUrl(null);
|
||||||
contentProviderUtils.updateMarker(context, waypoint);
|
contentProviderUtils.updateWaypoint(context, waypoint);
|
||||||
|
|
||||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(waypointId).getDescription());
|
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||||
assertFalse(waypoint.hasPhoto());
|
Assert.assertFalse(waypoint.hasPhoto());
|
||||||
assertTrue(dir.exists());
|
Assert.assertTrue(dir.exists());
|
||||||
assertTrue(dir.isDirectory());
|
Assert.assertTrue(dir.isDirectory());
|
||||||
assertEquals(1, dir.list().length);
|
Assert.assertEquals(1, dir.list().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -788,9 +798,9 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
|
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
|
||||||
assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
Assert.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);
|
||||||
assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
Assert.assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -829,11 +839,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
|
TrackPoint trackPoint = contentProviderUtils.createTrackPoint(cursorMock);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(longitude, trackPoint.getLongitude(), 0.01);
|
Assert.assertEquals(longitude, trackPoint.getLongitude(), 0.01);
|
||||||
assertEquals(latitude, trackPoint.getLatitude(), 0.01);
|
Assert.assertEquals(latitude, trackPoint.getLatitude(), 0.01);
|
||||||
assertEquals(time, trackPoint.getTime(), 0.01);
|
Assert.assertEquals(time, trackPoint.getTime(), 0.01);
|
||||||
assertEquals(speed, trackPoint.getSpeed(), 0.01);
|
Assert.assertEquals(speed, trackPoint.getSpeed(), 0.01);
|
||||||
assertFalse(trackPoint.hasHeartRate());
|
Assert.assertFalse(trackPoint.hasHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -847,7 +857,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);
|
||||||
assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
|
Assert.assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -866,10 +876,10 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
Assert.assertTrue(trackPoints.get(10).hasHeartRate());
|
||||||
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
Assert.assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
||||||
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
Assert.assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
||||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
|
Assert.assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -904,7 +914,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
|
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, cursor.getCount());
|
Assert.assertEquals(2, cursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -926,7 +936,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
|
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, cursor.getCount());
|
Assert.assertEquals(2, cursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -950,12 +960,12 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
for (int i = 0; i < trackpointIds.length; i++) {
|
||||||
assertTrue(trackPointIterator.hasNext());
|
Assert.assertTrue(trackPointIterator.hasNext());
|
||||||
TrackPoint trackPoint = trackPointIterator.next();
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId());
|
Assert.assertEquals(startTrackPointId - i, trackPointIterator.getTrackPointId());
|
||||||
checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation());
|
checkLocation((trackpointIds.length - 1) - i, trackPoint.getLocation());
|
||||||
}
|
}
|
||||||
assertFalse(trackPointIterator.hasNext());
|
Assert.assertFalse(trackPointIterator.hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -980,13 +990,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
for (int i = 0; i < trackpointIds.length; i++) {
|
||||||
assertTrue(trackPointIterator.hasNext());
|
Assert.assertTrue(trackPointIterator.hasNext());
|
||||||
TrackPoint trackPoint = trackPointIterator.next();
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
assertEquals(startTrackPointId + i, trackPointIterator.getTrackPointId());
|
Assert.assertEquals(startTrackPointId + i, trackPointIterator.getTrackPointId());
|
||||||
|
|
||||||
checkLocation(i, trackPoint.getLocation());
|
checkLocation(i, trackPoint.getLocation());
|
||||||
}
|
}
|
||||||
assertFalse(trackPointIterator.hasNext());
|
Assert.assertFalse(trackPointIterator.hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -996,16 +1006,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) {
|
||||||
assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
Assert.assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
||||||
assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
Assert.assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
||||||
assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
|
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
|
||||||
assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
Assert.assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatIdListForUri() {
|
public void testFormatIdListForUri() {
|
||||||
assertEquals("", ContentProviderUtils.formatIdListForUri());
|
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri());
|
||||||
assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
|
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
|
||||||
assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
|
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteConstraintException;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
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;
|
||||||
|
|
||||||
@@ -17,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;
|
||||||
@@ -37,21 +38,39 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the SQL create statements for all SQLite elements of type (ordered by name).
|
* Get the SQL create statements for all tables (ordered by name).
|
||||||
*
|
*
|
||||||
* @param type index, table
|
* @return Map(TableName, SQL)
|
||||||
* @return Map(name, SQL)
|
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> getSQL(SQLiteDatabase db, String type) {
|
@VisibleForTesting
|
||||||
HashMap<String, String> sqlMap = new HashMap<>();
|
public static Map<String, String> getTableSQL(SQLiteDatabase db) {
|
||||||
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "type=?", new String[]{type}, null, null, "name")) {
|
HashMap<String, String> tableSQL = new HashMap<>();
|
||||||
|
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + WaypointsColumns.TABLE_NAME + "')", null, null, null, "name")) {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
sqlMap.put(cursor.getString(0), cursor.getString(1));
|
tableSQL.put(cursor.getString(0), cursor.getString(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sqlMap;
|
return tableSQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the SQL create statements for all indices (ordered by name).
|
||||||
|
*
|
||||||
|
* @return Map(tableName, SQL)
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public static Map<String, String> getIndexSQL(SQLiteDatabase db) {
|
||||||
|
HashMap<String, String> indexSQL = new HashMap<>();
|
||||||
|
try (Cursor cursor = db.rawQuery("SELECT tbl_name, SQL FROM sqlite_master WHERE type = 'index'", null)) {
|
||||||
|
if (cursor != null) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
indexSQL.put(cursor.getString(0), cursor.getString(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return indexSQL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,10 +106,10 @@ 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, MarkerColumns.CREATE_TABLE));
|
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE));
|
||||||
assertTrue(hasSqlCreate(db, MarkerColumns.CREATE_TABLE_INDEX));
|
assertTrue(hasSqlCreate(db, WaypointsColumns.CREATE_TABLE_INDEX));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
fail("Database could not be created: " + e);
|
fail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +121,8 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
Map<String, String> tableByUpgrade;
|
Map<String, String> tableByUpgrade;
|
||||||
Map<String, String> indicesByUpgrade;
|
Map<String, String> indicesByUpgrade;
|
||||||
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
try (SQLiteDatabase dbUpgraded = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
||||||
tableByUpgrade = getSQL(dbUpgraded, "table");
|
tableByUpgrade = getTableSQL(dbUpgraded);
|
||||||
indicesByUpgrade = getSQL(dbUpgraded, "index");
|
indicesByUpgrade = getIndexSQL(dbUpgraded);
|
||||||
}
|
}
|
||||||
context.deleteDatabase(DATABASE_NAME);
|
context.deleteDatabase(DATABASE_NAME);
|
||||||
|
|
||||||
@@ -111,25 +130,23 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
Map<String, String> tablesByCreate;
|
Map<String, String> tablesByCreate;
|
||||||
Map<String, String> indicesByCreate;
|
Map<String, String> indicesByCreate;
|
||||||
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
|
||||||
tablesByCreate = getSQL(dbCreated, "table");
|
tablesByCreate = getTableSQL(dbCreated);
|
||||||
indicesByCreate = getSQL(dbCreated, "index");
|
indicesByCreate = getIndexSQL(dbCreated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// then - verify table structure
|
// then - verify table structure
|
||||||
int tableCount = 3 + 2; //Three with data tables + two SQLite
|
assertEquals(3, tableByUpgrade.size());
|
||||||
assertEquals(tableCount, tableByUpgrade.size());
|
|
||||||
assertEquals(tableByUpgrade.size(), tablesByCreate.size());
|
assertEquals(tableByUpgrade.size(), tablesByCreate.size());
|
||||||
|
|
||||||
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(MarkerColumns.TABLE_NAME), tableByUpgrade.get(MarkerColumns.TABLE_NAME));
|
assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.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(MarkerColumns.TABLE_NAME), indicesByCreate.get(MarkerColumns.TABLE_NAME));
|
assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -141,8 +158,8 @@ public class CustomSQLiteOpenHelperTest {
|
|||||||
Map<String, String> tablesByDowngrade;
|
Map<String, String> tablesByDowngrade;
|
||||||
Map<String, String> indicesByDowngrade;
|
Map<String, String> indicesByDowngrade;
|
||||||
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
|
try (SQLiteDatabase db = new CustomSQLiteOpenHelper(context, DATABASE_NAME, 23).getReadableDatabase()) {
|
||||||
tablesByDowngrade = getSQL(db, "table");
|
tablesByDowngrade = getTableSQL(db);
|
||||||
indicesByDowngrade = getSQL(db, "index");
|
indicesByDowngrade = getIndexSQL(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
// then - verify table structure
|
// then - verify table structure
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ 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;
|
||||||
@@ -11,19 +12,17 @@ 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");
|
||||||
|
|
||||||
assertEquals(0, ShareContentProvider.parseURI(uri).size());
|
Assert.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));
|
||||||
@@ -31,7 +30,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);
|
||||||
|
|
||||||
assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
|
Assert.assertEquals(trackIds, ShareContentProvider.parseURI(shareURIandMIME.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -40,6 +39,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);
|
||||||
|
|
||||||
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
|
Assert.assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/kml_only_track/1/..%2F..%2F%261%3D1.kml"), shareURIandMIME.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,12 @@ 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;
|
|
||||||
import static org.junit.Assert.assertNotEquals;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class SensorDataCyclingTest {
|
public class SensorDataCyclingTest {
|
||||||
|
|
||||||
@@ -24,7 +21,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -37,7 +34,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(33.53, current.getCadence_rpm(), 0.01);
|
Assert.assertEquals(33.53, current.getCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -50,7 +47,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0, current.getCadence_rpm(), 0.01);
|
Assert.assertEquals(0, current.getCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -64,7 +61,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(current.hasCadence_rpm());
|
Assert.assertFalse(current.hasCadence_rpm());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -77,7 +74,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -90,7 +87,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
Assert.assertEquals(60, current.getCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -103,7 +100,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous, 2150);
|
current.compute(previous, 2150);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1.20, current.getSpeed_mps(), 0.01);
|
Assert.assertEquals(1.20, current.getSpeed_mps(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -116,34 +113,6 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous, 2000);
|
current.compute(previous, 2000);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, current.getSpeed_mps(), 0.01);
|
Assert.assertEquals(2, current.getSpeed_mps(), 0.01);
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equals_speed_with_no_data() {
|
|
||||||
// given
|
|
||||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress");
|
|
||||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
|
||||||
|
|
||||||
// when
|
|
||||||
previous.toString();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNotEquals(previous, current);
|
|
||||||
assertNotEquals(previous, previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equals_cadence_with_no_data() {
|
|
||||||
// given
|
|
||||||
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress");
|
|
||||||
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048);
|
|
||||||
|
|
||||||
// when
|
|
||||||
previous.toString();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertNotEquals(previous, current);
|
|
||||||
assertNotEquals(previous, previous);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ 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;
|
||||||
@@ -34,8 +35,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -79,9 +78,9 @@ public class ChartFragmentTest {
|
|||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
Assert.assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
||||||
assertEquals(Float.NaN, point.getCadence(), 0.01);
|
Assert.assertEquals(Float.NaN, point.getCadence(), 0.01);
|
||||||
assertEquals(Float.NaN, point.getPower(), 0.01);
|
Assert.assertEquals(Float.NaN, point.getPower(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,9 +98,9 @@ public class ChartFragmentTest {
|
|||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
Assert.assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||||
assertEquals(101.0, point.getCadence(), 0.01);
|
Assert.assertEquals(101.0, point.getCadence(), 0.01);
|
||||||
assertEquals(102.0, point.getPower(), 0.01);
|
Assert.assertEquals(102.0, point.getPower(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,12 +112,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);
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
Assert.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);
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
Assert.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();
|
||||||
@@ -130,7 +129,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;
|
||||||
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
Assert.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();
|
||||||
@@ -141,7 +140,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;
|
||||||
assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
|
Assert.assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -157,7 +156,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);
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
Assert.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();
|
||||||
@@ -171,7 +170,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;
|
||||||
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
Assert.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();
|
||||||
@@ -184,7 +183,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;
|
||||||
assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
|
Assert.assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -195,12 +194,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);
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
Assert.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);
|
||||||
assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
|
Assert.assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,7 +214,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);
|
||||||
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
|
Assert.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.
|
||||||
@@ -223,7 +222,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);
|
||||||
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
|
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,7 +239,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);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
Assert.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.
|
||||||
@@ -254,7 +253,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);
|
||||||
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
|
Assert.assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -268,7 +267,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);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
|
||||||
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
|
|
||||||
@@ -279,7 +278,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);
|
||||||
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
Assert.assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -293,7 +292,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);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
Assert.assertEquals(0.0, point.getSpeed(), 0.01);
|
||||||
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
|
|
||||||
@@ -304,7 +303,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);
|
||||||
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
|
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,6 +315,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);
|
||||||
assertEquals(0.0, point.getPace(), 0.01);
|
Assert.assertEquals(0.0, point.getPace(), 0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ 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;
|
||||||
|
|
||||||
@@ -33,12 +34,11 @@ 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(MarkerColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
|
private static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(WaypointsColumns.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.insertMarker(any())).thenReturn(WAYPOINT_ID_O_URI);
|
when(contentProviderUtils.insertWaypoint(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) {
|
||||||
assertEquals(name, track.getName());
|
Assert.assertEquals(name, track.getName());
|
||||||
assertEquals(description, track.getDescription());
|
Assert.assertEquals(description, track.getDescription());
|
||||||
if (time != -1L) {
|
if (time != -1L) {
|
||||||
assertEquals(time, track.getTrackStatistics().getStartTime_ms());
|
Assert.assertEquals(time, track.getTrackStatistics().getStartTime_ms());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import androidx.test.filters.LargeTest;
|
|||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
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;
|
||||||
@@ -22,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;
|
||||||
@@ -54,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<Marker> markers = new ArrayList<>();
|
private final List<Waypoint> waypoints = new ArrayList<>();
|
||||||
private final List<TrackPoint> trackPoints = new ArrayList<>();
|
private final List<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
|
|
||||||
private Track.Id importTrackId;
|
private Track.Id importTrackId;
|
||||||
@@ -73,26 +72,25 @@ 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++) {
|
||||||
Marker marker = new Marker(trackId, track.second[i].getLocation());
|
Waypoint waypoint = new Waypoint(track.second[i].getLocation());
|
||||||
marker.setName("the marker " + i);
|
waypoint.setName("the waypoint " + i);
|
||||||
marker.setDescription("the marker description " + i);
|
waypoint.setDescription("the waypoint description " + i);
|
||||||
marker.setCategory("the marker category" + i);
|
waypoint.setCategory("the waypoint category" + i);
|
||||||
marker.setIcon("the marker icon" + i);
|
waypoint.setIcon("the waypoing icon" + i);
|
||||||
marker.setPhotoUrl("the photo url" + i);
|
waypoint.setPhotoUrl("the photo url" + i);
|
||||||
contentProviderUtils.insertMarker(marker);
|
waypoint.setTrackId(trackId);
|
||||||
|
contentProviderUtils.insertWaypoint(waypoint);
|
||||||
|
|
||||||
markers.add(marker);
|
waypoints.add(waypoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(markers.size(), contentProviderUtils.getMarkerCount(trackId));
|
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
if (importTrackId != null) {
|
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@@ -131,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. markers
|
// 2. waypoints
|
||||||
assertMarkers();
|
assertWaypoints();
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
assertTrackpoints(false, false, false, false);
|
assertTrackpoints(false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@@ -166,15 +164,15 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getName(), importedTrack.getName());
|
assertEquals(track.getName(), importedTrack.getName());
|
||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
|
|
||||||
// 2. markers
|
// 2. waypoints
|
||||||
assertMarkers();
|
assertWaypoints();
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
assertTrackpoints(true, true, true, true);
|
assertTrackpoints(true, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test(expected = ImportAlreadyExistsException.class)
|
@Test
|
||||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
|
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
|
||||||
// given
|
// given
|
||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||||
@@ -197,7 +195,6 @@ public class ExportImportTest {
|
|||||||
assertNull(importedTrack);
|
assertNull(importedTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kmz_only_track() {
|
public void kmz_only_track() {
|
||||||
@@ -205,7 +202,6 @@ public class ExportImportTest {
|
|||||||
Log.e(TAG, "Test not implemented.");
|
Log.e(TAG, "Test not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kmz_with_trackdetail() {
|
public void kmz_with_trackdetail() {
|
||||||
@@ -213,7 +209,6 @@ public class ExportImportTest {
|
|||||||
Log.e(TAG, "Test not implemented.");
|
Log.e(TAG, "Test not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kmz_with_trackdetail_and_sensordata() {
|
public void kmz_with_trackdetail_and_sensordata() {
|
||||||
@@ -221,7 +216,6 @@ public class ExportImportTest {
|
|||||||
Log.e(TAG, "Test not implemented.");
|
Log.e(TAG, "Test not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
||||||
@@ -259,15 +253,15 @@ 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. markers
|
// 2. waypoints
|
||||||
assertMarkers();
|
assertWaypoints();
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
assertTrackpoints(false, true, true, false);
|
assertTrackpoints(false, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test(expected = ImportAlreadyExistsException.class)
|
@Test
|
||||||
public void gpx_duplicate_trackUUID() {
|
public void gpx_duplicate_trackUUID() {
|
||||||
// given
|
// given
|
||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
||||||
@@ -291,26 +285,26 @@ public class ExportImportTest {
|
|||||||
assertNull(trackImported);
|
assertNull(trackImported);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertMarkers() {
|
private void assertWaypoints() {
|
||||||
assertEquals(markers.size(), contentProviderUtils.getMarkerCount(importTrackId));
|
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(importTrackId));
|
||||||
|
|
||||||
List<Marker> importedMarkers = contentProviderUtils.getMarkers(importTrackId);
|
List<Waypoint> importedWaypoints = contentProviderUtils.getWaypoints(importTrackId);
|
||||||
for (int i = 0; i < markers.size(); i++) {
|
for (int i = 0; i < waypoints.size(); i++) {
|
||||||
Marker marker = markers.get(i);
|
Waypoint waypoint = waypoints.get(i);
|
||||||
Marker importMarker = importedMarkers.get(i);
|
Waypoint importedWaypoint = importedWaypoints.get(i);
|
||||||
assertEquals(marker.getCategory(), importMarker.getCategory());
|
assertEquals(waypoint.getCategory(), importedWaypoint.getCategory());
|
||||||
assertEquals(marker.getDescription(), importMarker.getDescription());
|
assertEquals(waypoint.getDescription(), importedWaypoint.getDescription());
|
||||||
// assertEquals(marker.getIcon(), importMarker.getIcon()); // TODO for KML
|
// assertEquals(waypoint.getIcon(), importedWaypoint.getIcon()); // TODO for KML
|
||||||
assertEquals(marker.getName(), importMarker.getName());
|
assertEquals(waypoint.getName(), importedWaypoint.getName());
|
||||||
assertEquals("", importMarker.getPhotoUrl());
|
assertEquals("", importedWaypoint.getPhotoUrl());
|
||||||
|
|
||||||
assertEquals(marker.getLocation().getLatitude(), importMarker.getLocation().getLatitude(), 0.001);
|
assertEquals(waypoint.getLocation().getLatitude(), importedWaypoint.getLocation().getLatitude(), 0.001);
|
||||||
assertEquals(marker.getLocation().getLongitude(), importMarker.getLocation().getLongitude(), 0.001);
|
assertEquals(waypoint.getLocation().getLongitude(), importedWaypoint.getLocation().getLongitude(), 0.001);
|
||||||
assertEquals(marker.getLocation().getAltitude(), importMarker.getLocation().getAltitude(), 0.001);
|
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain) {
|
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) {
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
||||||
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
||||||
|
|
||||||
@@ -336,9 +330,6 @@ 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,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
@@ -22,8 +23,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -58,17 +57,17 @@ public class AdaptiveLocationListenerPolicyTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetDesiredPollingInterval() {
|
public void testGetDesiredPollingInterval() {
|
||||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||||
assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
Assert.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.
|
||||||
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||||
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||||
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +75,6 @@ public class AdaptiveLocationListenerPolicyTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetMinDistance() {
|
public void testGetMinDistance() {
|
||||||
assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
|
Assert.assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,16 @@ 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;
|
||||||
@@ -44,22 +45,15 @@ 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.
|
||||||
*
|
*
|
||||||
@@ -126,14 +120,14 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testStartable() throws TimeoutException {
|
public void testStartable() throws TimeoutException {
|
||||||
mServiceRule.startService(createStartIntent(context));
|
mServiceRule.startService(createStartIntent(context));
|
||||||
assertNotNull(mServiceRule.bindService(createStartIntent(context)));
|
Assert.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));
|
||||||
assertNotNull(service);
|
Assert.assertNotNull(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -141,7 +135,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();
|
||||||
assertTrue(tracks.isEmpty());
|
Assert.assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Intent startIntent = createStartIntent(context);
|
Intent startIntent = createStartIntent(context);
|
||||||
@@ -150,8 +144,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.
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
Assert.assertNull(service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -164,8 +158,8 @@ public class TrackRecordingServiceTest {
|
|||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
Assert.assertNull(service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -178,7 +172,7 @@ public class TrackRecordingServiceTest {
|
|||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -193,21 +187,21 @@ public class TrackRecordingServiceTest {
|
|||||||
service.pauseCurrentTrack();
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
Assert.assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
||||||
|
|
||||||
//when
|
//when
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
newTrackPoint(service);
|
newTrackPoint(service);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
Assert.assertEquals(trackId, service.getRecordingTrackId());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(5, trackPoints.size());
|
Assert.assertEquals(5, trackPoints.size());
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
||||||
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -219,23 +213,23 @@ public class TrackRecordingServiceTest {
|
|||||||
newTrackPoint(service);
|
newTrackPoint(service);
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
|
Assert.assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
newTrackPoint(service);
|
newTrackPoint(service);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
Assert.assertEquals(trackId, service.getRecordingTrackId());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(4, trackPoints.size());
|
Assert.assertEquals(4, trackPoints.size());
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
||||||
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Sometimes fails on CI.")
|
@FlakyTest(detail = "Sometimes fails on CI.")
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||||
@@ -248,8 +242,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
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
Assert.assertNull(service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -258,7 +252,7 @@ public class TrackRecordingServiceTest {
|
|||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
service.startNewTrack();
|
service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
|
|
||||||
Track.Id trackId = service.getRecordingTrackId();
|
Track.Id trackId = service.getRecordingTrackId();
|
||||||
|
|
||||||
@@ -266,10 +260,10 @@ public class TrackRecordingServiceTest {
|
|||||||
Track.Id newTrackId = service.startNewTrack();
|
Track.Id newTrackId = service.startNewTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(newTrackId);
|
Assert.assertNull(newTrackId);
|
||||||
|
|
||||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
Assert.assertEquals(trackId, service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -277,15 +271,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)));
|
||||||
assertFalse(service.isRecording());
|
Assert.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
|
||||||
assertFalse(PreferencesUtils.isRecording(context));
|
Assert.assertFalse(PreferencesUtils.isRecording(context));
|
||||||
assertNull(service.getRecordingTrackId());
|
Assert.assertNull(service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -293,13 +287,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)));
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Marker.Id waypointId = service.insertMarker(null, null, null, null);
|
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(waypointId);
|
Assert.assertNull(waypointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -308,29 +302,29 @@ public class TrackRecordingServiceTest {
|
|||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
service.startNewTrack();
|
service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
newTrackPoint(service);
|
newTrackPoint(service);
|
||||||
Track.Id trackId = service.getRecordingTrackId();
|
Track.Id trackId = service.getRecordingTrackId();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Marker.Id waypointId = service.insertMarker(null, null, null, null);
|
Waypoint.Id waypointId = service.insertWaypoint(null, null, null, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotEquals(-1L, waypointId);
|
Assert.assertNotEquals(-1L, waypointId);
|
||||||
Marker wpt = contentProviderUtils.getMarker(waypointId);
|
Waypoint wpt = contentProviderUtils.getWaypoint(waypointId);
|
||||||
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
Assert.assertEquals(context.getString(R.string.marker_waypoint_icon_url), wpt.getIcon());
|
||||||
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||||
assertEquals(trackId, wpt.getTrackId());
|
Assert.assertEquals(trackId, wpt.getTrackId());
|
||||||
assertEquals(0.0, wpt.getLength(), 0.01);
|
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||||
assertNotNull(wpt.getLocation());
|
Assert.assertNotNull(wpt.getLocation());
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTrack(Track track, boolean isRecording) {
|
private void addTrack(Track track, boolean isRecording) {
|
||||||
assertTrue(track.getId().isValid());
|
Assert.assertTrue(track.getId().isValid());
|
||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
Assert.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,6 +12,7 @@ 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;
|
||||||
@@ -32,9 +33,6 @@ 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>
|
||||||
@@ -105,11 +103,11 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(6, trackPoints.size());
|
Assert.assertEquals(6, trackPoints.size());
|
||||||
assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01);
|
Assert.assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -129,11 +127,11 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(2, trackPoints.size());
|
Assert.assertEquals(2, trackPoints.size());
|
||||||
assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
|
Assert.assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @MediumTest
|
// @MediumTest
|
||||||
@@ -171,13 +169,13 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(3, trackPoints.size());
|
Assert.assertEquals(3, trackPoints.size());
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
||||||
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
||||||
assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
|
Assert.assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -197,14 +195,14 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(4, trackPoints.size());
|
Assert.assertEquals(4, trackPoints.size());
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
||||||
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
Assert.assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
||||||
assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
|
Assert.assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
|
||||||
assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
|
Assert.assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -240,16 +238,16 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(6, trackPoints.size());
|
Assert.assertEquals(6, trackPoints.size());
|
||||||
assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
|
Assert.assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
|
||||||
assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
|
Assert.assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
|
||||||
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
||||||
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
||||||
assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
|
Assert.assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
|
||||||
assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
|
Assert.assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -268,16 +266,16 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
||||||
assertEquals(7, trackPoints.size());
|
Assert.assertEquals(7, trackPoints.size());
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
Assert.assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
||||||
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
Assert.assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
||||||
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
Assert.assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
|
Assert.assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
|
||||||
assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
|
Assert.assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
|
||||||
assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
|
Assert.assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ 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;
|
||||||
@@ -31,12 +32,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -193,23 +188,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();
|
||||||
assertTrue(tracks.isEmpty());
|
Assert.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)));
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
// Start a track.
|
// Start a track.
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
assertTrue(trackId.isValid());
|
Assert.assertTrue(trackId.isValid());
|
||||||
assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
assertNotNull(track);
|
Assert.assertNotNull(track);
|
||||||
assertEquals(trackId, track.getId());
|
Assert.assertEquals(trackId, track.getId());
|
||||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
Assert.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();
|
||||||
@@ -226,20 +221,20 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
service.newTrackPoint(trackPoint, prefAccuracy);
|
service.newTrackPoint(trackPoint, prefAccuracy);
|
||||||
|
|
||||||
if (i % 7 == 0) {
|
if (i % 7 == 0) {
|
||||||
service.insertMarker(null, null, null, null);
|
service.insertWaypoint(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();
|
||||||
assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
Assert.assertNull(service.getRecordingTrackId());
|
||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
assertNotNull(track);
|
Assert.assertNotNull(track);
|
||||||
assertEquals(trackId, track.getId());
|
Assert.assertEquals(trackId, track.getId());
|
||||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
assertNotNull(trackStatistics);
|
Assert.assertNotNull(trackStatistics);
|
||||||
assertTrue(trackStatistics.getStartTime_ms() > 0);
|
Assert.assertTrue(trackStatistics.getStartTime_ms() > 0);
|
||||||
assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
|
Assert.assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
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,12 +23,11 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -54,6 +53,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);
|
||||||
assertTrue(task instanceof AnnouncementPeriodicTask);
|
Assert.assertTrue(task instanceof AnnouncementPeriodicTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,13 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
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.
|
||||||
@@ -51,8 +50,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.0f);
|
statistics.setTotalElevationGain(50.0);
|
||||||
statistics2.setTotalElevationGain(850.0f); // Result: 850+50
|
statistics2.setTotalElevationGain(850.0); // 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);
|
||||||
@@ -62,28 +61,28 @@ public class TrackStatisticsTest {
|
|||||||
|
|
||||||
statistics.merge(statistics2);
|
statistics.merge(statistics2);
|
||||||
|
|
||||||
assertEquals(1000L, statistics.getStartTime_ms());
|
Assert.assertEquals(1000L, statistics.getStartTime_ms());
|
||||||
assertEquals(4000L, statistics.getStopTime_ms());
|
Assert.assertEquals(4000L, statistics.getStopTime_ms());
|
||||||
assertEquals(2500L, statistics.getTotalTime());
|
Assert.assertEquals(2500L, statistics.getTotalTime());
|
||||||
assertEquals(1300L, statistics.getMovingTime());
|
Assert.assertEquals(1300L, statistics.getMovingTime());
|
||||||
assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
Assert.assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
||||||
assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
Assert.assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
||||||
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
|
Assert.assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
|
||||||
assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
Assert.assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
||||||
assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
|
Assert.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
|
||||||
assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
|
Assert.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
|
||||||
assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
Assert.assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,11 @@ 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 {
|
||||||
@@ -35,50 +26,9 @@ public class AnnouncementUtilsTest {
|
|||||||
stats.setTotalElevationGain(6000);
|
stats.setTotalElevationGain(6000);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", null);
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane");
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
|
Assert.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,13 +2,11 @@ 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
|
||||||
@@ -21,7 +19,7 @@ public class BluetoothUtilsTest {
|
|||||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, heartRate);
|
Assert.assertEquals(60, heartRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -34,7 +32,7 @@ public class BluetoothUtilsTest {
|
|||||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(257, heartRate);
|
Assert.assertEquals(257, heartRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -46,8 +44,8 @@ public class BluetoothUtilsTest {
|
|||||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||||
assertNull(sensor.getSpeed());
|
Assert.assertNull(sensor.getSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -59,8 +57,8 @@ public class BluetoothUtilsTest {
|
|||||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(sensor.getCadence());
|
Assert.assertNull(sensor.getCadence());
|
||||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -72,19 +70,7 @@ public class BluetoothUtilsTest {
|
|||||||
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
SensorDataCycling.CadenceAndSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
Assert.assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
Assert.assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void parseCyclingPower_power() {
|
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER_MEASUREMENT_CHAR_UUID, 0, 0);
|
|
||||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
|
||||||
|
|
||||||
// when
|
|
||||||
int power_w = BluetoothUtils.parseCyclingPower(characteristic);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(40, power_w);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
@@ -22,8 +23,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -38,7 +37,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");
|
||||||
assertEquals("Filename.ext", filename);
|
Assert.assertEquals("Filename.ext", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,7 +47,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");
|
||||||
assertEquals("default(1).prop", filename);
|
Assert.assertEquals("default(1).prop", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,13 +58,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).";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSanitizeFileNameWithSuffix() {
|
public void testSanitizeFileNameWithSuffix() {
|
||||||
String name = "FileName.jpeg";
|
String name = "FileName.jpeg";
|
||||||
assertEquals(name, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(name, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,7 +75,7 @@ public class FileUtilsTest {
|
|||||||
public void testSanitizeFileName_i18n() {
|
public void testSanitizeFileName_i18n() {
|
||||||
String name = "您好-привет";
|
String name = "您好-привет";
|
||||||
String expected = "您好-привет";
|
String expected = "您好-привет";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +86,7 @@ public class FileUtilsTest {
|
|||||||
public void testSanitizeFileName_special_characters() {
|
public void testSanitizeFileName_special_characters() {
|
||||||
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +97,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";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,8 +114,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++) {
|
||||||
assertEquals('a', truncated.charAt(i));
|
Assert.assertEquals('a', truncated.charAt(i));
|
||||||
}
|
}
|
||||||
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
|
Assert.assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ 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 {
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ public class PreferencesUtilsTest {
|
|||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
|
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -44,7 +43,7 @@ public class PreferencesUtilsTest {
|
|||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -58,6 +57,6 @@ public class PreferencesUtilsTest {
|
|||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
Assert.assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
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,15 +21,13 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -46,7 +44,7 @@ public class StringUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFormatDateTimeIso8601() {
|
public void testFormatDateTimeIso8601() {
|
||||||
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,19 +53,19 @@ public class StringUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testformatElapsedTime() {
|
public void testformatElapsedTime() {
|
||||||
// 1 second
|
// 1 second
|
||||||
assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
||||||
// 10 seconds
|
// 10 seconds
|
||||||
assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
Assert.assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
||||||
// 1 minute
|
// 1 minute
|
||||||
assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
Assert.assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
||||||
// 10 minutes
|
// 10 minutes
|
||||||
assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
Assert.assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
||||||
// 1 hour
|
// 1 hour
|
||||||
assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
||||||
// 10 hours
|
// 10 hours
|
||||||
assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
||||||
// 100 hours
|
// 100 hours
|
||||||
assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,19 +74,19 @@ public class StringUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testformatElapsedTimeWithHour() {
|
public void testformatElapsedTimeWithHour() {
|
||||||
// 1 second
|
// 1 second
|
||||||
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
||||||
// 10 seconds
|
// 10 seconds
|
||||||
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
Assert.assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
||||||
// 1 minute
|
// 1 minute
|
||||||
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
Assert.assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
||||||
// 10 minutes
|
// 10 minutes
|
||||||
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
Assert.assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
||||||
// 1 hour
|
// 1 hour
|
||||||
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
Assert.assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
||||||
// 10 hours
|
// 10 hours
|
||||||
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
Assert.assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
||||||
// 100 hours
|
// 100 hours
|
||||||
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
Assert.assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,13 +96,13 @@ public class StringUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testFormatDistance() {
|
public void testFormatDistance() {
|
||||||
// A large number in metric
|
// A large number in metric
|
||||||
assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
|
Assert.assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
|
||||||
// A large number in imperial
|
// A large number in imperial
|
||||||
assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
|
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
|
||||||
// A small number in metric
|
// A small number in metric
|
||||||
assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
|
Assert.assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
|
||||||
// A small number in imperial
|
// A small number in imperial
|
||||||
assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
|
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,8 +110,8 @@ public class StringUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFormatCData() {
|
public void testFormatCData() {
|
||||||
assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
||||||
assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,7 +168,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));
|
||||||
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
|
Assert.assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,9 +177,9 @@ public class StringUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetTimeParts_positive() {
|
public void testGetTimeParts_positive() {
|
||||||
int[] parts = StringUtils.getTimeParts(61000);
|
int[] parts = StringUtils.getTimeParts(61000);
|
||||||
assertEquals(1, parts[0]);
|
Assert.assertEquals(1, parts[0]);
|
||||||
assertEquals(1, parts[1]);
|
Assert.assertEquals(1, parts[1]);
|
||||||
assertEquals(0, parts[2]);
|
Assert.assertEquals(0, parts[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -190,40 +188,40 @@ public class StringUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetTimeParts_negative() {
|
public void testGetTimeParts_negative() {
|
||||||
int[] parts = StringUtils.getTimeParts(-61000);
|
int[] parts = StringUtils.getTimeParts(-61000);
|
||||||
assertEquals(-1, parts[0]);
|
Assert.assertEquals(-1, parts[0]);
|
||||||
assertEquals(-1, parts[1]);
|
Assert.assertEquals(-1, parts[1]);
|
||||||
assertEquals(0, parts[2]);
|
Assert.assertEquals(0, parts[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatDecimal() {
|
public void testFormatDecimal() {
|
||||||
assertEquals("0", StringUtils.formatDecimal(0.0, 0));
|
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 0));
|
||||||
assertEquals("0", StringUtils.formatDecimal(0.1, 0));
|
Assert.assertEquals("0", StringUtils.formatDecimal(0.1, 0));
|
||||||
assertEquals("1", StringUtils.formatDecimal(1.1, 0));
|
Assert.assertEquals("1", StringUtils.formatDecimal(1.1, 0));
|
||||||
assertEquals("10", StringUtils.formatDecimal(10, 0));
|
Assert.assertEquals("10", StringUtils.formatDecimal(10, 0));
|
||||||
assertEquals("10", StringUtils.formatDecimal(10.1, 0));
|
Assert.assertEquals("10", StringUtils.formatDecimal(10.1, 0));
|
||||||
assertEquals("-0", StringUtils.formatDecimal(-0.1, 0));
|
Assert.assertEquals("0", StringUtils.formatDecimal(-0.1, 0));
|
||||||
|
|
||||||
assertEquals("0", StringUtils.formatDecimal(0.0, 2));
|
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 2));
|
||||||
assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
|
Assert.assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
|
||||||
assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
|
Assert.assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
|
||||||
assertEquals("10", StringUtils.formatDecimal(10, 2));
|
Assert.assertEquals("10", StringUtils.formatDecimal(10, 2));
|
||||||
assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
|
Assert.assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
|
||||||
assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
|
Assert.assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
|
||||||
assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
|
Assert.assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
|
||||||
|
|
||||||
assertEquals("1", StringUtils.formatDecimal(0.99, 1));
|
Assert.assertEquals("1", StringUtils.formatDecimal(0.99, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetSpeedParts() {
|
public void testGetSpeedParts() {
|
||||||
assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
|
Assert.assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
|
||||||
assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
|
Assert.assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
|
||||||
|
|
||||||
assertEquals("11.9", StringUtils.getSpeedParts(context, 3.31, true, true).first);
|
Assert.assertEquals("11.99", StringUtils.getSpeedParts(context, 3.33, true, true).first);
|
||||||
assertEquals("7.5", StringUtils.getSpeedParts(context, 3.34, false, true).first);
|
Assert.assertEquals("7.45", StringUtils.getSpeedParts(context, 3.33, false, true).first);
|
||||||
|
|
||||||
assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
|
Assert.assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
|
||||||
assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
|
Assert.assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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;
|
||||||
|
|
||||||
@@ -31,8 +32,6 @@ 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}.
|
||||||
*
|
*
|
||||||
@@ -52,7 +51,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));
|
||||||
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,7 +61,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);
|
||||||
assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,6 +70,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));
|
||||||
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
Assert.assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
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
|
||||||
@@ -19,7 +18,7 @@ public class TrackPointUtilsTest {
|
|||||||
TrackPointUtils.fixTime(trackPoint);
|
TrackPointUtils.fixTime(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(time, trackPoint.getTime());
|
Assert.assertEquals(time, trackPoint.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -33,7 +32,7 @@ public class TrackPointUtilsTest {
|
|||||||
TrackPointUtils.fixTime(trackPoint);
|
TrackPointUtils.fixTime(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(time, trackPoint.getTime(), 1000);
|
Assert.assertEquals(time, trackPoint.getTime(), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -47,7 +46,7 @@ public class TrackPointUtilsTest {
|
|||||||
TrackPointUtils.fixTime(trackPoint);
|
TrackPointUtils.fixTime(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(time, trackPoint.getTime());
|
Assert.assertEquals(time, trackPoint.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,12 @@ 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 {
|
||||||
|
|
||||||
@@ -19,6 +18,6 @@ public class UUIDUtilsTest {
|
|||||||
byte[] bytes = UUIDUtils.toBytes(uuid);
|
byte[] bytes = UUIDUtils.toBytes(uuid);
|
||||||
UUID output = UUIDUtils.fromBytes(bytes);
|
UUID output = UUIDUtils.fromBytes(bytes);
|
||||||
|
|
||||||
assertEquals(uuid, output);
|
Assert.assertEquals(uuid, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ 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;
|
||||||
@@ -13,9 +14,6 @@ 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 {
|
||||||
|
|
||||||
@@ -35,7 +33,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.0f);
|
statistics.setTotalElevationGain(50.0);
|
||||||
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
|
||||||
@@ -61,13 +59,13 @@ public class AggregatedStatisticsTest {
|
|||||||
aggregatedStatistics.aggregate(track);
|
aggregatedStatistics.aggregate(track);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, aggregatedStatistics.getCount());
|
Assert.assertEquals(1, aggregatedStatistics.getCount());
|
||||||
assertNotNull(aggregatedStatistics.get(biking));
|
Assert.assertNotNull(aggregatedStatistics.get(biking));
|
||||||
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
||||||
|
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -84,7 +82,7 @@ public class AggregatedStatisticsTest {
|
|||||||
aggregatedStatistics.aggregate(track);
|
aggregatedStatistics.aggregate(track);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotNull(aggregatedStatistics.get(mountainBiking));
|
Assert.assertNotNull(aggregatedStatistics.get(mountainBiking));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -101,7 +99,7 @@ public class AggregatedStatisticsTest {
|
|||||||
aggregatedStatistics.aggregate(track);
|
aggregatedStatistics.aggregate(track);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotNull(aggregatedStatistics.get(trailRunning));
|
Assert.assertNotNull(aggregatedStatistics.get(trailRunning));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -122,13 +120,13 @@ public class AggregatedStatisticsTest {
|
|||||||
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
|
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, aggregatedStatistics.getCount());
|
Assert.assertEquals(1, aggregatedStatistics.getCount());
|
||||||
assertNotNull(aggregatedStatistics.get(biking));
|
Assert.assertNotNull(aggregatedStatistics.get(biking));
|
||||||
assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
|
Assert.assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
|
||||||
|
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime * 2L, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime * 2L, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -154,30 +152,30 @@ public class AggregatedStatisticsTest {
|
|||||||
aggregatedStatistics.aggregate(tracks[2]); // biking activity 3.
|
aggregatedStatistics.aggregate(tracks[2]); // biking activity 3.
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(3, aggregatedStatistics.getCount());
|
Assert.assertEquals(3, aggregatedStatistics.getCount());
|
||||||
assertNotNull(aggregatedStatistics.get(biking));
|
Assert.assertNotNull(aggregatedStatistics.get(biking));
|
||||||
assertNotNull(aggregatedStatistics.get(running));
|
Assert.assertNotNull(aggregatedStatistics.get(running));
|
||||||
assertNotNull(aggregatedStatistics.get(walking));
|
Assert.assertNotNull(aggregatedStatistics.get(walking));
|
||||||
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
||||||
assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
|
Assert.assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
|
||||||
assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
|
Assert.assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
|
||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,46 +212,46 @@ public class AggregatedStatisticsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
// 4 sports.
|
// 4 sports.
|
||||||
assertEquals(4, aggregatedStatistics.getCount());
|
Assert.assertEquals(4, aggregatedStatistics.getCount());
|
||||||
|
|
||||||
// There is a map for every sport.
|
// There is a map for every sport.
|
||||||
assertNotNull(aggregatedStatistics.get(biking));
|
Assert.assertNotNull(aggregatedStatistics.get(biking));
|
||||||
assertNotNull(aggregatedStatistics.get(running));
|
Assert.assertNotNull(aggregatedStatistics.get(running));
|
||||||
assertNotNull(aggregatedStatistics.get(walking));
|
Assert.assertNotNull(aggregatedStatistics.get(walking));
|
||||||
assertNotNull(aggregatedStatistics.get(driving));
|
Assert.assertNotNull(aggregatedStatistics.get(driving));
|
||||||
|
|
||||||
// Number of tracks by sport.
|
// Number of tracks by sport.
|
||||||
assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
|
Assert.assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
|
||||||
assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running.
|
Assert.assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running.
|
||||||
assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking.
|
Assert.assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking.
|
||||||
assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving.
|
Assert.assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving.
|
||||||
|
|
||||||
// Biking.
|
// Biking.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime * 5, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime * 5, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running.
|
// Running.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime * 2, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walking.
|
// Walking.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime * 2, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driving.
|
// Driving.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
||||||
assertEquals(totalTime, statistics2.getMovingTime());
|
Assert.assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
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 = 3000f;
|
|
||||||
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 = 3000f;
|
|
||||||
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 = 1000f;
|
|
||||||
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;
|
|
||||||
float totalGain = 0f;
|
|
||||||
for (IntervalStatistics.Interval i : intervalList) {
|
|
||||||
totalDistance += i.getDistance_m();
|
|
||||||
totalTime += ((i.getDistance_m() / i.getSpeed_ms()) * UnitConversions.S_TO_MS);
|
|
||||||
totalGain += i.getGain_m();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
assertEquals(totalGain, trackPoints.size() * TestDataUtil.ELEVATION_GAIN, 0.1);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,7 @@ 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">
|
||||||
|
|
||||||
@@ -68,170 +69,38 @@ limitations under the License.
|
|||||||
|
|
||||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity" />
|
<activity android:name=".io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity" />
|
||||||
|
|
||||||
<activity android:name=".io.file.importer.ImportActivity">
|
<activity
|
||||||
|
android:name=".io.file.importer.ImportActivity"
|
||||||
|
android:theme="@style/ThemeCustomTransparentActivity">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:mimeType="application/gpx+xml"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.SEND"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:mimeType="application/gpx"/>
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<data android:mimeType="application/vnd.google-earth.kmz" />
|
||||||
<data android:scheme="file"/>
|
<data
|
||||||
<data android:scheme="content"/>
|
android:scheme="content"
|
||||||
<data android:scheme="data"/>
|
android:host="*"
|
||||||
<data android:host="*"/>
|
android:pathPattern="*kmz" />
|
||||||
<data android:mimeType="*/*"/>
|
|
||||||
<data android:pathPattern=".*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="file"/>
|
|
||||||
<data android:scheme="content"/>
|
|
||||||
<data android:scheme="data"/>
|
|
||||||
<data android:host="*"/>
|
|
||||||
<data android:pathPattern=".*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpx" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:mimeType="application/vnd.google-earth.kmz"/>
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<data android:mimeType="application/vnd.google-earth.kml+xml" />
|
||||||
<data android:scheme="file"/>
|
<data
|
||||||
<data android:scheme="content"/>
|
android:scheme="content"
|
||||||
<data android:scheme="data"/>
|
android:host="*"
|
||||||
<data android:host="*"/>
|
android:pathPattern="*kml" />
|
||||||
<data android:mimeType="*/*"/>
|
|
||||||
<data android:pathPattern=".*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="file"/>
|
|
||||||
<data android:scheme="content"/>
|
|
||||||
<data android:scheme="data"/>
|
|
||||||
<data android:host="*"/>
|
|
||||||
<data android:pathPattern=".*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kmz" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:mimeType="application/vnd.google-earth.kml+xml"/>
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<data android:mimeType="application/gpx+xml" />
|
||||||
<data android:scheme="file"/>
|
<data
|
||||||
<data android:scheme="content"/>
|
android:scheme="content"
|
||||||
<data android:scheme="data"/>
|
android:host="*"
|
||||||
<data android:host="*"/>
|
android:pathPattern="*gpx" />
|
||||||
<data android:mimeType="*/*"/>
|
|
||||||
<data android:pathPattern=".*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
</intent-filter>
|
|
||||||
|
|
||||||
<intent-filter
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:priority="50">
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<action android:name="android.intent.action.SEND" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="file"/>
|
|
||||||
<data android:scheme="content"/>
|
|
||||||
<data android:scheme="data"/>
|
|
||||||
<data android:host="*"/>
|
|
||||||
<data android:pathPattern=".*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kml" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
@@ -246,24 +115,16 @@ limitations under the License.
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MarkerListActivity"
|
android:name=".MarkerListActivity"
|
||||||
android:label="@string/menu_markers"
|
android:label="@string/menu_markers">
|
||||||
android:launchMode="singleTop">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
|
||||||
</intent-filter>
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.app.searchable"
|
android:name="android.app.default_searchable"
|
||||||
android:resource="@xml/searchable" />
|
android:value=".SearchListActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".TrackListActivity"
|
android:name=".SearchListActivity"
|
||||||
android:theme="@style/SplashTheme"
|
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
|
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
<meta-data
|
||||||
@@ -277,12 +138,23 @@ limitations under the License.
|
|||||||
|
|
||||||
<activity android:name=".TrackEditActivity" />
|
<activity android:name=".TrackEditActivity" />
|
||||||
|
|
||||||
|
<activity android:name=".TrackListActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.default_searchable"
|
||||||
|
android:value=".SearchListActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".AggregatedStatisticsActivity" />
|
<activity android:name=".AggregatedStatisticsActivity" />
|
||||||
|
|
||||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
||||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity" />
|
|
||||||
|
|
||||||
<activity android:name=".io.file.exporter.ExportActivity" />
|
<activity
|
||||||
|
android:name=".io.file.exporter.ExportActivity"
|
||||||
|
android:theme="@style/ThemeCustomTransparentActivity" />
|
||||||
|
|
||||||
<activity android:name=".settings.SettingsActivity" />
|
<activity android:name=".settings.SettingsActivity" />
|
||||||
|
|
||||||
@@ -322,15 +194,5 @@ limitations under the License.
|
|||||||
android:foregroundServiceType="location|connectedDevice"
|
android:foregroundServiceType="location|connectedDevice"
|
||||||
android:icon="@drawable/ic_logo_color_24dp"
|
android:icon="@drawable/ic_logo_color_24dp"
|
||||||
android:label="@string/recording_service" />
|
android:label="@string/recording_service" />
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".io.file.exporter.ExportService"
|
|
||||||
android:exported="false"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".io.file.importer.ImportService"
|
|
||||||
android:exported="false"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -2,35 +2,38 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.View;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.databinding.AboutBinding;
|
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
import de.dennisguse.opentracks.util.ViewUtils;
|
import de.dennisguse.opentracks.util.ViewUtils;
|
||||||
|
|
||||||
public class AboutActivity extends AbstractActivity {
|
public class AboutActivity extends AbstractActivity {
|
||||||
|
|
||||||
private AboutBinding viewBinding;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setTitle(getString(R.string.about_preference_title));
|
setTitle(getString(R.string.about_preference_title));
|
||||||
|
|
||||||
viewBinding.aboutTextDescription.setText(getString(R.string.about_description));
|
TextView textDescription = findViewById(R.id.about_text_description);
|
||||||
viewBinding.aboutTextVersionName.setText(getString(R.string.about_version_name, SystemUtils.getAppVersionName(this)));
|
textDescription.setText(getString(R.string.about_description));
|
||||||
viewBinding.aboutTextVersionCode.setText(getString(R.string.about_version_code, SystemUtils.getAppVersionCode(this)));
|
|
||||||
viewBinding.aboutAppUrl.setText(getString(R.string.about_url, getString(R.string.app_web_url)));
|
TextView textVersionName = findViewById(R.id.about_text_version_name);
|
||||||
|
textVersionName.setText(getString(R.string.about_version_name, SystemUtils.getAppVersionName(this)));
|
||||||
|
|
||||||
|
TextView textVersionCode = findViewById(R.id.about_text_version_code);
|
||||||
|
textVersionCode.setText(getString(R.string.about_version_code, SystemUtils.getAppVersionCode(this)));
|
||||||
|
|
||||||
|
TextView textURL = findViewById(R.id.about_app_url);
|
||||||
|
textURL.setText(getString(R.string.about_url, getString(R.string.app_web_url)));
|
||||||
|
|
||||||
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
|
ViewUtils.makeClickableLinks(findViewById(android.R.id.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = AboutBinding.inflate(getLayoutInflater());
|
return R.layout.about;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
@@ -36,11 +35,14 @@ public abstract class AbstractActivity extends AppCompatActivity {
|
|||||||
// Set volume control stream for text to speech
|
// Set volume control stream for text to speech
|
||||||
setVolumeControlStream(TTSUtils.getTTSStream());
|
setVolumeControlStream(TTSUtils.getTTSStream());
|
||||||
|
|
||||||
setContentView(getRootView());
|
setContentView(getLayoutResId());
|
||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract View getRootView();
|
/**
|
||||||
|
* Gets the layout resource id.
|
||||||
|
*/
|
||||||
|
protected abstract int getLayoutResId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class for the following common tasks across
|
* An abstract class for the following common tasks across
|
||||||
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and {@link TrackRecordedActivity}:
|
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and
|
||||||
|
* {@link SearchListActivity}:
|
||||||
* <p>
|
* <p>
|
||||||
* - share track <br>
|
* - share track <br>
|
||||||
* - delete tracks <br>
|
* - delete tracks <br>
|
||||||
@@ -47,7 +48,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == DELETE_REQUEST_CODE) {
|
if (requestCode == DELETE_REQUEST_CODE) {
|
||||||
onTrackDeleted();
|
onDeleted();
|
||||||
} else {
|
} else {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
}
|
}
|
||||||
@@ -92,5 +93,5 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
/**
|
/**
|
||||||
* Called after {@link TrackDeleteActivity} returns its result.
|
* Called after {@link TrackDeleteActivity} returns its result.
|
||||||
*/
|
*/
|
||||||
abstract protected void onTrackDeleted();
|
abstract protected void onDeleted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,37 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.adapters.AggregatedStatisticsAdapter;
|
import de.dennisguse.opentracks.adapters.AggregatedStatisticsAdapter;
|
||||||
import de.dennisguse.opentracks.databinding.AggregatedStatsBinding;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.AggregatedStatisticsModel;
|
import de.dennisguse.opentracks.viewmodels.AggregatedStatisticsModel;
|
||||||
|
|
||||||
public class AggregatedStatisticsActivity extends AbstractActivity {
|
public class AggregatedStatisticsActivity extends AbstractActivity {
|
||||||
|
|
||||||
private AggregatedStatsBinding viewBinding;
|
private ListView listView;
|
||||||
|
|
||||||
private AggregatedStatisticsAdapter adapter;
|
private AggregatedStatisticsAdapter adapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
viewBinding.aggregatedStatsList.setEmptyView(viewBinding.aggregatedStatsEmptyView);
|
listView = findViewById(R.id.aggregated_stats_list);
|
||||||
|
listView.setEmptyView(findViewById(R.id.aggregated_stats_empty_view));
|
||||||
|
|
||||||
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(this, aggregatedStatistics);
|
adapter = new AggregatedStatisticsAdapter(getApplicationContext(), aggregatedStatistics);
|
||||||
viewBinding.aggregatedStatsList.setAdapter(adapter);
|
listView.setAdapter(adapter);
|
||||||
}
|
}
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = AggregatedStatsBinding.inflate(getLayoutInflater());
|
return R.layout.aggregated_stats;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.databinding.HelpBinding;
|
|
||||||
import de.dennisguse.opentracks.util.ViewUtils;
|
import de.dennisguse.opentracks.util.ViewUtils;
|
||||||
|
|
||||||
public class HelpActivity extends AbstractActivity {
|
public class HelpActivity extends AbstractActivity {
|
||||||
@@ -17,7 +15,7 @@ public class HelpActivity extends AbstractActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
return HelpBinding.inflate(getLayoutInflater()).getRoot();
|
return R.layout.help;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package de.dennisguse.opentracks;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -31,9 +30,8 @@ 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.Marker;
|
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.databinding.MarkerDetailActivityBinding;
|
|
||||||
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;
|
||||||
|
|
||||||
@@ -50,35 +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 MarkerDetailActivityBinding viewBinding;
|
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);
|
||||||
|
|
||||||
Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
Waypoint.Id waypointId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
||||||
if (markerId == null) {
|
if (waypointId == 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);
|
||||||
Marker marker = contentProviderUtils.getMarker(markerId);
|
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
|
|
||||||
markerIds = new ArrayList<>();
|
waypointIds = new ArrayList<>();
|
||||||
int markerIndex = -1;
|
int markerIndex = -1;
|
||||||
|
|
||||||
//TODO Load only markerIds, not the whole marker
|
//TODO Load only waypointIds, not the whole waypoint
|
||||||
try (Cursor cursor = contentProviderUtils.getMarkerCursor(marker.getTrackId(), null, -1)) {
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(waypoint.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++) {
|
||||||
Marker currentMarker = contentProviderUtils.createMarker(cursor);
|
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
|
||||||
markerIds.add(currentMarker.getId());
|
waypointIds.add(currentMarker.getId());
|
||||||
if (markerId.equals(currentMarker.getId())) {
|
if (waypointId.equals(currentMarker.getId())) {
|
||||||
markerIndex = markerIds.size() - 1;
|
markerIndex = waypointIds.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
@@ -86,9 +82,10 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ViewPager viewPager = findViewById(R.id.maker_detail_activity_view_pager);
|
||||||
final MarkerDetailPagerAdapter markerAdapter = new MarkerDetailPagerAdapter(getSupportFragmentManager());
|
final MarkerDetailPagerAdapter markerAdapter = new MarkerDetailPagerAdapter(getSupportFragmentManager());
|
||||||
viewBinding.makerDetailActivityViewPager.setAdapter(markerAdapter);
|
viewPager.setAdapter(markerAdapter);
|
||||||
viewBinding.makerDetailActivityViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
@@ -103,17 +100,16 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
|||||||
public void onPageScrollStateChanged(int state) {
|
public void onPageScrollStateChanged(int state) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
viewBinding.makerDetailActivityViewPager.setCurrentItem(markerIndex == -1 ? 0 : markerIndex);
|
viewPager.setCurrentItem(markerIndex == -1 ? 0 : markerIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = MarkerDetailActivityBinding.inflate(getLayoutInflater());
|
return R.layout.marker_detail_activity;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMarkerDeleted() {
|
public void onDeleteMarkerDone() {
|
||||||
runOnUiThread(this::finish);
|
runOnUiThread(this::finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,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(markerIds.get(position));
|
return MarkerDetailFragment.newInstance(waypointIds.get(position));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -142,7 +138,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return markerIds.size();
|
return waypointIds.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,10 @@ import android.view.Menu;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.AutoCompleteTextView;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -37,13 +41,10 @@ import androidx.annotation.NonNull;
|
|||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
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.databinding.MarkerEditBinding;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
@@ -65,125 +66,98 @@ 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 final TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||||
private Marker marker;
|
private Waypoint waypoint;
|
||||||
|
|
||||||
private MenuItem insertPhotoMenuItem;
|
private MenuItem insertPhotoMenuItem;
|
||||||
private MenuItem insertGalleryImgMenuItem;
|
private MenuItem insertGalleryImgMenuItem;
|
||||||
|
|
||||||
private Uri photoUri;
|
private Uri photoUri;
|
||||||
private Uri photoUriOriginal;
|
|
||||||
private List<Uri> photoUriDeleteList = new ArrayList<>();
|
|
||||||
private boolean hasCamera;
|
private boolean hasCamera;
|
||||||
|
|
||||||
private boolean isNewMarker;
|
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
private MarkerEditBinding viewBinding;
|
private EditText waypointName;
|
||||||
|
private AutoCompleteTextView waypointMarkerType;
|
||||||
|
private EditText waypointDescription;
|
||||||
|
private ImageView waypointPhoto;
|
||||||
|
private ImageView waypointDeletePhotoBtn;
|
||||||
|
|
||||||
@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);
|
||||||
Marker.Id markerId = getIntent().getParcelableExtra(EXTRA_MARKER_ID);
|
Waypoint.Id waypointId = 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
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.marker_types, android.R.layout.simple_dropdown_item_1line);
|
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||||
viewBinding.markerEditMarkerType.setAdapter(adapter);
|
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
||||||
viewBinding.markerEditPhotoDelete.setOnClickListener(v -> {
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||||
if (marker != null && marker.hasPhoto()) {
|
waypointMarkerType.setAdapter(adapter);
|
||||||
marker.setPhotoUrl(null);
|
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
|
||||||
|
waypointPhoto = findViewById(R.id.marker_edit_waypoint_photo);
|
||||||
|
|
||||||
|
waypointDeletePhotoBtn = findViewById(R.id.marker_edit_waypoint_photo_delete);
|
||||||
|
waypointDeletePhotoBtn.setOnClickListener(v -> {
|
||||||
|
if (waypoint != null && waypoint.hasPhoto()) {
|
||||||
|
waypoint.setPhotoUrl(null);
|
||||||
}
|
}
|
||||||
viewBinding.markerEditPhoto.setImageBitmap(null);
|
waypointPhoto.setImageBitmap(null);
|
||||||
photoUriDeleteList.add(photoUri);
|
|
||||||
photoUriOriginal = photoUriOriginal == null ? photoUri : photoUriOriginal;
|
|
||||||
photoUri = null;
|
photoUri = null;
|
||||||
hideAndShowOptions();
|
hideAndShowOptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
viewBinding.markerEditCancel.setOnClickListener(v -> {
|
Button cancel = findViewById(R.id.marker_edit_cancel);
|
||||||
Track.Id trackId = getTrackId();
|
cancel.setOnClickListener(v -> finish());
|
||||||
if (isNewMarker && trackId != null) {
|
|
||||||
// new marker and user cancel -> delete all photos from track directory
|
|
||||||
for (Uri photoUriDelete : photoUriDeleteList) {
|
|
||||||
File photoFile = FileUtils.getPhotoFileIfExists(this, trackId, photoUriDelete);
|
|
||||||
FileUtils.deleteDirectoryRecurse(photoFile);
|
|
||||||
}
|
|
||||||
if (photoUri != null) {
|
|
||||||
File photoFile = FileUtils.getPhotoFileIfExists(this, trackId, photoUri);
|
|
||||||
FileUtils.deleteDirectoryRecurse(photoFile);
|
|
||||||
}
|
|
||||||
} else if (!isNewMarker && trackId != null) {
|
|
||||||
// no new marker, user cancel and photo was changed -> delete all photos but original one
|
|
||||||
for (Uri photoUri : photoUriDeleteList) {
|
|
||||||
if (photoUriOriginal != photoUri) {
|
|
||||||
File photoFile = FileUtils.getPhotoFileIfExists(this, trackId, photoUri);
|
|
||||||
FileUtils.deleteDirectoryRecurse(photoFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (photoUri != null && photoUri != photoUriOriginal) {
|
|
||||||
File photoFile = FileUtils.getPhotoFileIfExists(this, trackId, photoUri);
|
|
||||||
FileUtils.deleteDirectoryRecurse(photoFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
isNewMarker = markerId == null;
|
final boolean isNewMarker = waypointId == null;
|
||||||
|
|
||||||
setTitle(isNewMarker ? R.string.menu_insert_marker : R.string.menu_edit);
|
setTitle(isNewMarker ? R.string.menu_insert_marker : R.string.menu_edit);
|
||||||
viewBinding.markerEditDone.setText(isNewMarker ? R.string.generic_add : R.string.generic_save);
|
Button done = findViewById(R.id.marker_edit_done);
|
||||||
viewBinding.markerEditDone.setOnClickListener(v -> {
|
done.setText(isNewMarker ? R.string.generic_add : R.string.generic_save);
|
||||||
|
done.setOnClickListener(v -> {
|
||||||
if (isNewMarker) {
|
if (isNewMarker) {
|
||||||
addMarker();
|
addMarker();
|
||||||
} else {
|
} else {
|
||||||
saveMarker();
|
saveMarker();
|
||||||
}
|
}
|
||||||
Track.Id trackId = getTrackId();
|
|
||||||
if (trackId == null) {
|
|
||||||
for (Uri photoUri : photoUriDeleteList) {
|
|
||||||
File photoFile = FileUtils.getPhotoFileIfExists(this, trackId, photoUri);
|
|
||||||
FileUtils.deleteDirectoryRecurse(photoFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isNewMarker) {
|
if (isNewMarker) {
|
||||||
int nextMarkerNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextMarkerNumber(trackId);
|
int nextWaypointNumber = trackId == null ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId);
|
||||||
if (nextMarkerNumber == -1) {
|
if (nextWaypointNumber == -1) {
|
||||||
nextMarkerNumber = 0;
|
nextWaypointNumber = 0;
|
||||||
}
|
}
|
||||||
viewBinding.markerEditName.setText(getString(R.string.marker_name_format, nextMarkerNumber));
|
waypointName.setText(getString(R.string.marker_name_format, nextWaypointNumber));
|
||||||
viewBinding.markerEditName.selectAll();
|
waypointName.selectAll();
|
||||||
viewBinding.markerEditMarkerType.setText("");
|
waypointMarkerType.setText("");
|
||||||
viewBinding.markerEditDescription.setText("");
|
waypointDescription.setText("");
|
||||||
} else {
|
} else {
|
||||||
marker = new ContentProviderUtils(this).getMarker(markerId);
|
waypoint = new ContentProviderUtils(this).getWaypoint(waypointId);
|
||||||
if (marker == null) {
|
if (waypoint == null) {
|
||||||
Log.d(TAG, "marker is null");
|
Log.d(TAG, "waypoint is null");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
viewBinding.markerEditName.setText(marker.getName());
|
waypointName.setText(waypoint.getName());
|
||||||
viewBinding.markerEditMarkerType.setText(marker.getCategory());
|
waypointMarkerType.setText(waypoint.getCategory());
|
||||||
viewBinding.markerEditDescription.setText(marker.getDescription());
|
waypointDescription.setText(waypoint.getDescription());
|
||||||
if (marker.hasPhoto()) {
|
if (waypoint.hasPhoto()) {
|
||||||
photoUri = marker.getPhotoURI();
|
photoUri = waypoint.getPhotoURI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI);
|
photoUri = savedInstanceState.getParcelable(BUNDLE_PHOTO_URI);
|
||||||
if (marker != null) {
|
if (waypoint != null) {
|
||||||
marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
|
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (photoUri != null) {
|
if (photoUri != null) {
|
||||||
setMarkerImageView(photoUri);
|
setWaypointImageView(photoUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
hideAndShowOptions();
|
hideAndShowOptions();
|
||||||
@@ -208,9 +182,8 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = MarkerEditBinding.inflate(getLayoutInflater());
|
return R.layout.marker_edit;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -229,10 +202,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:
|
||||||
createMarkerWithPicture();
|
createWaypointWithPicture();
|
||||||
return true;
|
return true;
|
||||||
case R.id.marker_edit_insert_gallery_img:
|
case R.id.marker_edit_insert_gallery_img:
|
||||||
createMarkerWithGalleryImage();
|
createWaypointWithGalleryImage();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@@ -246,7 +219,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) {
|
||||||
setMarkerImageView(photoUri);
|
setWaypointImageView(photoUri);
|
||||||
}
|
}
|
||||||
} else if (requestCode == GALLERY_IMG_REQUEST_CODE) {
|
} else if (requestCode == GALLERY_IMG_REQUEST_CODE) {
|
||||||
if (resultCode == RESULT_CANCELED) {
|
if (resultCode == RESULT_CANCELED) {
|
||||||
@@ -260,7 +233,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
FileUtils.copy(srcFd, dstFile);
|
FileUtils.copy(srcFd, dstFile);
|
||||||
|
|
||||||
photoUri = FileUtils.getUriForFile(this, dstFile);
|
photoUri = FileUtils.getUriForFile(this, dstFile);
|
||||||
setMarkerImageView(photoUri);
|
setWaypointImageView(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();
|
||||||
@@ -277,26 +250,31 @@ 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 = (marker != null && marker.hasPhoto()) || photoUri != null;
|
boolean isPhotoSet = (waypoint != null && waypoint.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);
|
||||||
}
|
}
|
||||||
viewBinding.markerEditPhotoDelete.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE);
|
waypointDeletePhotoBtn.setVisibility(isPhotoSet ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the trackId; either from track or marker.
|
* Returns the trackId. If trackId == -1 then get it from waypoint object.
|
||||||
*/
|
*/
|
||||||
private Track.Id getTrackId() {
|
private Track.Id getTrackId() {
|
||||||
return trackId == null ? marker.getTrackId() : trackId;
|
return trackId == null ? waypoint.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);
|
||||||
viewBinding.markerEditPhoto.setImageBitmap(bitmap);
|
waypointPhoto.setImageBitmap(bitmap);
|
||||||
hideAndShowOptions();
|
hideAndShowOptions();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, e.getMessage());
|
Log.e(TAG, e.getMessage());
|
||||||
@@ -304,31 +282,31 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMarkerWithPicture() {
|
private void createWaypointWithPicture() {
|
||||||
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 createMarkerWithGalleryImage() {
|
private void createWaypointWithGalleryImage() {
|
||||||
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,
|
||||||
viewBinding.markerEditName.getText().toString(),
|
waypointName.getText().toString(),
|
||||||
viewBinding.markerEditMarkerType.getText().toString(),
|
waypointMarkerType.getText().toString(),
|
||||||
viewBinding.markerEditDescription.getText().toString(),
|
waypointDescription.getText().toString(),
|
||||||
photoUri != null ? photoUri.toString() : null);
|
photoUri != null ? photoUri.toString() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveMarker() {
|
private void saveMarker() {
|
||||||
marker.setName(viewBinding.markerEditName.getText().toString());
|
waypoint.setName(waypointName.getText().toString());
|
||||||
marker.setCategory(viewBinding.markerEditMarkerType.getText().toString());
|
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||||
marker.setDescription(viewBinding.markerEditDescription.getText().toString());
|
waypoint.setDescription(waypointDescription.getText().toString());
|
||||||
marker.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
|
waypoint.setPhotoUrl(photoUri != null ? photoUri.toString() : null);
|
||||||
|
|
||||||
new ContentProviderUtils(this).updateMarker(this, marker);
|
new ContentProviderUtils(this).updateWaypoint(this, waypoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.app.SearchManager;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -27,19 +26,18 @@ import android.view.KeyEvent;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.SearchView;
|
|
||||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
import androidx.loader.content.CursorLoader;
|
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.databinding.MarkerListBinding;
|
|
||||||
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;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
@@ -69,6 +67,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
|
// Note that the key can be null
|
||||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
|
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(MarkerListActivity.this);
|
recordingTrackId = PreferencesUtils.getRecordingTrackId(MarkerListActivity.this);
|
||||||
}
|
}
|
||||||
@@ -82,11 +81,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
};
|
};
|
||||||
private Track track;
|
private Track track;
|
||||||
private ResourceCursorAdapter resourceCursorAdapter;
|
private ResourceCursorAdapter resourceCursorAdapter;
|
||||||
|
// UI elements
|
||||||
private MarkerListBinding viewBinding;
|
private ListView listView;
|
||||||
|
|
||||||
private final MarkerLoaderCallback loaderCallbacks = new MarkerLoaderCallback();
|
|
||||||
|
|
||||||
// Callback when an item is selected in the contextual action mode
|
// Callback when an item is selected in the contextual action mode
|
||||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -124,20 +120,23 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
|
|
||||||
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
|
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
|
||||||
|
|
||||||
viewBinding.markerList.setEmptyView(viewBinding.markerListEmpty);
|
listView = findViewById(R.id.marker_list);
|
||||||
viewBinding.markerList.setOnItemClickListener((parent, view, position, id) -> {
|
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
||||||
|
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 Marker.Id(id));
|
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Waypoint.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(MarkerColumns.NAME);
|
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME);
|
||||||
int timeIndex = cursor.getColumnIndexOrThrow(MarkerColumns.TIME);
|
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||||
int categoryIndex = cursor.getColumnIndex(MarkerColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY);
|
||||||
int descriptionIndex = cursor.getColumnIndex(MarkerColumns.DESCRIPTION);
|
int descriptionIndex = cursor.getColumnIndex(WaypointsColumns.DESCRIPTION);
|
||||||
int photoUrlIndex = cursor.getColumnIndex(MarkerColumns.PHOTOURL);
|
int photoUrlIndex = cursor.getColumnIndex(WaypointsColumns.PHOTOURL);
|
||||||
|
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE);
|
||||||
|
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE);
|
||||||
|
|
||||||
int iconId = MarkerUtils.ICON_ID;
|
int iconId = MarkerUtils.ICON_ID;
|
||||||
String name = cursor.getString(nameIndex);
|
String name = cursor.getString(nameIndex);
|
||||||
@@ -145,14 +144,33 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
String category = cursor.getString(categoryIndex);
|
String category = cursor.getString(categoryIndex);
|
||||||
String description = cursor.getString(descriptionIndex);
|
String description = cursor.getString(descriptionIndex);
|
||||||
String photoUrl = cursor.getString(photoUrlIndex);
|
String photoUrl = cursor.getString(photoUrlIndex);
|
||||||
|
//TODO also show latitude and longitude in list
|
||||||
|
double latitude = cursor.getDouble(latitudeIndex);
|
||||||
|
double longitude = cursor.getDouble(longitudeIndex);
|
||||||
|
|
||||||
ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl);
|
ListItemUtils.setListItem(MarkerListActivity.this, view, false, true, iconId, R.string.image_marker, name, null, null, 0, time, false, category, description, photoUrl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
viewBinding.markerList.setAdapter(resourceCursorAdapter);
|
listView.setAdapter(resourceCursorAdapter);
|
||||||
ActivityUtils.configureListViewContextualMenu(viewBinding.markerList, contextualActionModeCallback);
|
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||||
|
|
||||||
loadData(getIntent());
|
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||||
|
return ContentProviderUtils.getWaypointsLoader(MarkerListActivity.this, track != null ? track.getId() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||||
|
resourceCursorAdapter.swapCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||||
|
resourceCursorAdapter.swapCursor(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -175,9 +193,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = MarkerListBinding.inflate(getLayoutInflater());
|
return R.layout.marker_list;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -212,36 +229,39 @@ 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 longMarkerIds the marker ids
|
* @param longWaypointIds the marker ids
|
||||||
* @return true if handled.
|
* @return true if handled.
|
||||||
*/
|
*/
|
||||||
private boolean handleContextItem(int itemId, long... longMarkerIds) {
|
private boolean handleContextItem(int itemId, long... longWaypointIds) {
|
||||||
Marker.Id[] markerIds = new Marker.Id[longMarkerIds.length];
|
Waypoint.Id[] waypointIds = new Waypoint.Id[longWaypointIds.length];
|
||||||
for (int i = 0; i < longMarkerIds.length; i++) {
|
for (int i = 0; i < longWaypointIds.length; i++) {
|
||||||
markerIds[i] = new Marker.Id(longMarkerIds[i]);
|
waypointIds[i] = new Waypoint.Id(longWaypointIds[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 (markerIds.length == 1) {
|
if (waypointIds.length == 1) {
|
||||||
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getMarker(markerIds[0]));
|
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getWaypoint(waypointIds[0]));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.list_context_menu_edit:
|
case R.id.list_context_menu_edit:
|
||||||
if (markerIds.length == 1) {
|
if (waypointIds.length == 1) {
|
||||||
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||||
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, markerIds[0]);
|
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointIds[0]);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
case R.id.list_context_menu_delete:
|
case R.id.list_context_menu_delete:
|
||||||
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerIds);
|
if (waypointIds.length > 1 && waypointIds.length == listView.getCount()) {
|
||||||
|
waypointIds = null;
|
||||||
|
}
|
||||||
|
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointIds);
|
||||||
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 < viewBinding.markerList.getCount(); i++) {
|
for (int i = 0; i < listView.getCount(); i++) {
|
||||||
viewBinding.markerList.setItemChecked(i, true);
|
listView.setItemChecked(i, true);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
@@ -258,92 +278,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onDeleteMarkerDone() {
|
||||||
if (loaderCallbacks.getSearchQuery() != null) {
|
|
||||||
loaderCallbacks.setSearch(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
|
||||||
if (!searchView.isIconified()) {
|
|
||||||
searchView.setIconified(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
setIntent(intent);
|
|
||||||
loadData(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadData(Intent intent) {
|
|
||||||
String searchQuery = null;
|
|
||||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
|
||||||
searchQuery = intent.getStringExtra(SearchManager.QUERY);
|
|
||||||
}
|
|
||||||
|
|
||||||
loaderCallbacks.setSearch(searchQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMarkerDeleted() {
|
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MarkerLoaderCallback implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
|
|
||||||
private String searchQuery = null;
|
|
||||||
|
|
||||||
public String getSearchQuery() {
|
|
||||||
return searchQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearch(String searchQuery) {
|
|
||||||
this.searchQuery = searchQuery;
|
|
||||||
restart();
|
|
||||||
if (searchQuery != null) {
|
|
||||||
setTitle(searchQuery);
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.menu_markers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restart() {
|
|
||||||
LoaderManager.getInstance(MarkerListActivity.this).restartLoader(0, null, loaderCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
|
||||||
final String[] PROJECTION = new String[]{MarkerColumns._ID,
|
|
||||||
MarkerColumns.NAME, MarkerColumns.DESCRIPTION, MarkerColumns.CATEGORY,
|
|
||||||
MarkerColumns.TIME, MarkerColumns.PHOTOURL};
|
|
||||||
|
|
||||||
if (searchQuery == null) {
|
|
||||||
if (track != null) {
|
|
||||||
return new CursorLoader(MarkerListActivity.this, MarkerColumns.CONTENT_URI, PROJECTION, MarkerColumns.TRACKID + "=?", new String[]{String.valueOf(track.getId().getId())}, null);
|
|
||||||
} else {
|
|
||||||
return new CursorLoader(MarkerListActivity.this, MarkerColumns.CONTENT_URI, PROJECTION, null, null, null);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final String SEARCH_QUERY = MarkerColumns.NAME + " LIKE ? OR " +
|
|
||||||
MarkerColumns.DESCRIPTION + " LIKE ? OR " +
|
|
||||||
MarkerColumns.CATEGORY + " LIKE ?";
|
|
||||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
|
||||||
return new CursorLoader(MarkerListActivity.this, MarkerColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, MarkerColumns.DEFAULT_SORT_ORDER + " DESC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
|
||||||
resourceCursorAdapter.swapCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
|
||||||
resourceCursorAdapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,450 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
|
import android.app.SearchManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.SearchEngine;
|
||||||
|
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||||
|
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||||
|
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.fragments.ConfirmDeleteDialogFragment;
|
||||||
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||||
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
|
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||||
|
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||||
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An activity to display a list of searchable results.
|
||||||
|
*
|
||||||
|
* @author Rodrigo Damazio
|
||||||
|
* <p>
|
||||||
|
* TODO: allow to refine searchable (present searchable in context menu)
|
||||||
|
*/
|
||||||
|
public class SearchListActivity extends AbstractListActivity implements DeleteMarkerCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||||
|
|
||||||
|
private static final String TAG = SearchListActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String IS_RECORDING_FIELD = "isRecording";
|
||||||
|
private static final String IS_PAUSED_FIELD = "isPaused";
|
||||||
|
private static final String ICON_ID_FIELD = "icon";
|
||||||
|
private static final String ICON_CONTENT_DESCRIPTION_ID_FIELD = "iconContentDescription";
|
||||||
|
private static final String NAME_FIELD = "name";
|
||||||
|
private static final String TOTAL_TIME_FIELD = "totalTime";
|
||||||
|
private static final String TOTAL_DISTANCE_FIELD = "totalDistance";
|
||||||
|
private static final String MARKER_COUNT_FIELD = "markerCount";
|
||||||
|
private static final String START_TIME_FIELD = "startTime";
|
||||||
|
private static final String CATEGORY_FIELD = "category";
|
||||||
|
private static final String DESCRIPTION_FIELD = "description";
|
||||||
|
private static final String PHOTO_URL_FIELD = "photoUrl";
|
||||||
|
private static final String MARKER_LATITUDE_FIELD = "latitude";
|
||||||
|
private static final String MARKER_LONGITUDE_FIELD = "longitude";
|
||||||
|
private static final String TRACK_ID_FIELD = "trackId";
|
||||||
|
private static final String MARKER_ID_FIELD = "markerId";
|
||||||
|
|
||||||
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
|
||||||
|
private SearchEngine searchEngine;
|
||||||
|
|
||||||
|
private ArrayAdapter<Map<String, Object>> arrayAdapter;
|
||||||
|
|
||||||
|
private boolean metricUnits = true;
|
||||||
|
|
||||||
|
private Track.Id recordingTrackId;
|
||||||
|
|
||||||
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
|
// Callback when an item is selected in the contextual action mode
|
||||||
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||||
|
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
||||||
|
boolean isSingleSelection = positions.length == 1;
|
||||||
|
|
||||||
|
boolean isSingleSelectionTrack;
|
||||||
|
if (isSingleSelection) {
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
||||||
|
isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null;
|
||||||
|
} else {
|
||||||
|
isSingleSelectionTrack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not recording, one item, item is a track
|
||||||
|
MenuItem shareMenuItem = menu.findItem(R.id.list_context_menu_share);
|
||||||
|
if (isSingleSelectionTrack) {
|
||||||
|
shareMenuItem.setVisible(!isRecording);
|
||||||
|
}
|
||||||
|
|
||||||
|
// One item, item is a marker
|
||||||
|
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(isSingleSelection && !isSingleSelectionTrack);
|
||||||
|
// One item, can be a track or a marker
|
||||||
|
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||||
|
// One item. If track, no restriction.
|
||||||
|
menu.findItem(R.id.list_context_menu_delete).setVisible(isSingleSelection && isSingleSelectionTrack);
|
||||||
|
// Disable select all, no action is available for multiple selection
|
||||||
|
menu.findItem(R.id.list_context_menu_select_all).setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||||
|
return handleContextItem(itemId, positions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
|
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.stats_units_key, key)) {
|
||||||
|
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
|
||||||
|
}
|
||||||
|
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.recording_track_id_key, key)) {
|
||||||
|
recordingTrackId = PreferencesUtils.getRecordingTrackId(SearchListActivity.this);
|
||||||
|
}
|
||||||
|
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||||
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(SearchListActivity.this);
|
||||||
|
}
|
||||||
|
if (key != null) {
|
||||||
|
runOnUiThread(() -> arrayAdapter.notifyDataSetChanged());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
|
|
||||||
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||||
|
|
||||||
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||||
|
searchEngine = new SearchEngine(contentProviderUtils);
|
||||||
|
|
||||||
|
arrayAdapter = new ArrayAdapter<Map<String, Object>>(this, R.layout.list_item, R.id.list_item_name) {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, @NonNull android.view.ViewGroup parent) {
|
||||||
|
View view = convertView;
|
||||||
|
if (convertView == null) {
|
||||||
|
view = getLayoutInflater().inflate(R.layout.list_item, parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> resultMap = getItem(position);
|
||||||
|
boolean isRecording = (boolean) resultMap.get(IS_RECORDING_FIELD);
|
||||||
|
boolean isPaused = (boolean) resultMap.get(IS_PAUSED_FIELD);
|
||||||
|
int iconId = (int) resultMap.get(ICON_ID_FIELD);
|
||||||
|
int iconContentDescriptionId = (int) resultMap.get(ICON_CONTENT_DESCRIPTION_ID_FIELD);
|
||||||
|
String name = (String) resultMap.get(NAME_FIELD);
|
||||||
|
String totalTime = (String) resultMap.get(TOTAL_TIME_FIELD);
|
||||||
|
String totalDistance = (String) resultMap.get(TOTAL_DISTANCE_FIELD);
|
||||||
|
int markerCount = (int) resultMap.get(MARKER_COUNT_FIELD);
|
||||||
|
long startTime = (long) resultMap.get(START_TIME_FIELD);
|
||||||
|
String category = (String) resultMap.get(CATEGORY_FIELD);
|
||||||
|
String description = (String) resultMap.get(DESCRIPTION_FIELD);
|
||||||
|
String photoUrl = (String) resultMap.get(PHOTO_URL_FIELD);
|
||||||
|
|
||||||
|
ListItemUtils.setListItem(SearchListActivity.this, view, isRecording, isPaused, iconId,
|
||||||
|
iconContentDescriptionId, name, totalTime, totalDistance, markerCount,
|
||||||
|
startTime, false, category, description, photoUrl);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// UI elements
|
||||||
|
ListView listView = findViewById(R.id.search_list);
|
||||||
|
listView.setAdapter(arrayAdapter);
|
||||||
|
listView.setEmptyView(findViewById(R.id.search_list_empty));
|
||||||
|
listView.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(position);
|
||||||
|
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
|
||||||
|
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
||||||
|
Intent intent = IntentUtils.newIntent(SearchListActivity.this, TrackRecordedActivity.class);
|
||||||
|
if (markerId != null) {
|
||||||
|
intent = intent.putExtra(TrackRecordedActivity.EXTRA_MARKER_ID, markerId);
|
||||||
|
} else {
|
||||||
|
intent = intent.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
}
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||||
|
handleIntent(getIntent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||||
|
trackRecordingServiceConnection.startConnection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
arrayAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
trackRecordingServiceConnection.unbind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLayoutResId() {
|
||||||
|
return R.layout.search_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
handleIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a context item selection.
|
||||||
|
*
|
||||||
|
* @param itemId the menu item id
|
||||||
|
* @param positions the positions of the selected rows
|
||||||
|
* @return true if handled.
|
||||||
|
*/
|
||||||
|
private boolean handleContextItem(int itemId, int[] positions) {
|
||||||
|
if (positions.length != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
||||||
|
Track.Id trackId = (Track.Id) item.get(TRACK_ID_FIELD);
|
||||||
|
Waypoint.Id waypointId = (Waypoint.Id) item.get(MARKER_ID_FIELD);
|
||||||
|
Intent intent;
|
||||||
|
switch (itemId) {
|
||||||
|
case R.id.list_context_menu_show_on_map:
|
||||||
|
IntentUtils.showCoordinateOnMap(this, (double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_share:
|
||||||
|
intent = IntentUtils.newShareFileIntent(this, trackId);
|
||||||
|
intent = Intent.createChooser(intent, null);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_edit:
|
||||||
|
if (waypointId != null) {
|
||||||
|
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||||
|
.putExtra(MarkerEditActivity.EXTRA_MARKER_ID, waypointId);
|
||||||
|
} else {
|
||||||
|
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
||||||
|
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
}
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
// Close the searchable result since its content can change after edit.
|
||||||
|
finish();
|
||||||
|
return true;
|
||||||
|
case R.id.list_context_menu_delete:
|
||||||
|
if (waypointId != null) {
|
||||||
|
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), waypointId);
|
||||||
|
} else {
|
||||||
|
deleteTracks(trackId);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the intent.
|
||||||
|
*
|
||||||
|
* @param intent the intent
|
||||||
|
*/
|
||||||
|
private void handleIntent(Intent intent) {
|
||||||
|
if (!Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||||
|
Log.e(TAG, "Invalid intent action: " + intent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String textQuery = intent.getStringExtra(SearchManager.QUERY);
|
||||||
|
setTitle(textQuery);
|
||||||
|
|
||||||
|
doSearch(textQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do the searchable.
|
||||||
|
*
|
||||||
|
* @param textQuery the query
|
||||||
|
*/
|
||||||
|
private void doSearch(String textQuery) {
|
||||||
|
SearchQuery query = new SearchQuery(textQuery, null, null, System.currentTimeMillis());
|
||||||
|
SortedSet<ScoredResult> scoredResults = searchEngine.search(query);
|
||||||
|
final List<Map<String, Object>> displayResults = prepareResultsforDisplay(scoredResults);
|
||||||
|
|
||||||
|
// Use the UI thread to display the results
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
arrayAdapter.clear();
|
||||||
|
arrayAdapter.addAll(displayResults);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the result for display.
|
||||||
|
*
|
||||||
|
* @param scoredResults a list of score results
|
||||||
|
* @return a list of result maps
|
||||||
|
*/
|
||||||
|
private List<Map<String, Object>> prepareResultsforDisplay(Collection<ScoredResult> scoredResults) {
|
||||||
|
//TODO Replace use of map<string, object>, but rather provide Track or Waypoint directly.
|
||||||
|
ArrayList<Map<String, Object>> output = new ArrayList<>(scoredResults.size());
|
||||||
|
for (ScoredResult result : scoredResults) {
|
||||||
|
if (result.track != null) {
|
||||||
|
output.add(prepareTrackForDisplay(result.track));
|
||||||
|
} else {
|
||||||
|
output.add(prepareMarkerForDisplay(result.waypoint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<>();
|
||||||
|
|
||||||
|
//TODO: It may be more appropriate to obtain the track name as a join in the retrieval phase of the searchable.
|
||||||
|
String trackName = null;
|
||||||
|
Track.Id trackId = waypoint.getTrackId();
|
||||||
|
if (!trackId.isValid()) {
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
if (track != null) {
|
||||||
|
trackName = track.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap.put(IS_RECORDING_FIELD, false);
|
||||||
|
resultMap.put(IS_PAUSED_FIELD, true);
|
||||||
|
resultMap.put(ICON_ID_FIELD, MarkerUtils.ICON_ID);
|
||||||
|
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_marker);
|
||||||
|
resultMap.put(NAME_FIELD, waypoint.getName());
|
||||||
|
// 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_DISTANCE_FIELD, null);
|
||||||
|
resultMap.put(MARKER_COUNT_FIELD, 0);
|
||||||
|
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime());
|
||||||
|
resultMap.put(CATEGORY_FIELD, waypoint.getCategory());
|
||||||
|
resultMap.put(DESCRIPTION_FIELD, waypoint.getDescription());
|
||||||
|
resultMap.put(PHOTO_URL_FIELD, waypoint.getPhotoUrl());
|
||||||
|
resultMap.put(TRACK_ID_FIELD, waypoint.getTrackId());
|
||||||
|
resultMap.put(MARKER_ID_FIELD, waypoint.getId());
|
||||||
|
|
||||||
|
resultMap.put(MARKER_LATITUDE_FIELD, waypoint.getLocation().getLatitude());
|
||||||
|
resultMap.put(MARKER_LONGITUDE_FIELD, waypoint.getLocation().getLongitude());
|
||||||
|
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a track for display by filling in a result map.
|
||||||
|
*
|
||||||
|
* @param track the track
|
||||||
|
* @return the result map
|
||||||
|
*/
|
||||||
|
private Map<String, Object> prepareTrackForDisplay(Track track) {
|
||||||
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
|
||||||
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
|
String icon = track.getIcon();
|
||||||
|
String category = icon != null && !icon.equals("") ? null : track.getCategory();
|
||||||
|
|
||||||
|
resultMap.put(IS_RECORDING_FIELD, track.getId() == recordingTrackId);
|
||||||
|
resultMap.put(IS_PAUSED_FIELD, recordingTrackPaused);
|
||||||
|
resultMap.put(ICON_ID_FIELD, TrackIconUtils.getIconDrawable(icon));
|
||||||
|
resultMap.put(ICON_CONTENT_DESCRIPTION_ID_FIELD, R.string.image_track);
|
||||||
|
resultMap.put(NAME_FIELD, track.getName());
|
||||||
|
resultMap.put(TOTAL_TIME_FIELD, StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
|
||||||
|
resultMap.put(TOTAL_DISTANCE_FIELD, StringUtils.formatDistance(this, trackStatistics.getTotalDistance(), metricUnits));
|
||||||
|
resultMap.put(MARKER_COUNT_FIELD, contentProviderUtils.getWaypointCount(track.getId()));
|
||||||
|
resultMap.put(START_TIME_FIELD, trackStatistics.getStartTime_ms());
|
||||||
|
resultMap.put(CATEGORY_FIELD, category);
|
||||||
|
resultMap.put(DESCRIPTION_FIELD, track.getDescription());
|
||||||
|
resultMap.put(PHOTO_URL_FIELD, null);
|
||||||
|
resultMap.put(TRACK_ID_FIELD, track.getId());
|
||||||
|
resultMap.put(MARKER_ID_FIELD, null);
|
||||||
|
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteMarkerDone() {
|
||||||
|
runOnUiThread(() -> handleIntent(getIntent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TrackRecordingServiceConnection getTrackRecordingServiceConnection() {
|
||||||
|
return trackRecordingServiceConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDeleted() {
|
||||||
|
runOnUiThread(() -> handleIntent(getIntent()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ 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;
|
||||||
@@ -30,7 +31,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());
|
||||||
|
|
||||||
showTrackfileFormat(this, trackFileFormat, trackIds);
|
showTrackTrackfileFormat(this, trackFileFormat, trackIds);
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@@ -41,14 +42,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 showTrackfileFormat(Context context, TrackFileFormat trackFileFormat, Set<Track.Id> trackIds) {
|
private static void showTrackTrackfileFormat(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,7 +6,6 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,8 +32,6 @@ 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.");
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.drawable.TransitionDrawable;
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.databinding.TrackControllerBinding;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
@@ -37,132 +42,137 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public class TrackController implements View.OnTouchListener {
|
public class TrackController {
|
||||||
|
|
||||||
private static final String TAG = TrackController.class.getSimpleName();
|
private static final String TAG = TrackController.class.getSimpleName();
|
||||||
|
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private final Handler handlerUpdateTotalTime = new Handler();
|
private final Handler handlerUpdateTotalTime;
|
||||||
|
private final View containerView;
|
||||||
|
private final TextView statusTextView;
|
||||||
|
private final TextView totalTimeTextView;
|
||||||
|
private final ImageButton recordImageButton;
|
||||||
|
private final ImageButton stopImageButton;
|
||||||
private final boolean alwaysShow;
|
private final boolean alwaysShow;
|
||||||
|
private ButtonDelay buttonDelay;
|
||||||
private Runnable buttonDelay;
|
|
||||||
private final int buttonDelayDuration;
|
|
||||||
|
|
||||||
private final TrackControllerBinding viewBinding;
|
|
||||||
private final TransitionDrawable transition;
|
|
||||||
|
|
||||||
private boolean isRecording;
|
private boolean isRecording;
|
||||||
private boolean isPaused;
|
private boolean isPaused;
|
||||||
private long totalTime = 0;
|
private long totalTime = 0;
|
||||||
private long totalTimeTimestamp = 0;
|
|
||||||
private boolean isResumed = false;
|
|
||||||
|
|
||||||
private final Callback callback;
|
private long totalTimeTimestamp = 0;
|
||||||
|
|
||||||
|
private boolean isResumed = false;
|
||||||
|
|
||||||
// A runnable to update the total time.
|
// A runnable to update the total time.
|
||||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed && isRecording && !isPaused) {
|
if (isResumed && isRecording && !isPaused) {
|
||||||
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
||||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
|
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
TrackController(Activity activity, TrackControllerBinding viewBinding, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, Callback callback) {
|
TrackController(Activity activity, TrackRecordingServiceConnection trackRecordingServiceConnection, boolean alwaysShow, OnClickListener recordListener, OnClickListener stopListener) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.viewBinding = viewBinding;
|
|
||||||
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
this.trackRecordingServiceConnection = trackRecordingServiceConnection;
|
||||||
this.alwaysShow = alwaysShow;
|
this.alwaysShow = alwaysShow;
|
||||||
this.callback = callback;
|
handlerUpdateTotalTime = new Handler();
|
||||||
|
containerView = activity.findViewById(R.id.track_controller_container);
|
||||||
|
statusTextView = activity.findViewById(R.id.track_controller_status);
|
||||||
|
totalTimeTextView = activity.findViewById(R.id.track_controller_total_time);
|
||||||
|
|
||||||
buttonDelayDuration = activity.getResources().getInteger(R.integer.buttonDelayMillis);
|
recordImageButton = activity.findViewById(R.id.track_controller_record);
|
||||||
|
recordImageButton.setOnTouchListener((view, motionEvent) -> onRecordTouch(activity, recordListener, motionEvent));
|
||||||
|
|
||||||
viewBinding.trackControllerRecord.setOnTouchListener(this);
|
stopImageButton = activity.findViewById(R.id.track_controller_stop);
|
||||||
viewBinding.trackControllerRecord.setOnClickListener((view) -> {
|
stopImageButton.setOnTouchListener((view, motionEvent) -> onStopTouch(activity, stopListener, motionEvent));
|
||||||
if (buttonDelay != null || (isRecording && !isPaused)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback.recordStart();
|
|
||||||
});
|
|
||||||
|
|
||||||
viewBinding.trackControllerStop.setOnTouchListener(this);
|
|
||||||
transition = (TransitionDrawable) viewBinding.trackControllerContainer.getBackground();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility") //We do accessibility manually.
|
private boolean onRecordTouch(final Activity activity, final OnClickListener recordListener, final MotionEvent motionEvent) {
|
||||||
@Override
|
if (isRecording && !isPaused) {
|
||||||
public boolean onTouch(final View view, MotionEvent event) {
|
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
buttonDelay = new ButtonDelay(activity, recordImageButton, R.drawable.ic_button_pause_anim, R.string.hold_to_pause, recordListener);
|
||||||
return false;
|
new Thread(buttonDelay).start();
|
||||||
}
|
return true;
|
||||||
|
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
|
||||||
if (buttonDelay != null && event.getAction() == MotionEvent.ACTION_UP) {
|
recordImageButton.setImageResource(R.drawable.ic_button_pause);
|
||||||
handlerUpdateTotalTime.removeCallbacks(buttonDelay);
|
if (buttonDelay != null) {
|
||||||
buttonDelay = null;
|
buttonDelay.canceled = true;
|
||||||
((ImageButton) view).setPressed(false);
|
}
|
||||||
|
return true;
|
||||||
transition.resetTransition();
|
}
|
||||||
|
} else if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
showStatusSetDefaultText();
|
recordListener.onClick(null);
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Trigger button pressed animation
|
|
||||||
((ImageButton) view).setPressed(true);
|
|
||||||
|
|
||||||
//To pause a recording
|
|
||||||
if (viewBinding.trackControllerRecord.equals(view)
|
|
||||||
&& event.getAction() == MotionEvent.ACTION_DOWN
|
|
||||||
&& isRecording && !isPaused) {
|
|
||||||
|
|
||||||
transition.startTransition(buttonDelayDuration);
|
|
||||||
|
|
||||||
ActivityUtils.vibrate(activity, 150);
|
|
||||||
viewBinding.trackControllerStatus.setText(R.string.hold_to_pause);
|
|
||||||
|
|
||||||
buttonDelay = () -> {
|
|
||||||
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
|
||||||
view.performClick();
|
|
||||||
callback.recordStart(); //TODO Should be recordPause();
|
|
||||||
|
|
||||||
transition.resetTransition();
|
|
||||||
|
|
||||||
ActivityUtils.vibrate(activity, 1000);
|
|
||||||
};
|
|
||||||
handlerUpdateTotalTime.postDelayed(buttonDelay, buttonDelayDuration);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//To stop a recording
|
|
||||||
if (viewBinding.trackControllerStop.equals(view)
|
|
||||||
&& event.getAction() == MotionEvent.ACTION_DOWN
|
|
||||||
&& isRecording) {
|
|
||||||
|
|
||||||
|
|
||||||
transition.startTransition(buttonDelayDuration);
|
|
||||||
|
|
||||||
ActivityUtils.vibrate(activity, 150);
|
|
||||||
viewBinding.trackControllerStatus.setText(R.string.hold_to_stop);
|
|
||||||
|
|
||||||
buttonDelay = () -> {
|
|
||||||
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
|
||||||
callback.recordStop();
|
|
||||||
ActivityUtils.vibrate(activity, 1000);
|
|
||||||
|
|
||||||
transition.resetTransition();
|
|
||||||
};
|
|
||||||
handlerUpdateTotalTime.postDelayed(buttonDelay, buttonDelayDuration);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean onStopTouch(final Activity activity, final OnClickListener stopListener, final MotionEvent motionEvent) {
|
||||||
|
if (isRecording) {
|
||||||
|
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
|
buttonDelay = new ButtonDelay(activity, stopImageButton, R.drawable.ic_button_stop_anim, R.string.hold_to_stop, stopListener);
|
||||||
|
new Thread(buttonDelay).start();
|
||||||
|
return true;
|
||||||
|
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP ) {
|
||||||
|
stopImageButton.setImageResource(R.drawable.ic_button_stop);
|
||||||
|
if (buttonDelay != null) {
|
||||||
|
buttonDelay.canceled = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ButtonDelay implements Runnable {
|
||||||
|
|
||||||
|
private boolean canceled = false;
|
||||||
|
|
||||||
|
private final ImageButton imageButton;
|
||||||
|
private final Activity activity;
|
||||||
|
private final OnClickListener clickListener;
|
||||||
|
private final int delayMillis;
|
||||||
|
private final Drawable drawable;
|
||||||
|
private final int delayMessageId;
|
||||||
|
|
||||||
|
private ButtonDelay(final Activity activity, final ImageButton imageButton, final int animDrawableId, final int delayMessageId, final OnClickListener clickListener) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.clickListener = clickListener;
|
||||||
|
this.delayMillis = activity.getResources().getInteger(R.integer.buttonDelayMillis);
|
||||||
|
this.imageButton = imageButton;
|
||||||
|
this.drawable = ContextCompat.getDrawable(activity, animDrawableId);
|
||||||
|
this.delayMessageId = delayMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
activity.runOnUiThread(()->{
|
||||||
|
imageButton.setImageDrawable(drawable);
|
||||||
|
if (drawable instanceof AnimatedVectorDrawable) {
|
||||||
|
((AnimatedVectorDrawable) drawable).start();
|
||||||
|
}
|
||||||
|
ActivityUtils.vibrate(activity, 150);
|
||||||
|
ActivityUtils.toast(activity, delayMessageId, Toast.LENGTH_SHORT, Gravity.TOP);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(delayMillis);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
if (!canceled) {
|
||||||
|
activity.runOnUiThread(()-> {
|
||||||
|
clickListener.onClick(null);
|
||||||
|
ActivityUtils.vibrate(activity, 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void update(boolean recording, boolean paused) {
|
public void update(boolean recording, boolean paused) {
|
||||||
if (!isResumed) {
|
if (!isResumed) {
|
||||||
return;
|
return;
|
||||||
@@ -170,29 +180,30 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
isRecording = recording;
|
isRecording = recording;
|
||||||
isPaused = paused;
|
isPaused = paused;
|
||||||
boolean visible = alwaysShow || isRecording;
|
boolean visible = alwaysShow || isRecording;
|
||||||
viewBinding.trackControllerContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
|
containerView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackControllerRecord.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
recordImageButton.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
||||||
viewBinding.trackControllerRecord.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
recordImageButton.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
||||||
|
|
||||||
viewBinding.trackControllerStop.setEnabled(isRecording);
|
stopImageButton.setImageResource(isRecording ? R.drawable.ic_button_stop : R.drawable.ic_button_stop_disabled);
|
||||||
|
stopImageButton.setEnabled(isRecording);
|
||||||
|
|
||||||
viewBinding.trackControllerStatus.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
statusTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
viewBinding.trackControllerStatus.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
statusTextView.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
||||||
showStatusSetDefaultText();
|
statusTextView.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
viewBinding.trackControllerTotalTime.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
totalTimeTextView.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
totalTime = getTotalTime();
|
totalTime = getTotalTime();
|
||||||
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
totalTimeTimestamp = System.currentTimeMillis();
|
totalTimeTimestamp = System.currentTimeMillis();
|
||||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
||||||
@@ -211,11 +222,11 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void hide() {
|
public void hide() {
|
||||||
viewBinding.trackControllerContainer.setVisibility(View.GONE);
|
containerView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
viewBinding.trackControllerContainer.setVisibility(View.VISIBLE);
|
containerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -225,14 +236,4 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStatusSetDefaultText() {
|
|
||||||
viewBinding.trackControllerStatus.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
void recordStart();
|
|
||||||
|
|
||||||
void recordStop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import java.util.List;
|
|||||||
|
|
||||||
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.databinding.TrackDeleteBinding;
|
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,8 +40,6 @@ public class TrackDeleteActivity extends AbstractActivity {
|
|||||||
|
|
||||||
private Thread deleteThread;
|
private Thread deleteThread;
|
||||||
|
|
||||||
private TrackDeleteBinding viewBinding;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -55,7 +52,13 @@ public class TrackDeleteActivity extends AbstractActivity {
|
|||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
|
||||||
|
|
||||||
PowerManager.WakeLock wakeLock = SystemUtils.acquireWakeLock(TrackDeleteActivity.this, null);
|
PowerManager.WakeLock wakeLock = SystemUtils.acquireWakeLock(TrackDeleteActivity.this, null);
|
||||||
contentProviderUtils.deleteTracks(TrackDeleteActivity.this, trackIds);
|
|
||||||
|
for (Track.Id id : trackIds) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
contentProviderUtils.deleteTrack(TrackDeleteActivity.this, id);
|
||||||
|
}
|
||||||
|
|
||||||
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
@@ -79,13 +82,12 @@ public class TrackDeleteActivity extends AbstractActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = TrackDeleteBinding.inflate(getLayoutInflater());
|
return R.layout.track_delete;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAsyncTaskCompleted() {
|
public void onAsyncTaskCompleted() {
|
||||||
viewBinding.progressbar.setVisibility(View.INVISIBLE);
|
findViewById(R.id.progressbar).setVisibility(View.INVISIBLE);
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,19 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.AutoCompleteTextView;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
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.databinding.TrackEditBinding;
|
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
@@ -50,7 +55,10 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
private Track track;
|
private Track track;
|
||||||
private String iconValue;
|
private String iconValue;
|
||||||
|
|
||||||
private TrackEditBinding viewBinding;
|
private EditText nameEditText;
|
||||||
|
private AutoCompleteTextView activityType;
|
||||||
|
private Spinner activityTypeIcon;
|
||||||
|
private EditText description;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle bundle) {
|
protected void onCreate(Bundle bundle) {
|
||||||
@@ -67,22 +75,25 @@ 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.getId());
|
Log.e(TAG, "No track for " + trackId);
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackEditName.setText(track.getName());
|
nameEditText = findViewById(R.id.track_edit_name);
|
||||||
|
nameEditText.setText(track.getName());
|
||||||
|
|
||||||
viewBinding.trackEditActivityType.setText(track.getCategory());
|
activityType = findViewById(R.id.track_edit_activity_type);
|
||||||
|
activityType.setText(track.getCategory());
|
||||||
|
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||||
viewBinding.trackEditActivityType.setAdapter(adapter);
|
activityType.setAdapter(adapter);
|
||||||
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.trackEditActivityType.getAdapter().getItem(position))));
|
activityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||||
viewBinding.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
TrackEditActivity.this, (String) activityType.getAdapter().getItem(position))));
|
||||||
|
activityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
if (!hasFocus) {
|
if (!hasFocus) {
|
||||||
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
setActivityTypeIcon(TrackIconUtils.getIconValue(
|
||||||
TrackEditActivity.this, viewBinding.trackEditActivityType.getText().toString()));
|
TrackEditActivity.this, activityType.getText().toString()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -94,25 +105,40 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
iconValue = track.getIcon();
|
iconValue = track.getIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
setActivityTypeIcon(iconValue);
|
activityTypeIcon = findViewById(R.id.track_edit_activity_type_icon);
|
||||||
viewBinding.trackEditActivityTypeIcon.setOnClickListener(v -> ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), viewBinding.trackEditActivityType.getText().toString()));
|
activityTypeIcon.setAdapter(TrackIconUtils.getIconSpinnerAdapter(this, iconValue));
|
||||||
|
activityTypeIcon.setOnTouchListener((v, event) -> {
|
||||||
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
activityTypeIcon.setOnKeyListener((v, keyCode, event) -> {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||||
|
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
viewBinding.trackEditDescription.setText(track.getDescription());
|
description = findViewById(R.id.track_edit_description);
|
||||||
|
description.setText(track.getDescription());
|
||||||
|
|
||||||
viewBinding.trackEditSave.setOnClickListener(v -> {
|
Button saveButton = findViewById(R.id.track_edit_save);
|
||||||
TrackUtils.updateTrack(TrackEditActivity.this, track, viewBinding.trackEditName.getText().toString(),
|
saveButton.setOnClickListener(v -> {
|
||||||
viewBinding.trackEditActivityType.getText().toString(), viewBinding.trackEditDescription.getText().toString(),
|
TrackUtils.updateTrack(TrackEditActivity.this, track, nameEditText.getText().toString(),
|
||||||
|
activityType.getText().toString(), description.getText().toString(),
|
||||||
contentProviderUtils);
|
contentProviderUtils);
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Button cancel = findViewById(R.id.track_edit_cancel);
|
||||||
if (getIntent().getBooleanExtra(EXTRA_NEW_TRACK, false)) {
|
if (getIntent().getBooleanExtra(EXTRA_NEW_TRACK, false)) {
|
||||||
setTitle(R.string.track_edit_new_track_title);
|
setTitle(R.string.track_edit_new_track_title);
|
||||||
viewBinding.trackEditCancel.setVisibility(View.GONE);
|
cancel.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
setTitle(R.string.menu_edit);
|
setTitle(R.string.menu_edit);
|
||||||
viewBinding.trackEditCancel.setOnClickListener(v -> finish());
|
cancel.setOnClickListener(v -> finish());
|
||||||
viewBinding.trackEditCancel.setVisibility(View.VISIBLE);
|
cancel.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,19 +162,18 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = TrackEditBinding.inflate(getLayoutInflater());
|
return R.layout.track_edit;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setActivityTypeIcon(String iconValue) {
|
private void setActivityTypeIcon(String value) {
|
||||||
this.iconValue = iconValue;
|
iconValue = value;
|
||||||
viewBinding.trackEditActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(iconValue));
|
TrackIconUtils.setIconSpinner(activityTypeIcon, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChooseActivityTypeDone(String value) {
|
public void onChooseActivityTypeDone(String value) {
|
||||||
setActivityTypeIcon(value);
|
setActivityTypeIcon(value);
|
||||||
viewBinding.trackEditActivityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
activityType.setText(getString(TrackIconUtils.getIconActivityType(value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import android.view.KeyEvent;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.widget.ListView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -40,12 +42,12 @@ import androidx.appcompat.widget.SearchView;
|
|||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||||
import androidx.loader.app.LoaderManager;
|
import androidx.loader.app.LoaderManager;
|
||||||
import androidx.loader.content.CursorLoader;
|
import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
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.databinding.TrackListBinding;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||||
@@ -64,20 +66,35 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
|
|||||||
*
|
*
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
*/
|
*/
|
||||||
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback {
|
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||||
|
|
||||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||||
|
|
||||||
// The following are set in onCreate
|
// The following are set in onCreate
|
||||||
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private TrackController trackController;
|
private TrackController trackController;
|
||||||
|
private ListView listView;
|
||||||
private ResourceCursorAdapter resourceCursorAdapter;
|
private ResourceCursorAdapter resourceCursorAdapter;
|
||||||
private GpsStatusValue gpsStatusValue;
|
private GpsStatusValue gpsStatusValue;
|
||||||
|
|
||||||
private TrackListBinding viewBinding;
|
private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() {
|
||||||
|
@Override
|
||||||
|
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||||
|
return ContentProviderUtils.getTracksCursorLoader(TrackListActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
private final TrackLoaderCallBack loaderCallbacks = new TrackLoaderCallBack();
|
@Override
|
||||||
|
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
||||||
|
resourceCursorAdapter.swapCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
||||||
|
resourceCursorAdapter.swapCursor(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
private boolean metricUnits = true;
|
private boolean metricUnits = true;
|
||||||
@@ -95,8 +112,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onClick(int itemId, int[] positions, long[] trackIds) {
|
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||||
return handleContextItem(itemId, trackIds);
|
return handleContextItem(itemId, ids);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,7 +137,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
if (key != null) {
|
if (key != null) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
TrackListActivity.this.invalidateOptionsMenu();
|
TrackListActivity.this.invalidateOptionsMenu();
|
||||||
loaderCallbacks.restart();
|
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
|
||||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
||||||
trackController.update(isRecording, recordingTrackPaused);
|
trackController.update(isRecording, recordingTrackPaused);
|
||||||
});
|
});
|
||||||
@@ -132,6 +149,14 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
private MenuItem searchMenuItem;
|
private MenuItem searchMenuItem;
|
||||||
private MenuItem startGpsMenuItem;
|
private MenuItem startGpsMenuItem;
|
||||||
|
|
||||||
|
private final OnClickListener stopListener = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
updateMenuItems(false, false);
|
||||||
|
trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Callback when the trackRecordingServiceConnection binding changes.
|
// Callback when the trackRecordingServiceConnection binding changes.
|
||||||
private final Runnable bindChangedCallback = new Runnable() {
|
private final Runnable bindChangedCallback = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -166,23 +191,42 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final OnClickListener recordListener = new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (!PreferencesUtils.isRecording(recordingTrackId)) {
|
||||||
|
// Not recording -> Recording
|
||||||
|
updateMenuItems(false, true);
|
||||||
|
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
||||||
|
startActivity(newIntent);
|
||||||
|
} else if (recordingTrackPaused) {
|
||||||
|
// Paused -> Resume
|
||||||
|
updateMenuItems(false, true);
|
||||||
|
trackRecordingServiceConnection.resumeTrack();
|
||||||
|
trackController.update(true, false);
|
||||||
|
} else {
|
||||||
|
// Recording -> Paused
|
||||||
|
updateMenuItems(false, true);
|
||||||
|
trackRecordingServiceConnection.pauseTrack();
|
||||||
|
trackController.update(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@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);
|
||||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
|
||||||
|
|
||||||
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
||||||
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||||
|
|
||||||
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, true, this);
|
trackController = new TrackController(this, trackRecordingServiceConnection, true, recordListener, stopListener);
|
||||||
|
|
||||||
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
|
|
||||||
// Show trackController when search dialog is dismissed
|
// Show trackController when search dialog is dismissed
|
||||||
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
|
SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
|
||||||
@@ -190,8 +234,9 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
searchManager.setOnDismissListener(() -> trackController.show());
|
searchManager.setOnDismissListener(() -> trackController.show());
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
listView = findViewById(R.id.track_list);
|
||||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackId) -> {
|
listView.setEmptyView(findViewById(R.id.track_list_empty_view));
|
||||||
|
listView.setOnItemClickListener((parent, view, position, trackId) -> {
|
||||||
Intent newIntent;
|
Intent newIntent;
|
||||||
if (trackId == recordingTrackId.getId()) {
|
if (trackId == recordingTrackId.getId()) {
|
||||||
// Is recording -> open record activity.
|
// Is recording -> open record activity.
|
||||||
@@ -208,24 +253,23 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
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 idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
|
int idIndex = cursor.getColumnIndex(TracksColumns._ID);
|
||||||
int iconIndex = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
|
int iconIndex = cursor.getColumnIndex(TracksColumns.ICON);
|
||||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
int nameIndex = cursor.getColumnIndex(TracksColumns.NAME);
|
||||||
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY);
|
||||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION);
|
||||||
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
|
||||||
|
|
||||||
Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
|
Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
|
||||||
boolean isRecording = trackId.equals(recordingTrackId);
|
boolean isRecording = trackId == 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 = cursor.getInt(markerCountIndex);
|
int markerCount = contentProviderUtils.getWaypointCount(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);
|
||||||
@@ -235,10 +279,11 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
startTime, true, category, description, null);
|
startTime, true, category, description, null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
listView.setAdapter(resourceCursorAdapter);
|
||||||
ActivityUtils.configureListViewContextualMenu(viewBinding.trackList, contextualActionModeCallback);
|
|
||||||
|
|
||||||
loadData(getIntent());
|
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||||
|
|
||||||
|
LoaderManager.getInstance(this).initLoader(0, null, loaderCallbacks);
|
||||||
|
|
||||||
requestGPSPermissions();
|
requestGPSPermissions();
|
||||||
}
|
}
|
||||||
@@ -252,14 +297,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
trackRecordingServiceConnection.startConnection(this);
|
trackRecordingServiceConnection.startConnection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
trackController.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
@@ -270,6 +307,14 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
trackController.onResume(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
|
trackController.onResume(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
trackController.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
@@ -290,9 +335,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = TrackListBinding.inflate(getLayoutInflater());
|
return R.layout.track_list;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -368,36 +412,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (loaderCallbacks.getSearchQuery() != null) {
|
|
||||||
loaderCallbacks.setSearch(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
|
||||||
if (!searchView.isIconified()) {
|
|
||||||
searchView.setIconified(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
setIntent(intent);
|
|
||||||
loadData(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadData(Intent intent) {
|
|
||||||
String searchQuery = null;
|
|
||||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
|
||||||
searchQuery = intent.getStringExtra(SearchManager.QUERY);
|
|
||||||
}
|
|
||||||
|
|
||||||
loaderCallbacks.setSearch(searchQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSearchRequested() {
|
public boolean onSearchRequested() {
|
||||||
// Hide trackController when search dialog is shown
|
// Hide trackController when search dialog is shown
|
||||||
@@ -411,7 +425,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTrackDeleted() {
|
protected void onDeleted() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,91 +500,12 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
deleteTracks(trackIds);
|
deleteTracks(trackIds);
|
||||||
return true;
|
return true;
|
||||||
case R.id.list_context_menu_select_all:
|
case R.id.list_context_menu_select_all:
|
||||||
int size = viewBinding.trackList.getCount();
|
int size = listView.getCount();
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
viewBinding.trackList.setItemChecked(i, true);
|
listView.setItemChecked(i, true);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TrackLoaderCallBack implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
|
|
||||||
private String searchQuery = null;
|
|
||||||
|
|
||||||
public String getSearchQuery() {
|
|
||||||
return searchQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearch(String searchQuery) {
|
|
||||||
this.searchQuery = searchQuery;
|
|
||||||
restart();
|
|
||||||
if (searchQuery != null) {
|
|
||||||
setTitle(searchQuery);
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.app_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restart() {
|
|
||||||
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
|
||||||
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
|
||||||
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
|
|
||||||
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
|
||||||
|
|
||||||
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
|
||||||
|
|
||||||
if (searchQuery == null) {
|
|
||||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null, null, sortOrder);
|
|
||||||
} else {
|
|
||||||
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
|
||||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
|
||||||
TracksColumns.CATEGORY + " LIKE ?";
|
|
||||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
|
||||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
|
||||||
resourceCursorAdapter.swapCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
|
||||||
resourceCursorAdapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordStart() {
|
|
||||||
if (!PreferencesUtils.isRecording(recordingTrackId)) {
|
|
||||||
// Not recording -> Recording
|
|
||||||
updateMenuItems(false, true);
|
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
|
||||||
startActivity(newIntent);
|
|
||||||
} else if (recordingTrackPaused) {
|
|
||||||
// Paused -> Resume
|
|
||||||
updateMenuItems(false, true);
|
|
||||||
trackRecordingServiceConnection.resumeTrack();
|
|
||||||
trackController.update(true, false);
|
|
||||||
} else {
|
|
||||||
// Recording -> Paused
|
|
||||||
updateMenuItems(false, true);
|
|
||||||
trackRecordingServiceConnection.pauseTrack();
|
|
||||||
trackController.update(true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordStop() {
|
|
||||||
updateMenuItems(false, false);
|
|
||||||
trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,27 +21,29 @@ import android.os.Bundle;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
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 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.databinding.TrackRecordedBinding;
|
|
||||||
import de.dennisguse.opentracks.fragments.ChartFragment;
|
import de.dennisguse.opentracks.fragments.ChartFragment;
|
||||||
|
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;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
import de.dennisguse.opentracks.util.TrackUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity to show the track detail, record a new track or resumes an existing one.
|
* An activity to show the track detail, record a new track or resumes an existing one.
|
||||||
@@ -49,21 +51,21 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class TrackRecordedActivity extends AbstractListActivity implements 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.
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
private ViewPager pager;
|
||||||
|
|
||||||
private TrackRecordedBinding viewBinding;
|
// From intent.
|
||||||
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,10 +77,46 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
|
|
||||||
trackDataHub = new TrackDataHub(this);
|
trackDataHub = new TrackDataHub(this);
|
||||||
|
|
||||||
viewBinding.trackDetailActivityViewPager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT));
|
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
|
||||||
viewBinding.trackDetailActivityTablayout.setupWithViewPager(viewBinding.trackDetailActivityViewPager);
|
@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.setAdapter(adapter);
|
||||||
|
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
|
||||||
|
tabs.setupWithViewPager(pager);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
pager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +133,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
// Update UI
|
// Update UI
|
||||||
this.invalidateOptionsMenu();
|
this.invalidateOptionsMenu();
|
||||||
|
|
||||||
if (trackId != null) {
|
if (trackId.isValid()) {
|
||||||
trackDataHub.loadTrack(trackId);
|
trackDataHub.loadTrack(trackId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,13 +147,12 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt(CURRENT_TAB_TAG_KEY, viewBinding.trackDetailActivityViewPager.getCurrentItem());
|
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = TrackRecordedBinding.inflate(getLayoutInflater());
|
return R.layout.track_detail;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -188,7 +225,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTrackDeleted() {
|
protected void onDeleted() {
|
||||||
runOnUiThread(this::finish);
|
runOnUiThread(this::finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,15 +240,15 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
private void handleIntent(Intent intent) {
|
private void handleIntent(Intent intent) {
|
||||||
trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
|
trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
|
||||||
|
|
||||||
Marker.Id markerId = intent.getParcelableExtra(EXTRA_MARKER_ID);
|
Waypoint.Id waypointId = intent.getParcelableExtra(EXTRA_MARKER_ID);
|
||||||
if (markerId != null) {
|
if (waypointId != null) {
|
||||||
// Use the trackId from the marker
|
// Use the trackId from the marker
|
||||||
Marker marker = contentProviderUtils.getMarker(markerId);
|
Waypoint waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
if (marker == null) {
|
if (waypoint == null) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
trackId = marker.getTrackId();
|
trackId = waypoint.getTrackId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackId == null) {
|
if (trackId == null) {
|
||||||
@@ -220,48 +257,14 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
|
public void chooseActivityType(String category) {
|
||||||
|
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), category);
|
||||||
|
}
|
||||||
|
|
||||||
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
|
@Override
|
||||||
super(fm, behavior);
|
public void onChooseActivityTypeDone(String iconValue) {
|
||||||
}
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||||
@Override
|
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||||
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();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,20 +9,22 @@ import android.util.Log;
|
|||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
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.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
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.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.databinding.TrackRecordingBinding;
|
|
||||||
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;
|
||||||
@@ -41,7 +43,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
|
|||||||
*/
|
*/
|
||||||
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
|
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
|
||||||
//This mode of communication should be removed anyhow.
|
//This mode of communication should be removed anyhow.
|
||||||
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback {
|
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
|
|
||||||
@@ -54,10 +56,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
private ViewPager pager;
|
||||||
private TrackController trackController;
|
private TrackController trackController;
|
||||||
|
|
||||||
private TrackRecordingBinding viewBinding;
|
|
||||||
|
|
||||||
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
|
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
|
||||||
@@ -125,17 +126,46 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
private MenuItem insertMarkerMenuItem;
|
private MenuItem insertMarkerMenuItem;
|
||||||
private MenuItem markerListMenuItem;
|
private MenuItem markerListMenuItem;
|
||||||
|
|
||||||
|
private final OnClickListener recordListener = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (recordingTrackPaused) {
|
||||||
|
// Paused -> Resume
|
||||||
|
updateMenuItems(false);
|
||||||
|
trackRecordingServiceConnection.resumeTrack();
|
||||||
|
trackController.update(true, false);
|
||||||
|
} else {
|
||||||
|
// Recording -> Paused
|
||||||
|
updateMenuItems(true);
|
||||||
|
trackRecordingServiceConnection.pauseTrack();
|
||||||
|
trackController.update(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final OnClickListener stopListener = new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
|
||||||
|
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
|
||||||
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
startActivity(newIntent);
|
||||||
|
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||||
|
updateMenuItems(true);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
contentProviderUtils = new ContentProviderUtils(this);
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
|
||||||
trackId = null;
|
trackId = null;
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
//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.");
|
||||||
@@ -144,17 +174,20 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||||
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
trackDataHub = new TrackDataHub(this);
|
trackDataHub = new TrackDataHub(this);
|
||||||
|
|
||||||
viewBinding.trackDetailActivityViewPager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT));
|
pager = findViewById(R.id.track_detail_activity_view_pager);
|
||||||
viewBinding.trackDetailActivityTablayout.setupWithViewPager(viewBinding.trackDetailActivityViewPager);
|
pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), 1));
|
||||||
|
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
|
||||||
|
tabs.setupWithViewPager(pager);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
pager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
trackController = new TrackController(this, viewBinding.trackControllerContainer, trackRecordingServiceConnection, false, this);
|
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -232,7 +265,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt(CURRENT_TAB_TAG_KEY, viewBinding.trackDetailActivityViewPager.getCurrentItem());
|
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
|
||||||
outState.putParcelable(EXTRA_TRACK_ID, trackId);
|
outState.putParcelable(EXTRA_TRACK_ID, trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,9 +284,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected int getLayoutResId() {
|
||||||
viewBinding = TrackRecordingBinding.inflate(getLayoutInflater());
|
return R.layout.track_record;
|
||||||
return viewBinding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -332,32 +364,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordStart() {
|
|
||||||
if (recordingTrackPaused) {
|
|
||||||
// Paused -> Resume
|
|
||||||
updateMenuItems(false);
|
|
||||||
trackRecordingServiceConnection.resumeTrack();
|
|
||||||
trackController.update(true, false);
|
|
||||||
} else {
|
|
||||||
// Recording -> Paused
|
|
||||||
updateMenuItems(true);
|
|
||||||
trackRecordingServiceConnection.pauseTrack();
|
|
||||||
trackController.update(true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recordStop() {
|
|
||||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
|
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
|
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
|
||||||
startActivity(newIntent);
|
|
||||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
|
||||||
updateMenuItems(true);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
|
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
|
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
|
||||||
@@ -366,7 +372,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return 4;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -374,12 +380,10 @@ 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 StatisticsRecordingFragment.newInstance();
|
return new StatisticsRecordingFragment();
|
||||||
case 1:
|
case 1:
|
||||||
return IntervalsFragment.IntervalsRecordingFragment.newInstance();
|
|
||||||
case 2:
|
|
||||||
return ChartFragment.newInstance(false);
|
return ChartFragment.newInstance(false);
|
||||||
case 3:
|
case 2:
|
||||||
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);
|
||||||
@@ -392,10 +396,8 @@ 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.track_detail_intervals_tab);
|
|
||||||
case 2:
|
|
||||||
return getString(R.string.settings_chart_by_time);
|
return getString(R.string.settings_chart_by_time);
|
||||||
case 3:
|
case 2:
|
||||||
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);
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
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;
|
|
||||||
private String category;
|
|
||||||
|
|
||||||
public IntervalStatisticsAdapter(Context context, List<IntervalStatistics.Interval> intervalList, String category, StackMode stackMode) {
|
|
||||||
super(context, R.layout.interval_stats_list_item, intervalList);
|
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(context);
|
|
||||||
this.category = category;
|
|
||||||
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.rate = intervalView.findViewById(R.id.interval_item_rate);
|
|
||||||
viewHolder.gain = intervalView.findViewById(R.id.interval_item_gain);
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
if (PreferencesUtils.isReportSpeed(getContext(), category)) {
|
|
||||||
Pair<String, String> speedParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, true);
|
|
||||||
viewHolder.rate.setText(speedParts.first + " " + speedParts.second);
|
|
||||||
} else {
|
|
||||||
Pair<String, String> paceParts = StringUtils.getSpeedParts(getContext(), interval.getSpeed_ms(), metricUnits, false);
|
|
||||||
viewHolder.rate.setText(paceParts.first + " " + paceParts.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pair<String, String> gainParts = StringUtils.formatElevation(getContext(), interval.getGain_m(), metricUnits);
|
|
||||||
viewHolder.gain.setText(gainParts.first + " " + gainParts.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 rate;
|
|
||||||
private TextView gain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
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.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorData;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataCyclingPower;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
|
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
|
|
||||||
public class SensorsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
|
||||||
|
|
||||||
public static final int HEART_RATE_TYPE = 0;
|
|
||||||
public static final int CADENCE_TYPE = 1;
|
|
||||||
public static final int POWER_TYPE = 2;
|
|
||||||
|
|
||||||
private List<Pair<Integer, SensorData>> sensorDataList;
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
public SensorsAdapter(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sensor_item, parent, false);
|
|
||||||
return new SensorsAdapter.ViewHolder(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
|
||||||
SensorsAdapter.ViewHolder viewHolder = (SensorsAdapter.ViewHolder) holder;
|
|
||||||
int type = sensorDataList.get(position).first;
|
|
||||||
SensorData sensorData = sensorDataList.get(position).second;
|
|
||||||
viewHolder.setData(sensorData, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
if (sensorDataList == null) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return sensorDataList.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position) {
|
|
||||||
return sensorDataList.get(position).first;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Pair<Integer, SensorData>> swapData(List<Pair<Integer, SensorData>> data) {
|
|
||||||
if (sensorDataList == data) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
sensorDataList = data;
|
|
||||||
|
|
||||||
if (data != null) {
|
|
||||||
this.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
TextView label;
|
|
||||||
TextView sensorValue;
|
|
||||||
TextView value;
|
|
||||||
TextView unit;
|
|
||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
label = itemView.findViewById(R.id.stats_sensor_label);
|
|
||||||
sensorValue = itemView.findViewById(R.id.stats_sensor_sensor_value);
|
|
||||||
value = itemView.findViewById(R.id.stats_sensor_value);
|
|
||||||
unit = itemView.findViewById(R.id.stats_sensor_unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(SensorData sensorData, int type) {
|
|
||||||
switch (type) {
|
|
||||||
case HEART_RATE_TYPE:
|
|
||||||
setHeartRateSensorData((SensorDataHeartRate) sensorData);
|
|
||||||
break;
|
|
||||||
case CADENCE_TYPE:
|
|
||||||
setCadenceSensorData((SensorDataCycling.Cadence) sensorData);
|
|
||||||
break;
|
|
||||||
case POWER_TYPE:
|
|
||||||
setPowerSensorData((SensorDataCyclingPower) sensorData);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown sensor type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setHeartRateSensorData(SensorDataHeartRate data) {
|
|
||||||
String sensorValue = context.getString(R.string.value_unknown);
|
|
||||||
String sensorName = context.getString(R.string.value_unknown);
|
|
||||||
if (data != null) {
|
|
||||||
sensorName = data.getSensorNameOrAddress();
|
|
||||||
if (data.hasHeartRate_bpm() && data.isRecent()) {
|
|
||||||
sensorValue = StringUtils.formatDecimal(data.getHeartRate_bpm(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.label.setText(context.getString(R.string.sensor_state_heart_rate));
|
|
||||||
this.sensorValue.setText(sensorName);
|
|
||||||
this.value.setText(sensorValue);
|
|
||||||
this.unit.setText(R.string.sensor_unit_beats_per_minute);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCadenceSensorData(SensorDataCycling.Cadence data) {
|
|
||||||
String sensorValue = context.getString(R.string.value_unknown);
|
|
||||||
String sensorName = context.getString(R.string.value_unknown);
|
|
||||||
if (data != null) {
|
|
||||||
sensorName = data.getSensorNameOrAddress();
|
|
||||||
|
|
||||||
if (data.hasCadence_rpm() && data.isRecent()) {
|
|
||||||
sensorValue = StringUtils.formatDecimal(data.getCadence_rpm(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.label.setText(context.getString(R.string.sensor_state_cadence));
|
|
||||||
this.sensorValue.setText(sensorName);
|
|
||||||
this.value.setText(sensorValue);
|
|
||||||
this.unit.setText(R.string.sensor_unit_rounds_per_minute);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setPowerSensorData(SensorDataCyclingPower data) {
|
|
||||||
String sensorValue = context.getString(R.string.value_unknown);
|
|
||||||
String sensorName = context.getString(R.string.value_unknown);
|
|
||||||
if (data != null) {
|
|
||||||
sensorName = data.getSensorNameOrAddress();
|
|
||||||
|
|
||||||
if (data.hasPower_w() && data.isRecent()) {
|
|
||||||
sensorValue = StringUtils.formatDecimal(data.getPower_w(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.label.setText(context.getString(R.string.sensor_state_power));
|
|
||||||
this.sensorValue.setText(sensorName);
|
|
||||||
this.value.setText(sensorValue);
|
|
||||||
this.unit.setText(R.string.sensor_unit_power);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package de.dennisguse.opentracks.android;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps a {@link ContentResolver} and provides access as {@link IContentResolver}.
|
||||||
|
*/
|
||||||
|
public class ContentResolverWrapper implements IContentResolver {
|
||||||
|
|
||||||
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
|
public ContentResolverWrapper(ContentResolver contentResolver) {
|
||||||
|
this.contentResolver = contentResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
|
||||||
|
return contentResolver.insert(url, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] values) {
|
||||||
|
return contentResolver.bulkInsert(url, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||||
|
return contentResolver.query(uri, projection, selection, selectionArgs, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs) {
|
||||||
|
return contentResolver.update(uri, values, where, selectionArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int delete(@NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs) {
|
||||||
|
return contentResolver.delete(url, where, selectionArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package de.dennisguse.opentracks.android;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresPermission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the interface actually shared by {@link android.content.ContentProvider} and {@link android.content.ContentResolver}.
|
||||||
|
* So, both can be used interchangeably.
|
||||||
|
*/
|
||||||
|
public interface IContentResolver {
|
||||||
|
@Nullable
|
||||||
|
Uri insert(@RequiresPermission.Write @NonNull Uri url, @Nullable ContentValues values);
|
||||||
|
|
||||||
|
int bulkInsert(@RequiresPermission.Write @NonNull Uri url, @NonNull ContentValues[] values);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Cursor query(@RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder);
|
||||||
|
|
||||||
|
int update(@RequiresPermission.Write @NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs);
|
||||||
|
|
||||||
|
int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs);
|
||||||
|
}
|
||||||
@@ -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 = new ExtremityMonitor();
|
private final ExtremityMonitor extremityMonitor;
|
||||||
private final NumberFormat numberFormat = NumberFormat.getIntegerInstance();
|
private final NumberFormat numberFormat;
|
||||||
private final Path path = new Path();
|
private final Path path;
|
||||||
|
|
||||||
private int interval = 1;
|
private int interval = 1;
|
||||||
private int minMarkerValue = 0;
|
private int minMarkerValue = 0;
|
||||||
@@ -91,15 +91,17 @@ 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,6 +19,7 @@ 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;
|
||||||
@@ -41,12 +42,11 @@ 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.Marker;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
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 MARKER_X_ANCHOR = 13f / 48f;
|
private static final float WAYPOINT_X_ANCHOR = 13f / 48f;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
X_FRACTION_FORMAT.setMaximumFractionDigits(1);
|
X_FRACTION_FORMAT.setMaximumFractionDigits(1);
|
||||||
@@ -87,15 +87,14 @@ 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<Marker> markers = new ArrayList<>();
|
private final List<Waypoint> waypoints = 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 markerPin;
|
private final Drawable waypointMarker;
|
||||||
private final int markerWidth;
|
private final int markerWidth;
|
||||||
private final int markerHeight;
|
private final int markerHeight;
|
||||||
private final Scroller scroller;
|
private final Scroller scroller;
|
||||||
@@ -151,19 +150,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;
|
||||||
Marker nearestMarker = null;
|
Waypoint nearestWaypoint = null;
|
||||||
synchronized (markers) {
|
synchronized (waypoints) {
|
||||||
for (Marker marker : markers) {
|
for (Waypoint waypoint : waypoints) {
|
||||||
int distance = Math.abs(getX(getMarkerXValue(marker)) - (int) event.getX() - getScrollX());
|
int distance = Math.abs(getX(getWaypointXValue(waypoint)) - (int) event.getX() - getScrollX());
|
||||||
if (distance < minDistance) {
|
if (distance < minDistance) {
|
||||||
minDistance = distance;
|
minDistance = distance;
|
||||||
nearestMarker = marker;
|
nearestWaypoint = waypoint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nearestMarker != null && minDistance < markerWidth) {
|
if (nearestWaypoint != null && minDistance < markerWidth) {
|
||||||
Intent intent = IntentUtils.newIntent(getContext(), MarkerDetailActivity.class)
|
Intent intent = IntentUtils.newIntent(getContext(), MarkerDetailActivity.class)
|
||||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, nearestMarker.getId());
|
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, nearestWaypoint.getId());
|
||||||
getContext().startActivity(intent);
|
getContext().startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -192,6 +191,7 @@ 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,13 +307,11 @@ 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.FILL_AND_STROKE);
|
axisPaint.setStyle(Style.STROKE);
|
||||||
axisPaint.setColor(ThemeUtils.getTextColorPrimary(context));
|
axisPaint.setColor(context.getResources().getColor(android.R.color.black));
|
||||||
axisPaint.setAntiAlias(true);
|
axisPaint.setAntiAlias(true);
|
||||||
axisPaint.setTextSize(SMALL_TEXT_SIZE * scale);
|
axisPaint.setTextSize(SMALL_TEXT_SIZE * scale);
|
||||||
|
|
||||||
@@ -322,21 +320,22 @@ public class ChartView extends View {
|
|||||||
|
|
||||||
gridPaint = new Paint();
|
gridPaint = new Paint();
|
||||||
gridPaint.setStyle(Style.STROKE);
|
gridPaint.setStyle(Style.STROKE);
|
||||||
gridPaint.setColor(ThemeUtils.getTextColorSecondary(context));
|
gridPaint.setColor(context.getResources().getColor(android.R.color.darker_gray));
|
||||||
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());
|
||||||
|
|
||||||
markerPin = MarkerUtils.getDefaultPhoto(context);
|
waypointMarker = MarkerUtils.getDefaultPhoto(context);
|
||||||
markerWidth = markerPin.getIntrinsicWidth();
|
markerWidth = waypointMarker.getIntrinsicWidth();
|
||||||
markerHeight = markerPin.getIntrinsicHeight();
|
markerHeight = waypointMarker.getIntrinsicHeight();
|
||||||
markerPin.setBounds(0, 0, markerWidth, markerHeight);
|
waypointMarker.setBounds(0, 0, markerWidth, markerHeight);
|
||||||
|
|
||||||
scroller = new Scroller(context);
|
scroller = new Scroller(context);
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
@@ -435,15 +434,15 @@ public class ChartView extends View {
|
|||||||
scrollTo(0, 0);
|
scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMarker(Marker marker) {
|
public void addWaypoint(Waypoint waypoint) {
|
||||||
synchronized (markers) {
|
synchronized (waypoints) {
|
||||||
markers.add(marker);
|
waypoints.add(waypoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearMarker() {
|
public void clearWaypoints() {
|
||||||
synchronized (markers) {
|
synchronized (waypoints) {
|
||||||
markers.clear();
|
waypoints.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,13 +540,13 @@ public class ChartView extends View {
|
|||||||
synchronized (chartPoints) {
|
synchronized (chartPoints) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
|
|
||||||
canvas.drawColor(backgroundColor);
|
canvas.drawColor(Color.WHITE);
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
|
|
||||||
clipToGraphArea(canvas);
|
clipToGraphArea(canvas);
|
||||||
drawDataSeries(canvas);
|
drawDataSeries(canvas);
|
||||||
drawMarker(canvas);
|
drawWaypoints(canvas);
|
||||||
drawGrid(canvas);
|
drawGrid(canvas);
|
||||||
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
@@ -588,19 +587,24 @@ public class ChartView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawMarker(Canvas canvas) {
|
/**
|
||||||
synchronized (markers) {
|
* Draws the waypoints.
|
||||||
for (Marker marker : markers) {
|
*
|
||||||
double xValue = getMarkerXValue(marker);
|
* @param canvas the canvas
|
||||||
|
*/
|
||||||
|
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(getMarkerXValue(marker));
|
float x = getX(getWaypointXValue(waypoint));
|
||||||
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 * MARKER_X_ANCHOR), topBorder + spacer);
|
canvas.translate(x - (markerWidth * WAYPOINT_X_ANCHOR), topBorder + spacer);
|
||||||
|
|
||||||
markerPin.draw(canvas);
|
waypointMarker.draw(canvas);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,7 +904,7 @@ public class ChartView extends View {
|
|||||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||||
chartValueSeries.updateDimension();
|
chartValueSeries.updateDimension();
|
||||||
}
|
}
|
||||||
float density = getResources().getDisplayMetrics().density;
|
float density = getContext().getResources().getDisplayMetrics().density;
|
||||||
spacer = (int) (density * SPACER);
|
spacer = (int) (density * SPACER);
|
||||||
yAxisOffset = (int) (density * Y_AXIS_OFFSET);
|
yAxisOffset = (int) (density * Y_AXIS_OFFSET);
|
||||||
|
|
||||||
@@ -971,12 +975,17 @@ 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 length_km = marker.getLength() * UnitConversions.M_TO_KM;
|
double lenghtInKm = waypoint.getLength() * UnitConversions.M_TO_KM;
|
||||||
return metricUnits ? length_km : length_km * UnitConversions.KM_TO_MI;
|
return metricUnits ? lenghtInKm : lenghtInKm * UnitConversions.KM_TO_MI;
|
||||||
} else {
|
} else {
|
||||||
return marker.getDuration();
|
return waypoint.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 markers.
|
* Generates descriptions for tracks and waypoints.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
@@ -136,9 +136,7 @@ public class DescriptionGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Elevation gain
|
// Elevation gain
|
||||||
if (stats.hasTotalElevationGain()) {
|
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
||||||
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())));
|
||||||
|
|||||||
@@ -0,0 +1,425 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||||
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations under
|
||||||
|
* the License.
|
||||||
|
*/
|
||||||
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
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.stats.TrackStatistics;
|
||||||
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Engine for searching for tracks and waypoints by text.
|
||||||
|
*
|
||||||
|
* @author Rodrigo Damazio
|
||||||
|
*/
|
||||||
|
public class SearchEngine {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WHERE query to get tracks by name.
|
||||||
|
*/
|
||||||
|
private static final String TRACK_SELECTION_QUERY =
|
||||||
|
TracksColumns.NAME + " LIKE ? OR " +
|
||||||
|
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
|
TracksColumns.CATEGORY + " LIKE ?";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WHERE query to get waypoints by name.
|
||||||
|
*/
|
||||||
|
private static final String WAYPOINT_SELECTION_QUERY =
|
||||||
|
WaypointsColumns.NAME + " LIKE ? OR " +
|
||||||
|
WaypointsColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
|
WaypointsColumns.CATEGORY + " LIKE ?";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of track results.
|
||||||
|
*/
|
||||||
|
private static final String TRACK_SELECTION_ORDER = TracksColumns._ID + " DESC LIMIT 1000";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Order of waypoint results.
|
||||||
|
*/
|
||||||
|
private static final String WAYPOINT_SELECTION_ORDER = WaypointsColumns._ID + " DESC";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much we promote a match in the track category.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_CATEGORY_PROMOTION = 2.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much we promote a match in the track description.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_DESCRIPTION_PROMOTION = 8.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much we promote a match in the track name.
|
||||||
|
*/
|
||||||
|
private static final double TRACK_NAME_PROMOTION = 16.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much we promote a waypoint result if it's in the currently-selected track.
|
||||||
|
*/
|
||||||
|
private static final double CURRENT_TRACK_WAYPOINT_PROMOTION = 2.0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How much we promote a track result if it's the currently-selected track.
|
||||||
|
*/
|
||||||
|
private static final double CURRENT_TRACK_DEMOTION = 0.5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of waypoints which will be retrieved and scored.
|
||||||
|
*/
|
||||||
|
private static final int MAX_SCORED_WAYPOINTS = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000)
|
||||||
|
*/
|
||||||
|
public static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparator for scored results.
|
||||||
|
*/
|
||||||
|
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = (r1, r2) -> {
|
||||||
|
// Score ordering.
|
||||||
|
int scoreDiff = Double.compare(r2.score, r1.score);
|
||||||
|
if (scoreDiff != 0) {
|
||||||
|
return scoreDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make tracks come before waypoints.
|
||||||
|
if (r1.waypoint != null && r2.track != null) {
|
||||||
|
return 1;
|
||||||
|
} else if (r1.track != null && r2.waypoint != null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, use arbitrary ordering, by ID.
|
||||||
|
long id1 = r1.track != null ? r1.track.getId().getId() : r1.waypoint.getId().getId();
|
||||||
|
long id2 = r2.track != null ? r2.track.getId().getId() : r2.waypoint.getId().getId();
|
||||||
|
long idDiff = id2 - id1;
|
||||||
|
return Long.signum(idDiff);
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ContentProviderUtils providerUtils;
|
||||||
|
|
||||||
|
public SearchEngine(ContentProviderUtils providerUtils) {
|
||||||
|
this.providerUtils = providerUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Squashes a number by calculating 1 / log (1 + x).
|
||||||
|
*/
|
||||||
|
//TODO Why is this done?
|
||||||
|
private static double squash(double x) {
|
||||||
|
return 1.0 / Math.log1p(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a searchable query and returns a set of sorted results.
|
||||||
|
*
|
||||||
|
* @param query the query to execute
|
||||||
|
* @return a set of results, sorted according to their score
|
||||||
|
*/
|
||||||
|
public SortedSet<ScoredResult> search(SearchQuery query) {
|
||||||
|
TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR);
|
||||||
|
|
||||||
|
scoreTrackResults(retrieveTracks(query), query, scoredResults);
|
||||||
|
scoreWaypointResults(retrieveWaypoints(query), query, scoredResults);
|
||||||
|
|
||||||
|
return scoredResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves tracks matching the given query from the database.
|
||||||
|
*
|
||||||
|
* @param query the query to retrieve for
|
||||||
|
*/
|
||||||
|
private List<Track> retrieveTracks(SearchQuery query) {
|
||||||
|
ArrayList<Track> tracks = new ArrayList<>();
|
||||||
|
|
||||||
|
String queryLikeSelection = "%" + query.textQuery + "%";
|
||||||
|
String[] trackSelectionArgs = new String[]{
|
||||||
|
queryLikeSelection,
|
||||||
|
queryLikeSelection,
|
||||||
|
queryLikeSelection
|
||||||
|
};
|
||||||
|
|
||||||
|
try (Cursor cursor = providerUtils.getTrackCursor(TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
|
||||||
|
if (cursor != null) {
|
||||||
|
tracks.ensureCapacity(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
tracks.add(providerUtils.createTrack(cursor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves waypoints matching the given query from the database.
|
||||||
|
*
|
||||||
|
* @param query the query to retrieve for
|
||||||
|
*/
|
||||||
|
private List<Waypoint> retrieveWaypoints(SearchQuery query) {
|
||||||
|
ArrayList<Waypoint> waypoints = new ArrayList<>();
|
||||||
|
|
||||||
|
String queryLikeSelection2 = "%" + query.textQuery + "%";
|
||||||
|
String[] waypointSelectionArgs = new String[]{
|
||||||
|
queryLikeSelection2,
|
||||||
|
queryLikeSelection2,
|
||||||
|
queryLikeSelection2
|
||||||
|
};
|
||||||
|
|
||||||
|
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs, WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
|
||||||
|
if (cursor != null) {
|
||||||
|
waypoints.ensureCapacity(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
Waypoint waypoint = providerUtils.createWaypoint(cursor);
|
||||||
|
if (LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||||
|
waypoints.add(waypoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return waypoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a collection of track results.
|
||||||
|
*
|
||||||
|
* @param tracks the results to score
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param output the collection to fill with scored results
|
||||||
|
*/
|
||||||
|
private void scoreTrackResults(Collection<Track> tracks, SearchQuery query, Collection<ScoredResult> output) {
|
||||||
|
for (Track track : tracks) {
|
||||||
|
// Calculate the score.
|
||||||
|
double score = scoreTrackResult(query, track);
|
||||||
|
|
||||||
|
// Add to the output.
|
||||||
|
output.add(new ScoredResult(track, score));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a single track result.
|
||||||
|
*
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param track the results to score
|
||||||
|
* @return the score for the track
|
||||||
|
*/
|
||||||
|
private double scoreTrackResult(SearchQuery query, Track track) {
|
||||||
|
double score = 1.0;
|
||||||
|
|
||||||
|
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
|
||||||
|
|
||||||
|
TrackStatistics statistics = track.getTrackStatistics();
|
||||||
|
|
||||||
|
long meanTimestamp = (statistics.getStartTime_ms() + statistics.getStopTime_ms()) / 2L;
|
||||||
|
score *= getTimeBoost(query, meanTimestamp);
|
||||||
|
|
||||||
|
// Score the currently-selected track lower (user is already there, wouldn't be searching for it).
|
||||||
|
if (track.getId() == query.currentTrackId) {
|
||||||
|
score *= CURRENT_TRACK_DEMOTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a collection of waypoint results.
|
||||||
|
*
|
||||||
|
* @param waypoints the results to score
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param output the collection to fill with scored results
|
||||||
|
*/
|
||||||
|
private void scoreWaypointResults(Collection<Waypoint> waypoints, SearchQuery query, Collection<ScoredResult> output) {
|
||||||
|
for (Waypoint waypoint : waypoints) {
|
||||||
|
// Calculate the score.
|
||||||
|
double score = scoreWaypointResult(query, waypoint);
|
||||||
|
|
||||||
|
// Add to the output.
|
||||||
|
output.add(new ScoredResult(waypoint, score));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a single waypoint result.
|
||||||
|
*
|
||||||
|
* @param query the query to score for
|
||||||
|
* @param waypoint the results to score
|
||||||
|
* @return the score for the waypoint
|
||||||
|
*/
|
||||||
|
private double scoreWaypointResult(SearchQuery query, Waypoint waypoint) {
|
||||||
|
double score = 1.0;
|
||||||
|
|
||||||
|
Location location = waypoint.getLocation();
|
||||||
|
score *= getTitleBoost(query, waypoint.getName(), waypoint.getDescription(), waypoint.getCategory());
|
||||||
|
score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude());
|
||||||
|
score *= getTimeBoost(query, location.getTime());
|
||||||
|
|
||||||
|
// Score waypoints in the currently-selected track higher (searching inside the current track).
|
||||||
|
if (waypoint.getTrackId() != null && waypoint.getTrackId().equals(query.currentTrackId)) {
|
||||||
|
score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to the field(s) in which the match occured.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param name the name of the track or waypoint
|
||||||
|
* @param description the description of the track or waypoint
|
||||||
|
* @param category the category of the track or waypoint
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getTitleBoost(SearchQuery query, String name, String description, String category) {
|
||||||
|
// Title boost: track name > description > category.
|
||||||
|
double boost = 1.0;
|
||||||
|
if (name.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_NAME_PROMOTION;
|
||||||
|
}
|
||||||
|
if (description.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_DESCRIPTION_PROMOTION;
|
||||||
|
}
|
||||||
|
if (category.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
|
||||||
|
boost *= TRACK_CATEGORY_PROMOTION;
|
||||||
|
}
|
||||||
|
return boost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to the recency of the matched entity.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param timestamp the timestamp to calculate the boost for
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getTimeBoost(SearchQuery query, long timestamp) {
|
||||||
|
if (timestamp < OLDEST_ALLOWED_TIMESTAMP) {
|
||||||
|
// Safety: if timestamp is too old or invalid, don't rank based on time.
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Score recent tracks higher.
|
||||||
|
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
|
||||||
|
if (timeAgoHours > 0L) {
|
||||||
|
return squash(timeAgoHours);
|
||||||
|
} else {
|
||||||
|
// Should rarely happen (track recorded in the last hour).
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the boosting of the score due to proximity to a location.
|
||||||
|
*
|
||||||
|
* @param query the query to boost for
|
||||||
|
* @param latitude the latitude to calculate the boost for
|
||||||
|
* @param longitude the longitude to calculate the boost for
|
||||||
|
* @return the total boost to be applied to the result
|
||||||
|
*/
|
||||||
|
private double getDistanceBoost(SearchQuery query, double latitude, double longitude) {
|
||||||
|
if (query.currentLocation == null) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float[] distanceResults = new float[1];
|
||||||
|
|
||||||
|
Location.distanceBetween(
|
||||||
|
latitude, longitude,
|
||||||
|
query.currentLocation.getLatitude(), query.currentLocation.getLongitude(),
|
||||||
|
distanceResults);
|
||||||
|
|
||||||
|
// Score tracks close to the current location higher.
|
||||||
|
double distanceKm = distanceResults[0] * UnitConversions.M_TO_KM;
|
||||||
|
|
||||||
|
if (distanceKm > 0.0) {
|
||||||
|
// Use the inverse of the amortized distance.
|
||||||
|
return squash(distanceKm);
|
||||||
|
} else {
|
||||||
|
// Should rarely happen (distance is exactly 0).
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of a searchable query, along with all contextual data needed to execute it.
|
||||||
|
*/
|
||||||
|
public static class SearchQuery {
|
||||||
|
final String textQuery;
|
||||||
|
final Location currentLocation;
|
||||||
|
final Track.Id currentTrackId;
|
||||||
|
final long currentTimestamp;
|
||||||
|
|
||||||
|
public SearchQuery(String textQuery, Location currentLocation, Track.Id currentTrackId, long currentTimestamp) {
|
||||||
|
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
|
||||||
|
this.currentLocation = currentLocation;
|
||||||
|
this.currentTrackId = currentTrackId;
|
||||||
|
this.currentTimestamp = currentTimestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Description of a searchable result which has been retrieved and scored.
|
||||||
|
*/
|
||||||
|
public static class ScoredResult {
|
||||||
|
public final Track track;
|
||||||
|
public final Waypoint waypoint;
|
||||||
|
final double score;
|
||||||
|
|
||||||
|
ScoredResult(Track track, double score) {
|
||||||
|
this.track = track;
|
||||||
|
this.waypoint = null;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScoredResult(Waypoint waypoint, double score) {
|
||||||
|
this.track = null;
|
||||||
|
this.waypoint = waypoint;
|
||||||
|
this.score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ScoredResult ["
|
||||||
|
+ (track != null ? ("trackId=" + track.getId().getId() + ", ") : "")
|
||||||
|
+ (waypoint != null ? ("wptId=" + waypoint.getId().getId() + ", ") : "")
|
||||||
|
+ "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;
|
||||||
@@ -54,16 +54,18 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target number of track points displayed by the diagrams (recommended).
|
* Target number of track points displayed by the map overlay.
|
||||||
* We may display more than this number of points.
|
* We may display more than this number of points.
|
||||||
*/
|
*/
|
||||||
private static final int TARGET_DISPLAYED_TRACKPOINTS = 5000;
|
@Deprecated
|
||||||
|
private static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of markers to displayed in the diagrams.
|
* Maximum number of waypoints to displayed.
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
private static final int MAX_DISPLAYED_MARKERS = 128;
|
@Deprecated
|
||||||
|
private static final int MAX_DISPLAYED_WAYPOINTS = 128;
|
||||||
|
|
||||||
private static final String TAG = TrackDataHub.class.getSimpleName();
|
private static final String TAG = TrackDataHub.class.getSimpleName();
|
||||||
|
|
||||||
@@ -88,11 +90,11 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
|
|
||||||
// Registered listeners
|
// Registered listeners
|
||||||
private ContentObserver tracksTableObserver;
|
private ContentObserver tracksTableObserver;
|
||||||
private ContentObserver markersTableObserver;
|
private ContentObserver waypointsTableObserver;
|
||||||
private ContentObserver trackPointsTableObserver;
|
private ContentObserver trackPointsTableObserver;
|
||||||
|
|
||||||
public TrackDataHub(Context context) {
|
public TrackDataHub(Context context) {
|
||||||
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACKPOINTS);
|
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACK_POINTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -124,13 +126,13 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
};
|
};
|
||||||
contentResolver.registerContentObserver(TracksColumns.CONTENT_URI, false, tracksTableObserver);
|
contentResolver.registerContentObserver(TracksColumns.CONTENT_URI, false, tracksTableObserver);
|
||||||
|
|
||||||
markersTableObserver = new ContentObserver(handler) {
|
waypointsTableObserver = new ContentObserver(handler) {
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
notifyMarkersTableUpdate(trackDataManager.getListenerMarkers());
|
notifyWaypointsTableUpdate(trackDataManager.getListenerWaypoints());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
contentResolver.registerContentObserver(MarkerColumns.CONTENT_URI, false, markersTableObserver);
|
contentResolver.registerContentObserver(WaypointsColumns.CONTENT_URI, false, waypointsTableObserver);
|
||||||
|
|
||||||
trackPointsTableObserver = new ContentObserver(handler) {
|
trackPointsTableObserver = new ContentObserver(handler) {
|
||||||
@Override
|
@Override
|
||||||
@@ -163,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(markersTableObserver);
|
contentResolver.unregisterContentObserver(waypointsTableObserver);
|
||||||
contentResolver.unregisterContentObserver(trackPointsTableObserver);
|
contentResolver.unregisterContentObserver(trackPointsTableObserver);
|
||||||
|
|
||||||
if (handlerThread != null) {
|
if (handlerThread != null) {
|
||||||
@@ -189,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 markersTable, final boolean trackPointsTable_SampleIn, final boolean trackPointsTable_SampleOut) {
|
public void registerTrackDataListener(final TrackDataListener trackDataListener, final boolean tracksTable, final boolean waypointsTable, final boolean trackPointsTable_SampleIn, final boolean trackPointsTable_SampleOut) {
|
||||||
runInHandlerThread(() -> {
|
runInHandlerThread(() -> {
|
||||||
trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, markersTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut);
|
trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, waypointsTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut);
|
||||||
if (started) {
|
if (started) {
|
||||||
loadDataForListener(trackDataListener);
|
loadDataForListener(trackDataListener);
|
||||||
}
|
}
|
||||||
@@ -248,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());
|
||||||
notifyMarkersTableUpdate(trackDataManager.getListenerMarkers());
|
notifyWaypointsTableUpdate(trackDataManager.getListenerWaypoints());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,8 +277,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
|
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackDataManager.listensForMarkers(trackDataListener)) {
|
if (trackDataManager.listensForWaypoints(trackDataListener)) {
|
||||||
notifyMarkersTableUpdate(trackDataListeners);
|
notifyWaypointsTableUpdate(trackDataListeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,36 +298,36 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies marker table update.
|
* Notifies waypoint table update.
|
||||||
* Currently, reloads all the markers up to {@link #MAX_DISPLAYED_MARKERS}. To be run in the {@link #handler} thread.
|
* Currently, reloads all the waypoints up to {@link #MAX_DISPLAYED_WAYPOINTS}. 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 notifyMarkersTableUpdate(Set<TrackDataListener> trackDataListeners) {
|
private void notifyWaypointsTableUpdate(Set<TrackDataListener> trackDataListeners) {
|
||||||
if (trackDataListeners.isEmpty()) {
|
if (trackDataListeners.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TrackDataListener trackDataListener : trackDataListeners) {
|
for (TrackDataListener trackDataListener : trackDataListeners) {
|
||||||
trackDataListener.clearMarkers();
|
trackDataListener.clearWaypoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
try (Cursor cursor = contentProviderUtils.getMarkerCursor(selectedTrackId, null, MAX_DISPLAYED_MARKERS)) {
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(selectedTrackId, null, MAX_DISPLAYED_WAYPOINTS)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
Marker marker = contentProviderUtils.createMarker(cursor);
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
||||||
if (!LocationUtils.isValidLocation(marker.getLocation())) {
|
if (!LocationUtils.isValidLocation(waypoint.getLocation())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (TrackDataListener trackDataListener : trackDataListeners) {
|
for (TrackDataListener trackDataListener : trackDataListeners) {
|
||||||
trackDataListener.onNewMarker(marker);
|
trackDataListener.onNewWaypoint(waypoint);
|
||||||
}
|
}
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TrackDataListener trackDataListener : trackDataListeners) {
|
for (TrackDataListener trackDataListener : trackDataListeners) {
|
||||||
trackDataListener.onNewMarkersDone();
|
trackDataListener.onNewWaypointsDone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 markers.
|
* Called to clear previously sent waypoints.
|
||||||
*/
|
*/
|
||||||
void clearMarkers();
|
void clearWaypoints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a new marker is read.
|
* Called when a new waypoint is read.
|
||||||
*
|
*
|
||||||
* @param marker the marker
|
* @param waypoint the waypoint
|
||||||
*/
|
*/
|
||||||
void onNewMarker(Marker marker);
|
void onNewWaypoint(Waypoint waypoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when finish sending new markers.
|
* Called when finish sending new waypoints.
|
||||||
* This gets called after every batch of calls to {@link #clearMarkers()} and {@link #onNewMarker(Marker)}.
|
* This gets called after every batch of calls to {@link #clearWaypoints()} and {@link #onNewWaypoint(Waypoint)}.
|
||||||
*/
|
*/
|
||||||
void onNewMarkersDone();
|
void onNewWaypointsDone();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> dataListenerMarkers = new HashSet<>();
|
private final Set<TrackDataListener> dataListenerWaypoints = 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 markersTable, boolean trackPointsTable_SampleIn, boolean trackPointsTable_SampleOut) {
|
void registerTrackDataListener(final TrackDataListener trackDataListener, boolean tracksTable, boolean waypointsTable, boolean trackPointsTable_SampleIn, boolean trackPointsTable_SampleOut) {
|
||||||
if (tracksTable) dataListenerTracks.add(trackDataListener);
|
if (tracksTable) dataListenerTracks.add(trackDataListener);
|
||||||
if (markersTable) dataListenerMarkers.add(trackDataListener);
|
if (waypointsTable) dataListenerWaypoints.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);
|
||||||
dataListenerMarkers.add(trackDataListener);
|
dataListenerWaypoints.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() + dataListenerMarkers.size() + dataListenerTrackPoints_SampledIn.size() + dataListenerTrackPoints_SampledOut.size() > 0;
|
return dataListenerTracks.size() + dataListenerWaypoints.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(dataListenerMarkers);
|
listener.addAll(dataListenerWaypoints);
|
||||||
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 listensForMarkers(TrackDataListener listener) {
|
boolean listensForWaypoints(TrackDataListener listener) {
|
||||||
return dataListenerMarkers.contains(listener);
|
return dataListenerWaypoints.contains(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean listensForTrackPoints_SampledIn(TrackDataListener listener) {
|
boolean listensForTrackPoints_SampledIn(TrackDataListener listener) {
|
||||||
@@ -81,8 +81,8 @@ class TrackDataManager {
|
|||||||
return dataListenerTracks;
|
return dataListenerTracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<TrackDataListener> getListenerMarkers() {
|
Set<TrackDataListener> getListenerWaypoints() {
|
||||||
return dataListenerMarkers;
|
return dataListenerWaypoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<TrackDataListener> getListenerTrackPoints_SampledIn() {
|
Set<TrackDataListener> getListenerTrackPoints_SampledIn() {
|
||||||
|
|||||||