Compare commits
217 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df502f835c | |||
| dd6a4cd153 | |||
| 76656997ef | |||
| 32070a0bd6 | |||
| b9420d4dde | |||
| 1b810e1294 | |||
| f16d59bff6 | |||
| b5bf3aadc5 | |||
| f6a27a1522 | |||
| 83758fcd72 | |||
| 070917bc0b | |||
| 543a04dfdd | |||
| b4d5eb0eec | |||
| 9f84cc4952 | |||
| 09fe35f1ca | |||
| 27e50b2dd9 | |||
| 971460848b | |||
| 8bc2937bb4 | |||
| 0e9b68c2a1 | |||
| ba298c2afb | |||
| 79f5ed4dc3 | |||
| bbb2f874ee | |||
| 9af3759b07 | |||
| 1f6fe6bf0c | |||
| 7b291e85d7 | |||
| 0f72066bc0 | |||
| 70e0e7b216 | |||
| 7e50bb1b6d | |||
| a3eb107068 | |||
| 6133ef121a | |||
| c6d24e9534 | |||
| 441d3f721f | |||
| 3c212e4bcc | |||
| 50eb71bc1f | |||
| 0806821b5f | |||
| 36d0f17953 | |||
| 700adb7972 | |||
| a156369f8b | |||
| c0f6dc511b | |||
| 63e1461eba | |||
| 804e5f10c8 | |||
| 6cc7271a41 | |||
| f66194189b | |||
| 5d97d6d492 | |||
| f3a308e669 | |||
| a230504925 | |||
| 90e19e282c | |||
| b585bfe44c | |||
| 475e04eeb4 | |||
| f89b2b0efb | |||
| b94f3c176b | |||
| d1d729d287 | |||
| e23bbcc405 | |||
| 4129985b0f | |||
| 5cb7b623d9 | |||
| 3c52efadd6 | |||
| 73b4977051 | |||
| 422c2c02ce | |||
| d692329975 | |||
| 931574bc11 | |||
| caddbd8501 | |||
| 8ece2c8612 | |||
| 3a219321d1 | |||
| 35cb924a17 | |||
| b67e5bbd20 | |||
| 1db275230e | |||
| 5ac72092fb | |||
| bdd9ded1e8 | |||
| 759aeb6ed6 | |||
| 12023d51ae | |||
| c426785b90 | |||
| 09d960c7a1 | |||
| f37fe4a503 | |||
| 013e56bef0 | |||
| d6835ba766 | |||
| d7890cbc81 | |||
| 2b6886c534 | |||
| e77590a125 | |||
| ca330b2d80 | |||
| 20f1524701 | |||
| 96b2a15643 | |||
| ad59392e02 | |||
| 29f92b887c | |||
| 46328211c2 | |||
| 3760130d1f | |||
| 1094827fac | |||
| 36fcb44af5 | |||
| a683fbac4b | |||
| 64ad9019d1 | |||
| 1e80d5c8a8 | |||
| ac765b1ee8 | |||
| 850c4f0a9a | |||
| 4c32999752 | |||
| 76823b167a | |||
| 9994cfd260 | |||
| 1750236474 | |||
| fed6e31f9e | |||
| 9bafc7d4a1 | |||
| be58b48d20 | |||
| 303abac980 | |||
| d1a459efc2 | |||
| 96069e6f09 | |||
| 301a721329 | |||
| e8c6801a32 | |||
| 355f3ef614 | |||
| eaa6083586 | |||
| a0c5d02c51 | |||
| 76f9f5fc82 | |||
| 8e2598e93b | |||
| c0672c41df | |||
| 4c6601fbc5 | |||
| b99c87d1bd | |||
| 66ffbfe951 | |||
| 8cb01a1027 | |||
| 193e5eccbb | |||
| bf7fc28bb0 | |||
| e35cbc343a | |||
| 1c7b053c8c | |||
| 88cdcd4b47 | |||
| 1432fed8f9 | |||
| a14307b9a4 | |||
| 3bdce5aa08 | |||
| d7526f93b8 | |||
| 8932578b90 | |||
| 631ac9881d | |||
| 78334ed797 | |||
| b06da89654 | |||
| d6c2e78993 | |||
| 67d1ac551d | |||
| dcb6be717d | |||
| f2c04eec1a | |||
| a08311cf43 | |||
| 1e28f3aa86 | |||
| 426af41f2c | |||
| fd08a219f9 | |||
| 25b6aec0e9 | |||
| 3e40209d15 | |||
| 54ff94f905 | |||
| 7a4faf6b0d | |||
| 8922b00a5f | |||
| 702efa8693 | |||
| e7a8c897a3 | |||
| c59d1d87bd | |||
| a706efb8b3 | |||
| e391e83a00 | |||
| 2940bf20d8 | |||
| 081db5a58b | |||
| b34805272f | |||
| 98443dba99 | |||
| dd6e733d8c | |||
| 92d3439c3b | |||
| bd155cd600 | |||
| e14e53c0ae | |||
| 77a6a63174 | |||
| 40f2463f09 | |||
| e5d1048c8d | |||
| bf3b529fb2 | |||
| d0fde46027 | |||
| 0f348dff10 | |||
| d3f5080e73 | |||
| a453c8ec1e | |||
| 2a60aa8f59 | |||
| 41e7870509 | |||
| 57f66b77ab | |||
| 53572406e9 | |||
| a288255ab1 | |||
| 353c958ae0 | |||
| bc5ab0d7e5 | |||
| 9d915b5640 | |||
| 3aaee270eb | |||
| d53a7156e2 | |||
| 3af24ea3d0 | |||
| 5483212b06 | |||
| 419279fe5e | |||
| 501e899b27 | |||
| 3bb9edb009 | |||
| e52b1188a3 | |||
| 9e877e4762 | |||
| 5711757110 | |||
| e1c33dad43 | |||
| 4b2400774c | |||
| afd4a59622 | |||
| bdce437470 | |||
| 33479cda11 | |||
| 385cee3dc7 | |||
| d0bbd7655b | |||
| a83b8f62fe | |||
| 0cd9176703 | |||
| d8e526923d | |||
| 3a6f417662 | |||
| 7d807a7f11 | |||
| e9d5718495 | |||
| 0dd7f74a11 | |||
| 9bd4ba489c | |||
| dd55b50dad | |||
| 1719d21a5b | |||
| 75ef8d99d4 | |||
| ae06fd1680 | |||
| 2b03f7dea8 | |||
| 7591b012b4 | |||
| 0e089f49f3 | |||
| 5ad1856b13 | |||
| 965dbbe829 | |||
| 262c9886df | |||
| ac5a9ea65d | |||
| ed9b4c8f3d | |||
| 209aa6a8a5 | |||
| 81ee6ac1fa | |||
| 024dd603cb | |||
| 50654c6baf | |||
| 8c9bf60fc6 | |||
| a83eb0b983 | |||
| ab3cbfea0f | |||
| c93e5ca1a6 | |||
| c2841092da | |||
| 8b11e1d611 | |||
| 6375a851b2 |
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
liberapay: dennis.guse
|
||||
custom: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report issues of OpenTracks features
|
||||
title: ''
|
||||
labels: potential bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
If applicable:
|
||||
* add screenshots
|
||||
* add logcat output
|
||||
|
||||
**Technical information**
|
||||
- Device: [e.g. Nexus 7 (2013)]
|
||||
- OS: [e.g. LineageOS 15.1]
|
||||
- Version [e.g. v3.2.4]
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for OpenTracks
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Thanks for your contribution.
|
||||
|
||||
**Describe the pull request**
|
||||
A clear and concise description of what the pull request changes/adds.
|
||||
|
||||
**Link to the the issue**
|
||||
(If available): The link to the issue that this pull request solves.
|
||||
|
||||
**License agreement**
|
||||
By opening this pull request, you are providing your contribution under the _Apache License 2.0_ (see [LICENSE.md](LICENSE.md)).
|
||||
@@ -73,8 +73,12 @@ captures/
|
||||
.idea/workspace.xml
|
||||
|
||||
# Keystore files
|
||||
# Uncomment the following line if you do not want to check your keystore files in.
|
||||
#*.jks
|
||||
*.jks
|
||||
|
||||
# External native build folder generated in Android Studio 2.2 and later
|
||||
.externalNativeBuild
|
||||
|
||||
# Fastlane
|
||||
Appfile
|
||||
releasePlayStore/*
|
||||
Gemfile.lock
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RenderSettings">
|
||||
<option name="showDecorations" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,12 +1,46 @@
|
||||
#See https://docs.travis-ci.com/user/languages/android/
|
||||
#See https://github.com/ankidroid/Anki-Android/blob/master/.travis.yml
|
||||
language: android
|
||||
dist: trusty
|
||||
|
||||
env:
|
||||
global:
|
||||
- ADB_INSTALL_TIMEOUT=8
|
||||
- ABI=armeabi-v7a
|
||||
- EMU_FLAVOR=default
|
||||
# PATH order is incredibly important. e.g. the 'emulator' script exists in more than one place!
|
||||
- ANDROID_HOME=/usr/local/android-sdk
|
||||
- TOOLS=${ANDROID_HOME}/tools
|
||||
- PATH=${ANDROID_HOME}:${ANDROID_HOME}/emulator:${TOOLS}:${TOOLS}/bin:${ANDROID_HOME}/platform-tools:${PATH}
|
||||
matrix:
|
||||
- API=27 ABI=x86_64
|
||||
|
||||
android:
|
||||
components:
|
||||
# installing tools to start, then use `sdkmanager` below to get the rest
|
||||
- tools
|
||||
- build-tools-28.0.3
|
||||
- android-29
|
||||
|
||||
# Emulator Management: Create, Start and Wait
|
||||
#install:
|
||||
# - echo y | sdkmanager "platform-tools"
|
||||
# - echo y | sdkmanager --channel=4 "emulator"
|
||||
# - echo y | sdkmanager "system-images;android-$API;$EMU_FLAVOR;$ABI"
|
||||
# - echo no | avdmanager create avd --force -n test -k "system-images;android-$API;$EMU_FLAVOR;$ABI" -c 10M
|
||||
# - emulator -verbose -avd test -no-accel -no-snapshot -no-window -camera-back none -camera-front none -selinux permissive -qemu -m 2048 &
|
||||
# - android-wait-for-emulator
|
||||
# - adb shell input keyevent 82 &
|
||||
|
||||
script:
|
||||
- ./gradlew build
|
||||
#TODO: When all tests are passing, CI should also execute them.
|
||||
#- ./gradlew connectedCheck
|
||||
- ./gradlew clean build
|
||||
# - ./gradlew connectedCheck
|
||||
|
||||
sbefore_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
- $HOME/.android/build-cache
|
||||
@@ -1,12 +0,0 @@
|
||||
# OpenTrack: Developer information
|
||||
|
||||
## Create releases for FDroid
|
||||
|
||||
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
|
||||
2. Decide on _version name_ (semantic versioning)
|
||||
3. Manually update _version code_ and _version name_ in `AndroidManifest.xml` (used by FDroid)
|
||||
4. Create changelog (`_version code_.txt`)
|
||||
5. Create commit with all changes
|
||||
6. Push commits
|
||||
7. Tag newly create commit with _version name_
|
||||
8. Push the tagged to public repository
|
||||
@@ -2,17 +2,38 @@
|
||||
|
||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
||||
|
||||
<a href="https://f-droid.org/packages/de.dennisguse.opentracks">
|
||||
<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="60">
|
||||
</a>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Free</th>
|
||||
<th>Paid</th>
|
||||
<th>Donations</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://f-droid.org/packages/de.dennisguse.opentracks">
|
||||
<img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">
|
||||
<img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="60" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://liberapay.com/dennis.guse/donate">
|
||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Features:
|
||||
* __Tracking:__ track your sport and outdoor activities
|
||||
* __Photos and Markers:__ mark interesting locations while tracking (also with photos)
|
||||
* __Heart rate:__ supports bluetooth LE sensors
|
||||
* __Heart rate:__ supports Bluetooth LE sensors
|
||||
* __Export data:__ export tracks either as KMZ (incl. photos), KML or GPX
|
||||
* __No Internet access:__ Internet is not used
|
||||
* __Voice annoucements__
|
||||
* __Voice announcements__
|
||||
* __No advertising__
|
||||
* __Privacy:__
|
||||
* _does not contain any in-app analytics_
|
||||
@@ -26,9 +47,17 @@ _Please note:_
|
||||
_Tracks_ does not contain a _map_ (as this would required Internet access).
|
||||
To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)) needs to be installed.
|
||||
|
||||
<a href="https://liberapay.com/dennis.guse/donate">
|
||||
<img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" height="45">
|
||||
</a>
|
||||
## Custom Dashboards
|
||||
As of v3.3.1 OpenTracks enables to use custom dashboards.
|
||||
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
|
||||
|
||||
## Screenshots
|
||||
<div>
|
||||
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_1.png">
|
||||
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_2.png">
|
||||
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_3.png">
|
||||
<img width="20%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_4.png">
|
||||
</div>
|
||||
|
||||
## Project history
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# OpenTracks Data API
|
||||
|
||||
## Reason
|
||||
OpenTracks by itself only provides basic features for data visualization and also lacks functionality for analytics.
|
||||
By itself OpenTracks provides a basic dashboard that can show summary of a track (while recording as well as afterwards).
|
||||
Although this might be (mostly) sufficient, it has some limitations.
|
||||
This include limited configurability (e.g., ordering of shown elements) and the lack of Internet access.
|
||||
In fact, the later is considered a feature but severely limits the implementation presenting a map (at least in an easy way).
|
||||
|
||||
_NOTE:_ for showing a track on a map a workaround was implemented in OpenTracks.
|
||||
Data could be exported as KMZ and presented in 3rd-party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
|
||||
|
||||
For the time being, the OpenTracks Data API can be used to implement mainly custom dashboards.
|
||||
|
||||
## Implementation
|
||||
OpenTracks by default does not expose any data to 3rd-party applications.
|
||||
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s.
|
||||
For each, one URI is provided.
|
||||
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
|
||||
|
||||
Limitations:
|
||||
* only tracks selected by the user is exposed,
|
||||
* `Waypoints` are not exposed,
|
||||
* access is only granted temporarily and automatically revoked (see `FLAG_GRANT_READ_URI_PERMISSION`),
|
||||
* no write access is impossible, and
|
||||
* neither track recordings can be started, paused, or stopped.
|
||||
|
||||
All required information can be found in `IntentDashboardUtils.java`.
|
||||
|
||||
A _reference implementation_ is the [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard).
|
||||
@@ -0,0 +1,37 @@
|
||||
# OpenTracks: Developer information
|
||||
|
||||
## Craft new releases
|
||||
|
||||
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
|
||||
2. Decide on _version name_ (semantic versioning)
|
||||
3. Manually update _version code_ and _version name_ in `AndroidManifest.xml` (used by FDroid)
|
||||
4. Create changelog (`_version code_.txt`)
|
||||
5. Create commit with all changes
|
||||
6. Tag newly create commit with _version name_ (e.g., v3.2.1)
|
||||
7. Push commits and tags to public repository
|
||||
8. Add changelog to tag (Github releases)
|
||||
|
||||
## F-Droid
|
||||
Application id: de.dennisguse.opentracks
|
||||
|
||||
It if it properly tagged F-Droid takes care of building the app and publishing.
|
||||
Usually, it takes two days before the update is published (build, signed, and published).
|
||||
|
||||
The recent build activity can be found [here](https://f-droid.org/wiki/index.php?title=Special:RecentChanges&days=7&from=&hidebots=0&hideanons=1&hideliu=1&limit=500)
|
||||
|
||||
## Google Play Store
|
||||
Application id: de.dennisguse.opentracks.playstore
|
||||
|
||||
Requirement: `gem install fastlane`
|
||||
|
||||
1. Build signed apk
|
||||
(requires keystore)
|
||||
2. Upload signed apk to [Google Play Console](https://play.google.com/apps/publish)
|
||||
3. Update store meta data (e.g., description and screenshots)
|
||||
`fastlane supply --version_code VERSION_CODE --skip_upload_apk`
|
||||
(use the version code from the manifest)
|
||||
(requires API key)
|
||||
|
||||
## Translations (localization)
|
||||
|
||||
Translations of the OpenTracks are handled in Transifex: [https://www.transifex.com/opentracks](https://www.transifex.com/opentracks/).
|
||||
@@ -6,7 +6,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,14 @@ def getVersionName = { ->
|
||||
try {
|
||||
def stdout = new ByteArrayOutputStream()
|
||||
exec {
|
||||
commandLine 'git', 'describe', '--tags', '--dirty'
|
||||
commandLine 'git', 'describe', '--tags'
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().trim()
|
||||
exec {
|
||||
commandLine 'git', 'log', '--format="%h"', '-n 1'
|
||||
standardOutput = stdout
|
||||
}
|
||||
return stdout.toString().replace('\n', '-').replace('"', '').trim()
|
||||
}
|
||||
catch (ignored) {
|
||||
return null
|
||||
@@ -50,6 +54,11 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
manifestPlaceholders = [
|
||||
applicationLogo: "@drawable/ic_logo_color_24dp",
|
||||
applicationName: "@string/app_name"
|
||||
]
|
||||
|
||||
//Useful for debugging.
|
||||
//versionCode getVersionCode()
|
||||
|
||||
@@ -65,24 +74,30 @@ android {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
versionNameSuffix "-debug"
|
||||
manifestPlaceholders = [
|
||||
applicationLogo: "@drawable/ic_logo_black_24dp",
|
||||
applicationName: "OpenTracks (Debug)"
|
||||
]
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
}
|
||||
|
||||
releasePlayStore {
|
||||
applicationIdSuffix ".playstore"
|
||||
versionNameSuffix "-PlayStore"
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable "AppCompatResource" // TODO Disable complaint to use androidx's SearchView as it is null.
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.preference:preference:1.1.0'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
@@ -94,4 +109,5 @@ dependencies {
|
||||
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:3.0.0'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
@startuml
|
||||
title
|
||||
State of OpenTracks
|
||||
end title
|
||||
|
||||
legend right
|
||||
SettingsActivity can be reached
|
||||
from every activity showing a menu.
|
||||
endlegend
|
||||
|
||||
[*] --> TrackListActivity
|
||||
TrackListActivity --> TrackDetailActivity : Start recording
|
||||
TrackListActivity --> SearchListActivity
|
||||
TrackListActivity --> MarkerListActivity : Show all markers
|
||||
TrackListActivity --> TrackDetailActivity : Show track
|
||||
TrackListActivity --> TrackDeleteActivity
|
||||
|
||||
SearchListActivity --> MarkerDetailActivity : Search result
|
||||
SearchListActivity --> TrackDetailActivity : Search result
|
||||
|
||||
TrackDetailActivity --> TrackDeleteActivity
|
||||
TrackDetailActivity --> TrackEditActivity
|
||||
TrackDetailActivity --> MarkerListActivity
|
||||
TrackDetailActivity --> MarkerEditActivity : Add marker
|
||||
TrackDetailActivity --> CameraIntent : Add marker (with photo)
|
||||
TrackDetailActivity --> ShareFileIntent
|
||||
TrackDetailActivity --> DashboardIntent : Database cursor
|
||||
TrackDetailActivity --> TrackOnMapIntent : KMZ
|
||||
|
||||
MarkerListActivity --> MarkerDetailActivity
|
||||
|
||||
MarkerDetailActivity --> MarkerEditActivity
|
||||
MarkerDetailActivity --> CoordinateOnMapIntent : URI
|
||||
|
||||
SettingsActivity --> AboutActivity
|
||||
SettingsActivity --> HelpActivity
|
||||
SettingsActivity --> ExportActivity
|
||||
SettingsActivity --> ImportActivity
|
||||
|
||||
@enduml
|
||||
@@ -0,0 +1,52 @@
|
||||
@startuml
|
||||
class Track {
|
||||
-id
|
||||
-name
|
||||
-description
|
||||
-category
|
||||
-numberOfPoints
|
||||
-icon
|
||||
}
|
||||
Track o-- TrackPoint
|
||||
Track o-- Waypoint
|
||||
Track "1" - "1" TripStatistics
|
||||
|
||||
class TripStatistics {
|
||||
- startTime
|
||||
- stopTime
|
||||
- totalDistance
|
||||
- totalTime
|
||||
- movingTime
|
||||
- maxSpeed
|
||||
- totalElevationGain
|
||||
}
|
||||
|
||||
class TrackPoint extends android.location.Location {
|
||||
- id
|
||||
- trackId
|
||||
- longitude
|
||||
- latitude
|
||||
- time
|
||||
- altitude
|
||||
- accuracy
|
||||
- speed
|
||||
- bearing
|
||||
- sensor_heartrate
|
||||
- sensor_cadence
|
||||
- sensor_power
|
||||
}
|
||||
|
||||
class Waypoint {
|
||||
- id
|
||||
- trackId
|
||||
- name
|
||||
- description
|
||||
- category
|
||||
- icon
|
||||
- length
|
||||
- duration
|
||||
- location
|
||||
- tripStatistics
|
||||
- photoUrl
|
||||
}
|
||||
@enduml
|
||||
@@ -17,7 +17,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -43,7 +43,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -41,7 +41,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -33,7 +33,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@@ -39,7 +39,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -18,7 +18,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@@ -21,7 +21,7 @@
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,38 @@
|
||||
# This file contains the fastlane.tools configuration
|
||||
# You can find the documentation at https://docs.fastlane.tools
|
||||
#
|
||||
# For a list of all available actions, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/actions
|
||||
#
|
||||
# For a list of all available plugins, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/plugins/available-plugins
|
||||
#
|
||||
|
||||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
desc "Runs all the tests"
|
||||
lane :test do
|
||||
gradle(task: "test")
|
||||
end
|
||||
|
||||
desc "Submit a new Beta Build to Crashlytics Beta"
|
||||
lane :beta do
|
||||
gradle(task: "clean assembleRelease")
|
||||
crashlytics
|
||||
|
||||
# sh "your_script.sh"
|
||||
# You can also use other beta testing services here
|
||||
end
|
||||
|
||||
desc "Deploy a new version to the Google Play"
|
||||
lane :deploy do
|
||||
gradle(task: "clean assembleRelease")
|
||||
upload_to_play_store
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
|
||||
Erfasst deine Kilometer, ohne dass es jemand anderes tut.
|
||||
|
||||
Funktionen & Features:
|
||||
* zeichnet Sport und andere Outdooraktivitäten auf
|
||||
* unterstützt das Markieren interessanter Orte, inkl. Foto-Funktion
|
||||
* zeichnet deine Herzfrequenz auf (unterstützt Bluetooth-LE-Sensoren)
|
||||
* exportiert deine Daten als KML oder GPX
|
||||
* funktioniert ganz ohne Internet
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
|
||||
@@ -1,4 +1,4 @@
|
||||
v3.2: OpenTracks
|
||||
v3.2.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Removed Widget
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.2.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Settings are now on a single page
|
||||
|
||||
Developer:
|
||||
- Settings moved to Androidx compat (uses fragments)
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.2.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Removed aggregated stats
|
||||
- Migrated to Material Design
|
||||
|
||||
Bugfix:
|
||||
- Fixed export of sensor data
|
||||
- Fixed import of incomplete sensor data
|
||||
|
||||
Developer:
|
||||
- Replaced actionbar with toolbar
|
||||
- Migrated to androidx appcompat (incl. Material Design)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.2.3: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Sharing: use Android Sharesheet
|
||||
- Sharing: share multiple tracks as individual files
|
||||
- Use Android ShareSheet
|
||||
- Heart rate sensor (Bluetooth LE): automatic reconnect
|
||||
|
||||
Bugfix:
|
||||
- Heart rate sensor (Bluetooth LE): disconnect properly
|
||||
- Heart rate wasn't always shown while recording
|
||||
|
||||
Developer:
|
||||
- Enabled TravisCI instrumentation tests
|
||||
@@ -0,0 +1,5 @@
|
||||
v3.2.4: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Settings activity crashes
|
||||
Removed possibility to change voice/split frequency from the context menu
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.2.5: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Disable activity change
|
||||
- Add permission for Android 10: recording location data
|
||||
|
||||
Bugfix:
|
||||
- Fix reset settings
|
||||
|
||||
Developer:
|
||||
- Preferences
|
||||
- if Settings cannot be loaded reset all preferences
|
||||
- moved access to preferences to PreferencesUtil
|
||||
- moved default of preferences from code to xml
|
||||
- use SummaryProvider
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.2.6: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Heart rate monitor: scan for available devices
|
||||
- Remove shortcut to Bluetooth settings
|
||||
|
||||
Developer:
|
||||
- Preferences: moved default of preferences from code to xml
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.7: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Notification show GPS accuracy
|
||||
- Show heads-up notification if GPS accuracy drops beyond threshold
|
||||
- Always show chart by time as well as by distance
|
||||
- Added help (in Settings)
|
||||
|
||||
Bugfix:
|
||||
- TrackDetailActivity: disabled non-working landscape mode (temporary)
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.8: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Chart: zoom via gestures
|
||||
|
||||
Bugfixes:
|
||||
- Export via KMZ did not export picture filenames correctly
|
||||
- Sharing multiple tracks did not include KMZ files
|
||||
- MarkerDetailActivity's UI was not shown
|
||||
- Settings crashed if preference of heart rate was not initialized
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.9: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Option to keep screen on while recording
|
||||
- Markers: removed split markers completely
|
||||
- Voice announcement now work without READ_PHONE_STATE permission
|
||||
|
||||
Bugfixes:
|
||||
- Statistics: heart rate was not shown
|
||||
- Statistics: time was briefly reset to 0s
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.2.10: OpenTracks
|
||||
|
||||
Changes:
|
||||
- ChartView is not configurable anymore
|
||||
- After reboot: removed automatic restart of an unfinished recording
|
||||
|
||||
Bugfix:
|
||||
- Speech announcements use MAY_DUCK (again)
|
||||
- ChartView shows markers (again)
|
||||
- Fixed landscape layout of TrackListActivity
|
||||
- Request legacy file API behavior (necessary for Android 10)
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.2.11: OpenTracks
|
||||
|
||||
Bug:
|
||||
- Max speed is identical to avg speed
|
||||
|
||||
Bugfix:
|
||||
- Crash on heart rate sensor selection
|
||||
|
||||
Developer:
|
||||
- Replaced TabHost by TabsLayout
|
||||
- Removed special first waypoint
|
||||
- Removed duplicate translations
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.3.0: OpenTracks
|
||||
|
||||
Breaking changes:
|
||||
- Pictures are now stored in app-private storage (do not show up other picture apps)
|
||||
NOTE: Pictures taking with previous version cannot be shown
|
||||
- Localization for Spanish (Latin America) only works on Android 7.0 or later
|
||||
|
||||
Changes:
|
||||
- Import/Export via Storage Access Framework (avoids READ/WRITE_EXTERNAL_STORAGE permission)
|
||||
|
||||
Developer:
|
||||
- Cleaned internal data structures
|
||||
- Use separate icons/app names for debug version
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.3.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Android 10: on uninstall allow to keep app data
|
||||
- Introduced Dashboard API: map icon can be used to open current track in external app
|
||||
(requires installation of another app like OSMDashboard)
|
||||
|
||||
Bugfix:
|
||||
- App label was broken (introduced in v3.3.0)
|
||||
|
||||
Developer:
|
||||
- Documentation for Dashboard API can be found in README_DEVELOPER.md
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.3.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Export: overwrites files now
|
||||
|
||||
Bugfix:
|
||||
- Always remove recording notification
|
||||
|
||||
Developer:
|
||||
- On API 26+ java.time is used for parsing and writing dates
|
||||
- Refactoring of TrackRecordingService
|
||||
@@ -3,6 +3,6 @@ OpenTracks is a sport tracking application that completely respects your privacy
|
||||
Features:
|
||||
* Sport tracking: track your sport and outdoor activities
|
||||
* Photos and markers: mark interesting locations while tracking
|
||||
* Heart rate: supports bluetooth LE sensors
|
||||
* Export data: Export tracks either as KML or GPX
|
||||
* Heart rate: supports Bluetooth LE sensors
|
||||
* Export data: export tracks either as KML or GPX
|
||||
* No Internet access: Internet is not used
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -0,0 +1,8 @@
|
||||
OpenTracks es un tracker deportivo que respeta tu privacidad.
|
||||
|
||||
Funciones:
|
||||
* Tracker deportivo: haz un seguimiento de tus actividades al aire libre
|
||||
* Marca lugares de interés, incluso agrega fotos a los marcadores
|
||||
* Pulsómetro (compatible con dispositivos Bluetooth LE)
|
||||
* Exporta tus rutas como KML o GPX
|
||||
* Completamente offline, no necesitas internet
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks es un tracker deportivo que respeta tu privacidad.
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
@@ -0,0 +1,9 @@
|
||||
OpenTracks est une application de suivi d'activités sportives qui respecte totalement votre vie privée.
|
||||
|
||||
Fonctionnalités :
|
||||
|
||||
* Suivi d'activité : enregistre vos activités sportives et de plein air
|
||||
* Photos et repères : ajout de photos et de repères pendant l'enregistrement de l'activité
|
||||
* Rythme cardiaque : support des capteurs Bluetooth LE
|
||||
* Export : export des traces au format KML ou GPX
|
||||
* Pas d'accès internet requis : Internet n'est pas nécessaire pour le fonctionnement de l'application
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
|
||||
|
After Width: | Height: | Size: 125 KiB |
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
/**
|
||||
* Tests for the BootReceiver.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BootReceiverTest {
|
||||
private static final String SERVICE_NAME = "de.dennisguse.opentracks.services.TrackRecordingService";
|
||||
|
||||
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
|
||||
/**
|
||||
* Tests the behavior when receive notification which is the phone boot.
|
||||
*/
|
||||
@Test
|
||||
public void testOnReceive_startService() {
|
||||
// Make sure no TrackRecordingService
|
||||
Intent stopIntent = new Intent(context, TrackRecordingService.class);
|
||||
context.stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
|
||||
BootReceiver bootReceiver = new BootReceiver();
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_BOOT_COMPLETED);
|
||||
bootReceiver.onReceive(context, intent);
|
||||
// Check if the service is started
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the behavior when receive notification which is not the phone boot.
|
||||
*/
|
||||
@Test
|
||||
public void testOnReceive_noStartService() {
|
||||
// Make sure no TrackRecordingService
|
||||
Intent stopIntent = new Intent(context, TrackRecordingService.class);
|
||||
context.stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
|
||||
BootReceiver bootReceiver = new BootReceiver();
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_BUG_REPORT);
|
||||
bootReceiver.onReceive(context, intent);
|
||||
|
||||
// Check if the service is not started
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a service is started in a context.
|
||||
*
|
||||
* @param context the context for checking a service
|
||||
*/
|
||||
private boolean isServiceExisted(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
Assert.assertNotNull(activityManager);
|
||||
|
||||
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||
|
||||
for (int i = 0; i < serviceList.size(); i++) {
|
||||
RunningServiceInfo serviceInfo = serviceList.get(i);
|
||||
ComponentName componentName = serviceInfo.service;
|
||||
if (componentName.getClassName().equals(SERVICE_NAME)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ package de.dennisguse.opentracks;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
|
||||
/**
|
||||
@@ -60,16 +60,16 @@ public class TrackStubUtils {
|
||||
*
|
||||
* @return a track stub.
|
||||
*/
|
||||
public static SensorDataSetLocation createSensorDataSetLocation() {
|
||||
public static TrackPoint createSensorDataSetLocation() {
|
||||
return createSensorDataSetLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SensorDataSetLocation} stub with specified values.
|
||||
* Creates a {@link TrackPoint} stub with specified values.
|
||||
*
|
||||
* @return a SensorDataSetLocation stub.
|
||||
*/
|
||||
private static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude, double altitude) {
|
||||
private static TrackPoint createSensorDataSetLocation(double latitude, double longitude, double altitude) {
|
||||
// Initial Location
|
||||
Location loc = new Location(LOCATION_PROVIDER);
|
||||
loc.setLatitude(latitude);
|
||||
@@ -81,6 +81,6 @@ public class TrackStubUtils {
|
||||
loc.setBearing(INITIAL_BEARING);
|
||||
SensorDataSet sd = new SensorDataSet(Float.NaN, Float.NaN);
|
||||
|
||||
return new SensorDataSetLocation(loc, sd);
|
||||
return new TrackPoint(loc, sd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package de.dennisguse.opentracks;
|
||||
package de.dennisguse.opentracks.chart;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -23,6 +23,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* Tests {@link ChartValueSeries}.
|
||||
*
|
||||
@@ -27,29 +27,30 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link DescriptionGeneratorImpl}.
|
||||
* Tests for {@link DescriptionGenerator}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DescriptionGeneratorImplTest {
|
||||
public class DescriptionGeneratorTest {
|
||||
|
||||
private static final long START_TIME = 1288721514000L;
|
||||
private DescriptionGeneratorImpl descriptionGenerator;
|
||||
private DescriptionGenerator descriptionGenerator;
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
descriptionGenerator = new DescriptionGeneratorImpl(ApplicationProvider.getApplicationContext());
|
||||
descriptionGenerator = new DescriptionGenerator(ApplicationProvider.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track, boolean)}.
|
||||
* Tests {@link DescriptionGenerator#generateTrackDescription(Track, boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateTrackDescription() {
|
||||
@@ -68,7 +69,7 @@ public class DescriptionGeneratorImplTest {
|
||||
track.setTripStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/dennisguse/opentracks'>OpenTracks</a><p>"
|
||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks</a><p>"
|
||||
+ "Name: -<br>"
|
||||
+ "Activity type: hiking<br>"
|
||||
+ "Description: -<br>"
|
||||
@@ -91,44 +92,9 @@ public class DescriptionGeneratorImplTest {
|
||||
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(TripStatistics)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateWaypointDescription() {
|
||||
Waypoint waypoint = new Waypoint();
|
||||
TripStatistics stats = new TripStatistics();
|
||||
stats.setTotalDistance(20000);
|
||||
stats.setTotalTime(600000);
|
||||
stats.setMovingTime(300000);
|
||||
stats.setMaxSpeed(100);
|
||||
stats.setMaxElevation(550);
|
||||
stats.setMinElevation(-500);
|
||||
stats.setTotalElevationGain(6000);
|
||||
stats.setMaxGrade(0.42);
|
||||
stats.setMinGrade(0.11);
|
||||
stats.setStartTime(START_TIME);
|
||||
waypoint.setTripStatistics(stats);
|
||||
String expected = "Total distance: 20.00 km (12.4 mi)\n"
|
||||
+ "Total time: 10:00\n"
|
||||
+ "Moving time: 05:00\n"
|
||||
+ "Average speed: 120.00 km/h (74.6 mi/h)\n"
|
||||
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
|
||||
+ "Max speed: 360.00 km/h (223.7 mi/h)\n"
|
||||
+ "Average pace: 0:30 min/km (0:48 min/mi)\n"
|
||||
+ "Average moving pace: 0:15 min/km (0:24 min/mi)\n"
|
||||
+ "Fastest pace: 0:10 min/km (0:16 min/mi)\n"
|
||||
+ "Max elevation: 550 m (1804 ft)\n"
|
||||
+ "Min elevation: -500 m (-1640 ft)\n"
|
||||
+ "Elevation gain: 6000 m (19685 ft)\n"
|
||||
+ "Max grade: 42 %\n"
|
||||
+ "Min grade: 11 %\n"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "\n";
|
||||
Assert.assertEquals(expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writeDistance(double, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteDistance() {
|
||||
@@ -138,7 +104,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writeTime(long, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteTime() {
|
||||
@@ -148,7 +114,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writeSpeed(double, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteSpeed() {
|
||||
@@ -158,7 +124,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writeElevation(double, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteElevation() {
|
||||
@@ -168,7 +134,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writePace(double, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writePace(double, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWritePace() {
|
||||
@@ -178,7 +144,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder, int, String)}.
|
||||
* Tests {@link DescriptionGenerator#writeGrade(double, StringBuilder, int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade() {
|
||||
@@ -188,7 +154,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder, int, String)} with a NaN.
|
||||
* Tests {@link DescriptionGenerator#writeGrade(double, StringBuilder, int, String)} with a NaN.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade_nan() {
|
||||
@@ -198,7 +164,7 @@ public class DescriptionGeneratorImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder, int, String)} with an infinite number.
|
||||
* Tests {@link DescriptionGenerator#writeGrade(double, StringBuilder, int, String)} with an infinite number.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade_infinite() {
|
||||
@@ -36,6 +36,9 @@ 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.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
@@ -58,7 +61,7 @@ public class SearchEngineTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
providerUtils = new ContentProviderUtils(context);
|
||||
engine = new SearchEngine(providerUtils);
|
||||
}
|
||||
|
||||
@@ -80,10 +83,6 @@ public class SearchEngineTest {
|
||||
stats.setStopTime(NOW);
|
||||
}
|
||||
|
||||
int latitude = (int) ((HERE.getLatitude() + distance) * 1E6);
|
||||
int longitude = (int) ((HERE.getLongitude() + distance) * 1E6);
|
||||
stats.setBounds(latitude, longitude, latitude, longitude);
|
||||
|
||||
Uri uri = providerUtils.insertTrack(track);
|
||||
return ContentUris.parseId(uri);
|
||||
}
|
||||
@@ -209,20 +208,6 @@ public class SearchEngineTest {
|
||||
assertWaypointResult(descriptionWaypointId, results.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTrackDistance() {
|
||||
// All results match text, but they're at difference distances from the user.
|
||||
long farFarAwayId = insertTrack("aa", 0.3);
|
||||
long nearId = insertTrack("ab", 0.1);
|
||||
long farId = insertTrack("ac", 0.2);
|
||||
|
||||
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
|
||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||
|
||||
// Distance order.
|
||||
assertTrackResults(results, nearId, farId, farFarAwayId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWaypointDistance() {
|
||||
// All results match text, but they're at difference distances from the user.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package de.dennisguse.opentracks.content.data;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
public class TestDataUtil {
|
||||
|
||||
public static final double INITIAL_LATITUDE = 37.0;
|
||||
public static final double INITIAL_LONGITUDE = -57.0;
|
||||
public static final double ALTITUDE_INTERVAL = 2.5;
|
||||
|
||||
/**
|
||||
* Simulates a track which is used for testing.
|
||||
*
|
||||
* @param id the id of the track
|
||||
* @param numPoints the location number in the track
|
||||
* @return the simulated track
|
||||
*/
|
||||
public static Track getTrack(long id, int numPoints) {
|
||||
Track track = new Track();
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id);
|
||||
track.setNumberOfPoints(numPoints);
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
track.addLocation(createLocation(i));
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a location.
|
||||
*
|
||||
* @param i the index to set the value of location.
|
||||
* @return created location
|
||||
*/
|
||||
public static Location createLocation(int i) {
|
||||
Location loc = new Location("test");
|
||||
loc.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
loc.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
loc.setAccuracy((float) i / 100.0f);
|
||||
loc.setAltitude(i * ALTITUDE_INTERVAL);
|
||||
loc.setTime(i + 1);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.content;
|
||||
package de.dennisguse.opentracks.content.provider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
@@ -26,7 +26,10 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider.DatabaseHelper;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider.DatabaseHelper;
|
||||
|
||||
/**
|
||||
* Tests {@link CustomContentProvider}.
|
||||
@@ -13,7 +13,7 @@
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package de.dennisguse.opentracks.content;
|
||||
package de.dennisguse.opentracks.content.provider;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
@@ -35,32 +35,32 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* A unit test for {@link ContentProviderUtilsImpl}.
|
||||
* A unit test for {@link ContentProviderUtils}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomContentProviderUtilsImplTest {
|
||||
public class CustomContentProviderUtilsTest {
|
||||
private static final String NAME_PREFIX = "test name";
|
||||
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
|
||||
private static final String TEST_DESC = "Test Desc!";
|
||||
private static final String TEST_DESC_NEW = "Test Desc new!";
|
||||
private static final double INITIAL_LATITUDE = 37.0;
|
||||
private static final double INITIAL_LONGITUDE = -57.0;
|
||||
private static final double ALTITUDE_INTERVAL = 2.5;
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils providerUtils;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Mock
|
||||
private Cursor cursorMock;
|
||||
@@ -70,22 +70,22 @@ public class CustomContentProviderUtilsImplTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noPoints() {
|
||||
testIterator(1, 0, 1, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 0, 1, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_customFactory() {
|
||||
final Location location = new Location("test_location");
|
||||
final TrackPoint location = new TrackPoint("test_location");
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
testIterator(1, 15, 4, false, new LocationFactory() {
|
||||
testIterator(1, 15, 4, false, new TrackPointFactory() {
|
||||
@Override
|
||||
public Location createLocation() {
|
||||
public TrackPoint createLocation() {
|
||||
counter.incrementAndGet();
|
||||
return location;
|
||||
}
|
||||
@@ -106,45 +106,45 @@ public class CustomContentProviderUtilsImplTest {
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noBatchAscending() {
|
||||
testIterator(1, 50, 100, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 50, 100, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noBatchDescending() {
|
||||
testIterator(1, 50, 100, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 50, 100, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_batchAscending() {
|
||||
testIterator(1, 50, 11, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 50, 11, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_batchDescending() {
|
||||
testIterator(1, 50, 11, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 50, 11, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_largeTrack() {
|
||||
testIterator(1, 20000, 2000, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(1, 20000, 2000, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
private List<Location> testIterator(long trackId, int numPoints, int batchSize, boolean descending, LocationFactory locationFactory) {
|
||||
private List<Location> testIterator(long trackId, int numPoints, int batchSize, boolean descending, TrackPointFactory trackPointFactory) {
|
||||
long lastPointId = initializeTrack(trackId, numPoints);
|
||||
((ContentProviderUtilsImpl) providerUtils).setDefaultCursorBatchSize(batchSize);
|
||||
((ContentProviderUtils) contentProviderUtils).setDefaultCursorBatchSize(batchSize);
|
||||
List<Location> locations = new ArrayList<Location>(numPoints);
|
||||
try (LocationIterator it = providerUtils.getTrackPointLocationIterator(trackId, -1L, descending, locationFactory)) {
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, -1L, descending, trackPointFactory)) {
|
||||
while (it.hasNext()) {
|
||||
Location loc = it.next();
|
||||
Assert.assertNotNull(loc);
|
||||
locations.add(loc);
|
||||
// Make sure the IDs are returned in the right order.
|
||||
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
|
||||
: lastPointId - numPoints + locations.size(), it.getLocationId());
|
||||
: lastPointId - numPoints + locations.size(), it.getTrackPointId());
|
||||
}
|
||||
Assert.assertEquals(numPoints, locations.size());
|
||||
}
|
||||
@@ -156,8 +156,8 @@ public class CustomContentProviderUtilsImplTest {
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id);
|
||||
track.setNumberOfPoints(numPoints);
|
||||
providerUtils.insertTrack(track);
|
||||
track = providerUtils.getTrack(id);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
track = contentProviderUtils.getTrack(id);
|
||||
Assert.assertNotNull(track);
|
||||
|
||||
Location[] locations = new Location[numPoints];
|
||||
@@ -169,15 +169,15 @@ public class CustomContentProviderUtilsImplTest {
|
||||
loc.setAltitude(i * 2.5);
|
||||
locations[i] = loc;
|
||||
}
|
||||
providerUtils.bulkInsertTrackPoint(locations, numPoints, id);
|
||||
contentProviderUtils.bulkInsertTrackPoint(locations, numPoints, id);
|
||||
|
||||
// Load all inserted locations.
|
||||
long lastPointId = -1;
|
||||
int counter = 0;
|
||||
try (LocationIterator it = providerUtils.getTrackPointLocationIterator(id, -1L, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY)) {
|
||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, -1L, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY)) {
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
lastPointId = it.getLocationId();
|
||||
lastPointId = it.getTrackPointId();
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createTrack(Cursor)}.
|
||||
* Tests the method {@link ContentProviderUtils#createTrack(Cursor)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateTrack() {
|
||||
@@ -211,23 +211,22 @@ public class CustomContentProviderUtilsImplTest {
|
||||
String name = NAME_PREFIX + trackId;
|
||||
when(cursorMock.getString(columnIndex++)).thenReturn(name);
|
||||
|
||||
Track track = providerUtils.createTrack(cursorMock);
|
||||
Track track = contentProviderUtils.createTrack(cursorMock);
|
||||
Assert.assertEquals(trackId, track.getId());
|
||||
Assert.assertEquals(name, track.getName());
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#deleteAllTracks(Context)}
|
||||
* Tests the method {@link ContentProviderUtils#deleteAllTracks(Context)}
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteAllTracks() {
|
||||
// Insert track, points and waypoint at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
providerUtils.insertWaypoint(waypoint);
|
||||
contentProviderUtils.insertWaypoint(waypoint);
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
Assert.assertEquals(1, tracksCursor.getCount());
|
||||
@@ -236,7 +235,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||
Assert.assertEquals(1, waypointCursor.getCount());
|
||||
// Delete all.
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
// Check whether all have been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
Assert.assertEquals(0, tracksCursor.getCount());
|
||||
@@ -247,21 +246,21 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#deleteTrack(Context, long)}.
|
||||
* Tests the method {@link ContentProviderUtils#deleteTrack(Context, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteTrack() {
|
||||
// Insert three tracks, points of two tracks and way point of one track.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
|
||||
providerUtils.insertTrack(track);
|
||||
insertTrackWithLocations(getTrack(trackId + 1, 10));
|
||||
insertTrackWithLocations(getTrack(trackId + 2, 10));
|
||||
contentProviderUtils.insertTrack(track);
|
||||
insertTrackWithLocations(TestDataUtil.getTrack(trackId + 1, 10));
|
||||
insertTrackWithLocations(TestDataUtil.getTrack(trackId + 2, 10));
|
||||
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setTrackId(trackId);
|
||||
providerUtils.insertWaypoint(waypoint);
|
||||
contentProviderUtils.insertWaypoint(waypoint);
|
||||
|
||||
ContentResolver contentResolver = context.getContentResolver();
|
||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
@@ -271,7 +270,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||
Assert.assertEquals(1, waypointCursor.getCount());
|
||||
// Delete one track.
|
||||
providerUtils.deleteTrack(context, trackId);
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
// Check whether all data of a track has been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
Assert.assertEquals(2, tracksCursor.getCount());
|
||||
@@ -282,63 +281,62 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getAllTracks()}
|
||||
* Tests the method {@link ContentProviderUtils#getAllTracks()}
|
||||
*/
|
||||
@Test
|
||||
public void testGetAllTracks() {
|
||||
int initialTrackNumber = providerUtils.getAllTracks().size();
|
||||
int initialTrackNumber = contentProviderUtils.getAllTracks().size();
|
||||
long trackId = System.currentTimeMillis();
|
||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
||||
List<Track> allTracks = providerUtils.getAllTracks();
|
||||
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||
List<Track> allTracks = contentProviderUtils.getAllTracks();
|
||||
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
|
||||
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastTrack()}
|
||||
* Tests the method {@link ContentProviderUtils#getLastTrack()}
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
||||
Assert.assertEquals(trackId, providerUtils.getLastTrack().getId());
|
||||
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrack(long)}
|
||||
* Tests the method {@link ContentProviderUtils#getTrack(long)}
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
||||
Assert.assertNotNull(providerUtils.getTrack(trackId));
|
||||
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||
Assert.assertNotNull(contentProviderUtils.getTrack(trackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#updateTrack(Track)}
|
||||
* Tests the method {@link ContentProviderUtils#updateTrack(Track)}
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 0);
|
||||
Track track = TestDataUtil.getTrack(trackId, 0);
|
||||
String nameOld = "name1";
|
||||
String nameNew = "name2";
|
||||
track.setName(nameOld);
|
||||
providerUtils.insertTrack(track);
|
||||
Assert.assertEquals(nameOld, providerUtils.getTrack(trackId).getName());
|
||||
contentProviderUtils.insertTrack(track);
|
||||
Assert.assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
|
||||
track.setName(nameNew);
|
||||
providerUtils.updateTrack(track);
|
||||
Assert.assertEquals(nameNew, providerUtils.getTrack(trackId).getName());
|
||||
contentProviderUtils.updateTrack(track);
|
||||
Assert.assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createContentValues(Waypoint)}.
|
||||
* Tests the method {@link ContentProviderUtils#createContentValues(Waypoint)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateContentValues_waypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
// Bottom
|
||||
long startTime = 1000L;
|
||||
// AverageSpeed
|
||||
@@ -355,12 +353,13 @@ public class CustomContentProviderUtilsImplTest {
|
||||
statistics.setMinElevation(1200.0);
|
||||
statistics.setMaxGrade(15.0);
|
||||
statistics.setMinGrade(minGrade);
|
||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
||||
|
||||
track.setTripStatistics(statistics);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
// Insert at first.
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setType(WaypointType.STATISTICS);
|
||||
waypoint.setTripStatistics(statistics);
|
||||
|
||||
Location location = new Location("test");
|
||||
location.setLatitude(22);
|
||||
@@ -368,9 +367,9 @@ public class CustomContentProviderUtilsImplTest {
|
||||
location.setAccuracy((float) 1 / 100.0f);
|
||||
location.setAltitude(2.5);
|
||||
waypoint.setLocation(location);
|
||||
providerUtils.insertWaypoint(waypoint);
|
||||
contentProviderUtils.insertWaypoint(waypoint);
|
||||
|
||||
ContentProviderUtilsImpl contentProviderUtils = new ContentProviderUtilsImpl(contentResolverMock);
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
||||
|
||||
long waypointId = System.currentTimeMillis();
|
||||
waypoint.setId(waypointId);
|
||||
@@ -378,12 +377,10 @@ public class CustomContentProviderUtilsImplTest {
|
||||
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
|
||||
Assert.assertEquals(22 * 1000000, contentValues.get(WaypointsColumns.LONGITUDE));
|
||||
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
|
||||
Assert.assertEquals(startTime, contentValues.get(WaypointsColumns.STARTTIME));
|
||||
Assert.assertEquals(minGrade, contentValues.get(WaypointsColumns.MINGRADE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createWaypoint(Cursor)}.
|
||||
* Tests the method {@link ContentProviderUtils#createWaypoint(Cursor)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWaypoint() {
|
||||
@@ -410,59 +407,44 @@ public class CustomContentProviderUtilsImplTest {
|
||||
long trackId = 11L;
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||
|
||||
Waypoint waypoint = providerUtils.createWaypoint(cursorMock);
|
||||
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock);
|
||||
Assert.assertEquals(id, waypoint.getId());
|
||||
Assert.assertEquals(name, waypoint.getName());
|
||||
Assert.assertEquals(trackId, waypoint.getTrackId());
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
|
||||
* {@link ContentProviderUtils#deleteWaypoint(long)}
|
||||
* when there is only one waypoint in the track.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteWaypoint_onlyOneWayPoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
// Insert at first.
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
waypoint1.setDescription(TEST_DESC);
|
||||
waypoint1.setTrackId(trackId);
|
||||
waypoint1.setType(WaypointType.STATISTICS);
|
||||
providerUtils.insertWaypoint(waypoint1);
|
||||
contentProviderUtils.insertWaypoint(waypoint1);
|
||||
|
||||
// Delete
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
|
||||
contentProviderUtils.deleteWaypoint(1);
|
||||
|
||||
@Override
|
||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
||||
return CustomContentProviderUtilsImplTest.MOCK_DESC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track aTrack, boolean html) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
providerUtils.deleteWaypoint(context, 1, descriptionGenerator);
|
||||
|
||||
Assert.assertNull(providerUtils.getWaypoint(1));
|
||||
Assert.assertNull(contentProviderUtils.getWaypoint(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
|
||||
* {@link ContentProviderUtils#deleteWaypoint(long)}
|
||||
* when there is more than one waypoint in the track.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteWaypoint_hasNextWayPoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
|
||||
TripStatistics statistics = new TripStatistics();
|
||||
statistics.setStartTime(1000L);
|
||||
@@ -476,184 +458,113 @@ public class CustomContentProviderUtilsImplTest {
|
||||
statistics.setMinElevation(1200.0);
|
||||
statistics.setMaxGrade(15.0);
|
||||
statistics.setMinGrade(-25.0);
|
||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
||||
|
||||
track.setTripStatistics(statistics);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
|
||||
// Insert at first.
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
waypoint1.setDescription(Long.toString(trackId));
|
||||
waypoint1.setDescription(MOCK_DESC);
|
||||
waypoint1.setTrackId(trackId);
|
||||
waypoint1.setType(WaypointType.STATISTICS);
|
||||
waypoint1.setTripStatistics(statistics);
|
||||
long waypoint1Id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint1));
|
||||
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
|
||||
|
||||
Waypoint waypoint2 = new Waypoint();
|
||||
waypoint2.setDescription(Long.toString(trackId));
|
||||
waypoint2.setDescription(MOCK_DESC);
|
||||
waypoint2.setTrackId(trackId);
|
||||
waypoint2.setType(WaypointType.STATISTICS);
|
||||
waypoint2.setTripStatistics(statistics);
|
||||
long waypoint2Id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint2));
|
||||
|
||||
long waypoint2Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2));
|
||||
|
||||
// Delete
|
||||
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
|
||||
@Override
|
||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
||||
return CustomContentProviderUtilsImplTest.MOCK_DESC;
|
||||
}
|
||||
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||
contentProviderUtils.deleteWaypoint(waypoint1Id);
|
||||
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track aTrack, boolean html) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Assert.assertNotNull(providerUtils.getWaypoint(waypoint1Id));
|
||||
providerUtils.deleteWaypoint(context, waypoint1Id, descriptionGenerator);
|
||||
Assert.assertNull(providerUtils.getWaypoint(waypoint1Id));
|
||||
|
||||
Assert.assertEquals(CustomContentProviderUtilsImplTest.MOCK_DESC, providerUtils.getWaypoint(waypoint2Id)
|
||||
.getDescription());
|
||||
Assert.assertEquals(MOCK_DESC, contentProviderUtils.getWaypoint(waypoint2Id).getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getFirstWaypointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFirstWaypointId() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
waypoint1.setTrackId(trackId);
|
||||
long waypoint1id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint1));
|
||||
|
||||
Waypoint waypoint2 = new Waypoint();
|
||||
waypoint2.setTrackId(trackId);
|
||||
long waypoint2id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint2));
|
||||
|
||||
Assert.assertEquals(-1L, providerUtils.getFirstWaypointId(-1));
|
||||
Assert.assertEquals(waypoint1id, providerUtils.getFirstWaypointId(trackId));
|
||||
Assert.assertEquals(waypoint2id, providerUtils.getLastWaypoint(trackId, WaypointType.WAYPOINT).getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getNextWaypointNumber(long, WaypointType)}.
|
||||
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetNextWaypointNumber() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
waypoint1.setType(WaypointType.STATISTICS);
|
||||
waypoint1.setTrackId(trackId);
|
||||
Waypoint waypoint2 = new Waypoint();
|
||||
waypoint2.setType(WaypointType.WAYPOINT);
|
||||
waypoint2.setTrackId(trackId);
|
||||
Waypoint waypoint3 = new Waypoint();
|
||||
waypoint3.setType(WaypointType.STATISTICS);
|
||||
waypoint3.setTrackId(trackId);
|
||||
Waypoint waypoint4 = new Waypoint();
|
||||
waypoint4.setType(WaypointType.WAYPOINT);
|
||||
waypoint4.setTrackId(trackId);
|
||||
providerUtils.insertWaypoint(waypoint1);
|
||||
providerUtils.insertWaypoint(waypoint2);
|
||||
providerUtils.insertWaypoint(waypoint3);
|
||||
providerUtils.insertWaypoint(waypoint4);
|
||||
contentProviderUtils.insertWaypoint(waypoint1);
|
||||
contentProviderUtils.insertWaypoint(waypoint2);
|
||||
contentProviderUtils.insertWaypoint(waypoint3);
|
||||
contentProviderUtils.insertWaypoint(waypoint4);
|
||||
|
||||
Assert.assertEquals(2, providerUtils.getNextWaypointNumber(trackId, WaypointType.STATISTICS));
|
||||
Assert.assertEquals(3, providerUtils.getNextWaypointNumber(trackId, WaypointType.WAYPOINT));
|
||||
Assert.assertEquals(4, contentProviderUtils.getNextWaypointNumber(trackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtils#getLastWaypoint(long, WaypointType)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastStatisticsWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
waypoint1.setTrackId(trackId);
|
||||
waypoint1.setType(WaypointType.STATISTICS);
|
||||
waypoint1.setDescription("Desc1");
|
||||
Waypoint waypoint2 = new Waypoint();
|
||||
waypoint2.setTrackId(trackId);
|
||||
waypoint2.setType(WaypointType.STATISTICS);
|
||||
waypoint2.setDescription("Desc2");
|
||||
Waypoint waypoint3 = new Waypoint();
|
||||
waypoint3.setTrackId(trackId);
|
||||
waypoint3.setType(WaypointType.WAYPOINT);
|
||||
waypoint3.setDescription("Desc3");
|
||||
providerUtils.insertWaypoint(waypoint1);
|
||||
providerUtils.insertWaypoint(waypoint2);
|
||||
providerUtils.insertWaypoint(waypoint3);
|
||||
|
||||
Assert.assertEquals("Desc2", providerUtils.getLastWaypoint(trackId, WaypointType.STATISTICS).getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#insertWaypoint(Waypoint)} and
|
||||
* {@link ContentProviderUtilsImpl#getWaypoint(long)}.
|
||||
* Tests the method {@link ContentProviderUtils#insertWaypoint(Waypoint)} and
|
||||
* {@link ContentProviderUtils#getWaypoint(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testInsertAndGetWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
long waypointId = ContentUris.parseId(providerUtils.insertWaypoint(waypoint));
|
||||
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
|
||||
|
||||
Assert.assertEquals(TEST_DESC, providerUtils.getWaypoint(waypointId).getDescription());
|
||||
Assert.assertEquals(TEST_DESC, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#updateWaypoint(Waypoint)}.
|
||||
* Tests the method {@link ContentProviderUtils#updateWaypoint(Waypoint)}.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
// Insert at first.
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setDescription(TEST_DESC);
|
||||
waypoint.setTrackId(trackId);
|
||||
long waypointId = ContentUris.parseId(providerUtils.insertWaypoint(waypoint));
|
||||
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
|
||||
|
||||
// Update
|
||||
waypoint = providerUtils.getWaypoint(waypointId);
|
||||
waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||
waypoint.setDescription(TEST_DESC_NEW);
|
||||
providerUtils.updateWaypoint(waypoint);
|
||||
contentProviderUtils.updateWaypoint(waypoint);
|
||||
|
||||
Assert.assertEquals(TEST_DESC_NEW, providerUtils.getWaypoint(waypointId).getDescription());
|
||||
Assert.assertEquals(TEST_DESC_NEW, contentProviderUtils.getWaypoint(waypointId).getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#bulkInsertTrackPoint(Location[], int, long)}.
|
||||
* Tests the method {@link ContentProviderUtils#bulkInsertTrackPoint(Location[], int, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testBulkInsertTrackPoint() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
|
||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), -1, trackId);
|
||||
Assert.assertEquals(20, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), 8, trackId);
|
||||
Assert.assertEquals(28, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), -1, trackId);
|
||||
Assert.assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), 8, trackId);
|
||||
Assert.assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createTrackPoint(Cursor)}.
|
||||
* Tests the method {@link ContentProviderUtils#createTrackPoint(Cursor)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateTrackPoint() {
|
||||
@@ -704,206 +615,146 @@ public class CustomContentProviderUtilsImplTest {
|
||||
byte[] sensor = "Sensor state".getBytes();
|
||||
when(cursorMock.getBlob(index++)).thenReturn(sensor);
|
||||
|
||||
Location location = providerUtils.createTrackPoint(cursorMock);
|
||||
Location location = contentProviderUtils.createTrackPoint(cursorMock);
|
||||
Assert.assertEquals((double) longitude, location.getLongitude(), 0.01);
|
||||
Assert.assertEquals((double) latitude, location.getLatitude(), 0.01);
|
||||
Assert.assertEquals(time, location.getTime(), 0.01);
|
||||
Assert.assertEquals(speed, location.getSpeed(), 0.01);
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtilsImpl#insertTrackPoint(Location, long)}.
|
||||
* {@link ContentProviderUtils#insertTrackPoint(Location, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testInsertTrackPoint() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
|
||||
providerUtils.insertTrackPoint(createLocation(22), trackId);
|
||||
Assert.assertEquals(11, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
contentProviderUtils.insertTrackPoint(TestDataUtil.createLocation(22), trackId);
|
||||
Assert.assertEquals(11, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getFirstTrackPointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFirstTrackPointId() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
|
||||
Assert.assertNotEquals(-1L, providerUtils.getFirstTrackPointId(trackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastTrackPointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastTrackPointId() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
|
||||
Assert.assertTrue(providerUtils.getFirstTrackPointId(trackId) < providerUtils.getLastTrackPointId(trackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastValidTrackPoint(long)}.
|
||||
* Tests the method {@link ContentProviderUtils#getLastValidTrackPoint(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastValidTrackPoint() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
insertTrackWithLocations(track);
|
||||
|
||||
Location lastLocation = providerUtils.getLastValidTrackPoint(trackId);
|
||||
Location lastLocation = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
checkLocation(9, lastLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)} in descending.
|
||||
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in descending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointCursor_desc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
trackpointIds[i] = ContentUris.parseId(providerUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
}
|
||||
|
||||
Cursor cursor = providerUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
|
||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[1], 5, true);
|
||||
Assert.assertEquals(2, cursor.getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)} in ascending.
|
||||
* Tests the method {@link ContentProviderUtils#getTrackPointCursor(long, long, int, boolean)} in ascending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointCursor_asc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
trackpointIds[i] = ContentUris.parseId(providerUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
}
|
||||
|
||||
Cursor cursor = providerUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
|
||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds[8], 5, false);
|
||||
Assert.assertEquals(2, cursor.getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in descending.
|
||||
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, TrackPointFactory)} in descending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointLocationIterator_desc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
trackpointIds[i] = ContentUris.parseId(providerUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
}
|
||||
|
||||
long startTrackPointId = trackpointIds[9];
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
Assert.assertTrue(locationIterator.hasNext());
|
||||
Location location = locationIterator.next();
|
||||
Assert.assertEquals(startTrackPointId - i, locationIterator.getLocationId());
|
||||
Assert.assertEquals(startTrackPointId - i, locationIterator.getTrackPointId());
|
||||
checkLocation((trackpointIds.length - 1) - i, location);
|
||||
}
|
||||
Assert.assertFalse(locationIterator.hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in ascending.
|
||||
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, TrackPointFactory)} in ascending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointLocationIterator_asc() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
providerUtils.insertTrack(track);
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
|
||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
trackpointIds[i] = ContentUris.parseId(providerUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
trackpointIds[i] = ContentUris.parseId(contentProviderUtils.insertTrackPoint(track.getLocations().get(i), track.getId()));
|
||||
}
|
||||
|
||||
long startTrackPointId = trackpointIds[0];
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||
for (int i = 0; i < trackpointIds.length; i++) {
|
||||
Assert.assertTrue(locationIterator.hasNext());
|
||||
Location location = locationIterator.next();
|
||||
Assert.assertEquals(startTrackPointId + i, locationIterator.getLocationId());
|
||||
Assert.assertEquals(startTrackPointId + i, locationIterator.getTrackPointId());
|
||||
|
||||
checkLocation(i, location);
|
||||
}
|
||||
Assert.assertFalse(locationIterator.hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a track which is used for testing.
|
||||
*
|
||||
* @param id the id of the track
|
||||
* @param numPoints the location number in the track
|
||||
* @return the simulated track
|
||||
*/
|
||||
private Track getTrack(long id, int numPoints) {
|
||||
Track track = new Track();
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id);
|
||||
track.setNumberOfPoints(numPoints);
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
track.addLocation(createLocation(i));
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a location.
|
||||
*
|
||||
* @param i the index to set the value of location.
|
||||
* @return created location
|
||||
*/
|
||||
private Location createLocation(int i) {
|
||||
Location loc = new Location("test");
|
||||
loc.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
loc.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
loc.setAccuracy((float) i / 100.0f);
|
||||
loc.setAltitude(i * ALTITUDE_INTERVAL);
|
||||
return loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the value of a location.
|
||||
*
|
||||
* @param i the index of this location which created in the method {@link CustomContentProviderUtilsImplTest#getTrack(long, int)}
|
||||
* @param i the index of this location which created in the method {@link TestDataUtil#getTrack(long, int)}
|
||||
* @param location the location to be checked
|
||||
*/
|
||||
private void checkLocation(int i, Location location) {
|
||||
Assert.assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
||||
Assert.assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
||||
Assert.assertEquals(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
||||
Assert.assertEquals(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
||||
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
|
||||
Assert.assertEquals(i * ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
||||
Assert.assertEquals(i * TestDataUtil.ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -912,7 +763,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* @param track track to be inserted
|
||||
*/
|
||||
private void insertTrackWithLocations(Track track) {
|
||||
providerUtils.insertTrack(track);
|
||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||
contentProviderUtils.insertTrack(track);
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,21 @@
|
||||
package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.location.Location;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.ChartView;
|
||||
import de.dennisguse.opentracks.TrackStubUtils;
|
||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
||||
import de.dennisguse.opentracks.chart.ChartView;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
@@ -44,10 +47,23 @@ public class ChartFragmentTest {
|
||||
|
||||
private ChartFragment chartFragment;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
chartFragment = new ChartFragment();
|
||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext()));
|
||||
boolean chartByDistance = false;
|
||||
chartFragment = new ChartFragment(chartByDistance) {
|
||||
};
|
||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
||||
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
||||
}
|
||||
|
||||
@@ -56,7 +72,7 @@ public class ChartFragmentTest {
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_sensorIncorrect() {
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
@@ -80,7 +96,7 @@ public class ChartFragmentTest {
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_sensorCorrect() {
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
@@ -107,17 +123,17 @@ public class ChartFragmentTest {
|
||||
// By distance.
|
||||
chartFragment.setChartByDistance(true);
|
||||
// Resets last location and writes first location.
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The second is a same location, just different time.
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The third location is a new location, and use metric.
|
||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation3.setLatitude(23);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||
|
||||
@@ -129,7 +145,7 @@ public class ChartFragmentTest {
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
|
||||
// The fourth location is a new location, and use metric.
|
||||
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation4.setLatitude(24);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation4);
|
||||
|
||||
@@ -151,12 +167,12 @@ public class ChartFragmentTest {
|
||||
chartFragment.setMetricUnits(false);
|
||||
|
||||
// The first is a same location, just different time.
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The second location is a new location, and use imperial.
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation2.setLatitude(23);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
|
||||
@@ -170,7 +186,7 @@ public class ChartFragmentTest {
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
|
||||
// The third location is a new location, and use imperial.
|
||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation3.setLatitude(24);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||
|
||||
@@ -190,11 +206,11 @@ public class ChartFragmentTest {
|
||||
public void testFillDataPoint_time() {
|
||||
// By time
|
||||
chartFragment.setChartByDistance(false);
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
long timeSpan = 222;
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals((double) timeSpan, point[0], 0.01);
|
||||
@@ -205,7 +221,7 @@ public class ChartFragmentTest {
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_elevation() {
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
/*
|
||||
* At first, clear old points of elevation, so give true to the second parameter.
|
||||
@@ -217,7 +233,7 @@ public class ChartFragmentTest {
|
||||
/*
|
||||
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
||||
*/
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
||||
@@ -235,7 +251,7 @@ public class ChartFragmentTest {
|
||||
* At first, clear old points of speed, so give true to the second parameter.
|
||||
* It will not be filled in to the speed buffer.
|
||||
*/
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(128.5f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
@@ -244,7 +260,7 @@ public class ChartFragmentTest {
|
||||
* Tests the logic when both metricUnits and reportSpeed are true.
|
||||
* This location will be filled into speed buffer.
|
||||
*/
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
/*
|
||||
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
|
||||
@@ -264,12 +280,12 @@ public class ChartFragmentTest {
|
||||
chartFragment.setMetricUnits(false);
|
||||
|
||||
// First data point is not added to the speed buffer
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(100.0f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
/*
|
||||
* Add a time span here to make sure the second point and the speed is valid.
|
||||
@@ -290,12 +306,12 @@ public class ChartFragmentTest {
|
||||
chartFragment.setReportSpeed(false);
|
||||
|
||||
// First data point is not added to the speed buffer
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(100.0f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
/*
|
||||
* Add a time span here to make sure the second point and the speed is valid.
|
||||
@@ -314,7 +330,7 @@ public class ChartFragmentTest {
|
||||
public void testFillDataPoint_pace_zeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartFragment.setReportSpeed(false);
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation.setSpeed(0);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
|
||||
|
||||
@@ -32,11 +32,11 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.SimpleTimeZone;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
||||
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 static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
@@ -57,9 +57,9 @@ public abstract class AbstractTestFileTrackImporter {
|
||||
static final double TRACK_ELEVATION = 324.0;
|
||||
|
||||
static final String TRACK_TIME_0 = "2010-04-22T18:21:00Z";
|
||||
static final String TRACK_TIME_1 = "2010-04-22T18:21:50.123";
|
||||
static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123";
|
||||
static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123";
|
||||
static final String TRACK_TIME_1 = "2010-04-22T18:21:50.123Z";
|
||||
static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123Z";
|
||||
static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123Z";
|
||||
|
||||
static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
|
||||
static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
|
||||
@@ -101,8 +101,6 @@ public abstract class AbstractTestFileTrackImporter {
|
||||
*/
|
||||
protected void expectFirstTrackPoint(Location location, long trackId, long trackPointId) {
|
||||
when(contentProviderUtils.bulkInsertTrackPoint(location != null ? (Location[]) any() : (Location[]) any(), eq(1), eq(trackId))).thenReturn(1);
|
||||
when(contentProviderUtils.getFirstTrackPointId(trackId)).thenReturn(trackPointId);
|
||||
when(contentProviderUtils.getLastTrackPointId(trackId)).thenReturn(trackPointId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +110,7 @@ public abstract class AbstractTestFileTrackImporter {
|
||||
* @param lastTrack true if it is the last track in the gpx
|
||||
* @param trackId the track id
|
||||
*/
|
||||
protected void expectUpdateTrack(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, long trackId) {
|
||||
protected void expectTrackUpdate(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, long trackId) {
|
||||
contentProviderUtils.updateTrack(trackCaptor.capture());
|
||||
|
||||
when(contentProviderUtils.insertWaypoint((Waypoint) any())).thenReturn(WAYPOINT_ID_O_URI);
|
||||
@@ -128,7 +126,5 @@ public abstract class AbstractTestFileTrackImporter {
|
||||
if (time != -1L) {
|
||||
Assert.assertEquals(time, track.getTripStatistics().getStartTime());
|
||||
}
|
||||
Assert.assertNotSame(-1, track.getStartId());
|
||||
Assert.assertNotSame(-1, track.getStopId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.LargeTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
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.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||
* <p>
|
||||
* TODO: test ignores {@link de.dennisguse.opentracks.stats.TripStatistics} for now.
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ExportImportTest {
|
||||
|
||||
private static final String TAG = ExportImportTest.class.getSimpleName();
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
private static final String TRACK_ICON = "the track icon";
|
||||
private static final String TRACK_CATEGORY = "the category";
|
||||
private static final String TRACK_DESCRIPTION = "the description";
|
||||
private final List<Waypoint> waypoints = new ArrayList<>();
|
||||
private long importTrackId;
|
||||
private long trackId = System.currentTimeMillis();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||
track.setIcon(TRACK_ICON);
|
||||
track.setCategory(TRACK_CATEGORY);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
contentProviderUtils.insertTrack(track);
|
||||
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setName("the waypoint " + i);
|
||||
waypoint.setDescription("the waypoint description " + i);
|
||||
waypoint.setCategory("the waypoint category" + i);
|
||||
waypoint.setIcon("the waypoing icon" + i);
|
||||
waypoint.setPhotoUrl("the photo url" + i);
|
||||
waypoint.setTrackId(trackId);
|
||||
waypoint.setLocation(track.getLocations().get(i));
|
||||
contentProviderUtils.insertWaypoint(waypoint);
|
||||
|
||||
waypoints.add(waypoint);
|
||||
}
|
||||
|
||||
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(trackId));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kml_only_track() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kml_with_trackdetail() {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL;
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, null);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
trackExporter.writeTrack(context, outputStream);
|
||||
|
||||
System.out.println(outputStream.toString());
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context, -1L);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getLocations(), importedTrack.getLocations());
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
|
||||
// 2. waypoints
|
||||
assertWaypoints();
|
||||
|
||||
//TODO Check absolute time of trackpoints
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kml_with_trackdetail_and_sensordata() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_only_track() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
||||
// TODO
|
||||
Log.e(TAG, "Test not implemented.");
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx() {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.GPX;
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, null);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
trackExporter.writeTrack(context, outputStream);
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
importTrackId = trackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
Track trackImported = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(trackImported);
|
||||
assertEquals(track.getLocations(), trackImported.getLocations());
|
||||
assertEquals(track.getCategory(), trackImported.getCategory());
|
||||
assertEquals(track.getDescription(), trackImported.getDescription());
|
||||
assertEquals(track.getName(), trackImported.getName());
|
||||
|
||||
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
|
||||
//assertEquals(track.getIcon(), trackImported.getIcon());
|
||||
|
||||
// 2. waypoints
|
||||
assertWaypoints();
|
||||
|
||||
//TODO Check absolute time of trackpoints
|
||||
}
|
||||
|
||||
private void assertWaypoints() {
|
||||
assertEquals(waypoints.size(), contentProviderUtils.getWaypointCount(importTrackId));
|
||||
|
||||
List<Waypoint> importedWaypoints = contentProviderUtils.getWaypoints(importTrackId);
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
Waypoint waypoint = waypoints.get(i);
|
||||
Waypoint importedWaypoint = importedWaypoints.get(i);
|
||||
assertEquals(waypoint.getCategory(), importedWaypoint.getCategory());
|
||||
assertEquals(waypoint.getDescription(), importedWaypoint.getDescription());
|
||||
// assertEquals(waypoint.getIcon(), importedWaypoint.getIcon()); // TODO for KML
|
||||
assertEquals(waypoint.getName(), importedWaypoint.getName());
|
||||
assertEquals("", importedWaypoint.getPhotoUrl());
|
||||
|
||||
assertEquals(waypoint.getLocation().getLatitude(), importedWaypoint.getLocation().getLatitude(), 0.001);
|
||||
assertEquals(waypoint.getLocation().getLongitude(), importedWaypoint.getLocation().getLongitude(), 0.001);
|
||||
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,13 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -84,8 +84,7 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
|
||||
@Test
|
||||
public void testOneTrackOneSegment() throws Exception {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
// given
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||
|
||||
@@ -95,25 +94,28 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
// A flush happens at the end
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(1), eq(TRACK_ID_0))).thenReturn(1);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
// when
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneTrackTwoSegments() throws Exception {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
// given
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
@@ -121,12 +123,18 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
// A flush happens at the end
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
// when
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
@@ -135,30 +143,33 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneTrackTwoSegmentsNoTime() {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
// given
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
// when
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
verify(contentProviderUtils);
|
||||
// then
|
||||
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
Assert.assertEquals(0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
|
||||
}
|
||||
|
||||
@@ -197,9 +208,7 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
|
||||
// For the following, use StubReturn since we don't care whether they are invoked or not.
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), anyInt(), anyLong())).thenReturn(1);
|
||||
when(contentProviderUtils.getFirstTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_0);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
contentProviderUtils.deleteTrack(context, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
|
||||
|
||||
@@ -27,11 +27,11 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -68,8 +68,7 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
|
||||
@Test
|
||||
public void testOneTrackOneSegment() throws Exception {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
// given
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||
|
||||
@@ -77,27 +76,31 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
when(contentProviderUtils.bulkInsertTrackPoint(new Location[]{location1}, eq(1), eq(TRACK_ID_0))).thenReturn(1);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(1), eq(TRACK_ID_0))).thenReturn(1);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
// when
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
|
||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneTrackTwoSegments() throws Exception {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
// given
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
|
||||
contentProviderUtils.clearTrack(context, TRACK_ID_0);
|
||||
@@ -106,12 +109,18 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
// A flush happens at the end
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
expectTrackUpdate(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
// when
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
|
||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||
|
||||
// then
|
||||
verify(contentProviderUtils, atLeastOnce()).updateTrack(trackCaptor.capture());
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
@@ -120,7 +129,6 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,19 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* Tests the {@link AdaptiveLocationListenerPolicy}.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
public class AdaptiveLocationListenerPolicyTest extends TestCase {
|
||||
@RunWith(JUnit4.class)
|
||||
public class AdaptiveLocationListenerPolicyTest {
|
||||
|
||||
private static final long MIN = 1000;
|
||||
private static final long MAX = 3000;
|
||||
@@ -31,12 +36,11 @@ public class AdaptiveLocationListenerPolicyTest extends TestCase {
|
||||
private static final long NEW_IDLE_TIME_NORMAL = 5000;
|
||||
private static final long NEW_IDLE_TIME_SMALL = 2000;
|
||||
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
|
||||
private AdaptiveLocationListenerPolicy adocationListenerPolicy;
|
||||
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
adocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||
@Before
|
||||
public void setUp() {
|
||||
adaptiveLocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,27 +52,27 @@ public class AdaptiveLocationListenerPolicyTest extends TestCase {
|
||||
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testGetDesiredPollingInterval() {
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||
assertEquals(MAX, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||
Assert.assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||
// 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,
|
||||
adocationListenerPolicy.getDesiredPollingInterval());
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
|
||||
Assert.assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance_m()}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetMinDistance() {
|
||||
assertEquals(MIN_DISTANCE, adocationListenerPolicy.getMinDistance_m());
|
||||
Assert.assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TrackRecordingServiceNotificationManagerTest {
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private Location locationMock;
|
||||
|
||||
@Mock
|
||||
private NotificationCompat.Builder notificationCompatBuilder;
|
||||
|
||||
@Mock
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
@Test
|
||||
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
||||
when(locationMock.hasAccuracy()).thenReturn(true);
|
||||
when(locationMock.getAccuracy()).thenReturn(999f);
|
||||
when(notificationCompatBuilder.setContentText(anyString())).thenReturn(notificationCompatBuilder);
|
||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean())).thenReturn(notificationCompatBuilder);
|
||||
|
||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||
|
||||
// when
|
||||
subject.updateLocation(context, locationMock, 100);
|
||||
subject.updateLocation(context, locationMock, 100);
|
||||
subject.updateLocation(context, locationMock, 1000);
|
||||
subject.updateLocation(context, locationMock, 100);
|
||||
|
||||
// then
|
||||
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
|
||||
verify(notificationCompatBuilder, times(2)).setOnlyAlertOnce(false);
|
||||
}
|
||||
}
|
||||
@@ -15,14 +15,12 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -38,19 +36,16 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
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.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
@@ -59,7 +54,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* <p>
|
||||
* ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* //TODO ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -72,19 +67,12 @@ public class TrackRecordingServiceTest {
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils providerUtils;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private long trackId = Math.abs(new Random().nextLong());
|
||||
private final long trackId = Math.abs(new Random().nextLong());
|
||||
|
||||
static Intent createStartIntent(Context context) {
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setClass(context, TrackRecordingService.class);
|
||||
return startIntent;
|
||||
}
|
||||
|
||||
static void updateAutoResumePrefs(Context context, int attempts, int timeoutMins) {
|
||||
PreferencesUtils.setInt(context, R.string.auto_resume_track_current_retry_key, attempts);
|
||||
PreferencesUtils.setInt(context, R.string.auto_resume_track_timeout_key, timeoutMins);
|
||||
return new Intent(context, TrackRecordingService.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -94,29 +82,26 @@ public class TrackRecordingServiceTest {
|
||||
};
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Disable auto resume by default.
|
||||
updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, 0);
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
// Reset service (if some previous test failed)
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
if (service.isRecording() || service.isPaused()) {
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@@ -133,121 +118,16 @@ public class TrackRecordingServiceTest {
|
||||
Assert.assertNotNull(service);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_shouldResume() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(trackId, System.currentTimeMillis(), true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent(context);
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We expect to resume the previous track.
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertEquals(trackId, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_simulateReboot() throws Exception {
|
||||
updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
// Simulate recording a track.
|
||||
long id = service.startNewTrack();
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertEquals(id, service.getRecordingTrackId());
|
||||
mServiceRule.unbindService();
|
||||
Assert.assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent(context);
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
mServiceRule.startService(startIntent);
|
||||
service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
Assert.assertTrue(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_noRecordingTrack() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(trackId, System.currentTimeMillis(), false);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent(context);
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because it was stopped.
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_expiredTrack() throws Exception {
|
||||
// Insert a dummy track last updated 20 min ago.
|
||||
createDummyTrack(trackId, System.currentTimeMillis() - 20 * 60 * 1000, true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent(context);
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because it has expired.
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_tooManyAttempts() throws Exception {
|
||||
// Insert a dummy track.
|
||||
createDummyTrack(trackId, System.currentTimeMillis(), true);
|
||||
|
||||
// Set the number of attempts to max.
|
||||
updateAutoResumePrefs(context, TrackRecordingService.MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent(context);
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
|
||||
//Explicit start service, so `startCommand()` is executed - which would be called by BootReceiver.
|
||||
context.startService(startIntent);
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because there were already too many attempts.
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_noTracks() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
List<Track> tracks = contentProviderUtils.getAllTracks();
|
||||
Assert.assertTrue(tracks.isEmpty());
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
Intent startIntent = createStartIntent(context);
|
||||
mServiceRule.startService(startIntent);
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(startIntent));
|
||||
|
||||
// Test if we start in no-recording mode by default.
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
@@ -258,7 +138,7 @@ public class TrackRecordingServiceTest {
|
||||
public void testRecording_oldTracks() throws Exception {
|
||||
createDummyTrack(trackId, -1L, false);
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
@@ -266,11 +146,14 @@ public class TrackRecordingServiceTest {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||
Intent startIntent = createStartIntent(context);
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(startIntent));
|
||||
|
||||
// Just set recording track to a bogus value.
|
||||
// Make sure that the service will not start recording and will clear the bogus track.
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L);
|
||||
|
||||
// Make sure that the service will not start recording and will clear the bogus track.
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
// then
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
@@ -278,69 +161,34 @@ public class TrackRecordingServiceTest {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||
createDummyTrack(trackId, -1L, true);
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
service.startNewTrack();
|
||||
Assert.assertTrue(service.isRecording());
|
||||
long trackId = service.getRecordingTrackId();
|
||||
|
||||
long newTrackId = service.startNewTrack();
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, newTrackId);
|
||||
|
||||
Assert.assertEquals(trackId, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
||||
Assert.assertEquals(trackId, service.getRecordingTrackId());
|
||||
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
// Ending the current track when there is no recording should not result in any error.
|
||||
service.endCurrentTrack();
|
||||
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertFalse(PreferencesUtils.isRecording(context));
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
Assert.assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
||||
createDummyTrack(trackId, -1L, true);
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertFalse(service.isPaused());
|
||||
insertLocation(service);
|
||||
|
||||
long waypointId1 = service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
Assert.assertNotEquals(-1L, waypointId1);
|
||||
long waypointId2 = service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
Assert.assertNotEquals(-1L, waypointId2);
|
||||
|
||||
Waypoint wpt = providerUtils.getWaypoint(waypointId1);
|
||||
Assert.assertEquals(context.getString(R.string.marker_statistics_icon_url), wpt.getIcon());
|
||||
Assert.assertEquals(context.getString(R.string.marker_split_name_format, 0), wpt.getName());
|
||||
Assert.assertEquals(WaypointType.STATISTICS, wpt.getType());
|
||||
Assert.assertEquals(trackId, wpt.getTrackId());
|
||||
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||
Assert.assertNotNull(wpt.getLocation());
|
||||
Assert.assertNotNull(wpt.getTripStatistics());
|
||||
// TODO check the rest of the params.
|
||||
|
||||
// TODO: Check waypoint 2.
|
||||
}
|
||||
|
||||
// NOTE: Do not use to create a track that is currently recording.
|
||||
private void createDummyTrack(long id, long stopTime, boolean isRecording) {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(id);
|
||||
@@ -353,8 +201,8 @@ public class TrackRecordingServiceTest {
|
||||
|
||||
private void addTrack(Track track, boolean isRecording) {
|
||||
Assert.assertTrue(track.getId() >= 0);
|
||||
providerUtils.insertTrack(track);
|
||||
Assert.assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||
contentProviderUtils.insertTrack(track);
|
||||
Assert.assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
||||
}
|
||||
@@ -362,7 +210,7 @@ public class TrackRecordingServiceTest {
|
||||
/**
|
||||
* Inserts a location and waits for 200ms.
|
||||
*/
|
||||
private void insertLocation(ITrackRecordingService trackRecordingService) throws InterruptedException {
|
||||
private void insertLocation(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
|
||||
Location location = new Location("gps");
|
||||
location.setLongitude(35.0f);
|
||||
location.setLatitude(45.0f);
|
||||
@@ -378,72 +226,31 @@ public class TrackRecordingServiceTest {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
long waypointId = service.insertWaypoint(null, null, null, null);
|
||||
Assert.assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||
createDummyTrack(trackId, -1L, true);
|
||||
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||
service.startNewTrack();
|
||||
Assert.assertTrue(service.isRecording());
|
||||
insertLocation(service);
|
||||
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
long trackId = service.getRecordingTrackId();
|
||||
long waypointId = service.insertWaypoint(null, null, null, null);
|
||||
Assert.assertNotEquals(-1L, waypointId);
|
||||
Waypoint wpt = providerUtils.getWaypoint(waypointId);
|
||||
Waypoint wpt = contentProviderUtils.getWaypoint(waypointId);
|
||||
Assert.assertEquals(context.getString(R.string.marker_waypoint_icon_url), wpt.getIcon());
|
||||
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
Assert.assertEquals(WaypointType.WAYPOINT, wpt.getType());
|
||||
Assert.assertEquals(trackId, wpt.getTrackId());
|
||||
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||
Assert.assertNotNull(wpt.getLocation());
|
||||
Assert.assertNull(wpt.getTripStatistics());
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous/waitable broadcast receiver to be used in testing.
|
||||
*/
|
||||
private class BlockingBroadcastReceiver extends BroadcastReceiver {
|
||||
private static final long MAX_WAIT_TIME_MS = 3000;
|
||||
private final List<Intent> receivedIntents = new ArrayList<>();
|
||||
|
||||
public List<Intent> getReceivedIntents() {
|
||||
return receivedIntents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context ctx, Intent intent) {
|
||||
Log.d("Test", "Got broadcast: " + intent);
|
||||
synchronized (receivedIntents) {
|
||||
receivedIntents.add(intent);
|
||||
receivedIntents.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean waitUntilReceived(int receiveCount) {
|
||||
long deadline = System.currentTimeMillis() + MAX_WAIT_TIME_MS;
|
||||
synchronized (receivedIntents) {
|
||||
while (receivedIntents.size() < receiveCount) {
|
||||
try {
|
||||
// Wait releases synchronized lock until it returns
|
||||
receivedIntents.wait(500);
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() > deadline) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,9 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
@@ -48,7 +47,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private ContentProviderUtils providerUtils;
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
@@ -68,29 +67,26 @@ public class TrackRecordingServiceTestLooper {
|
||||
};
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Disable auto resume by default.
|
||||
TrackRecordingServiceTest.updateAutoResumePrefs(context, PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, 0);
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws TimeoutException {
|
||||
// Reset service (if some previous test failed)
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
||||
if (service.isRecording() || service.isPaused()) {
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
// Ensure that the database is empty after every test
|
||||
providerUtils.deleteAllTracks(context);
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@@ -103,7 +99,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -125,7 +121,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -139,7 +135,8 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
int minRecordingIntervalDefault = Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, minRecordingIntervalDefault);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -150,31 +147,10 @@ public class TrackRecordingServiceTestLooper {
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByDistance() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByTime() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -188,7 +164,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -202,30 +178,31 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
|
||||
int recordingGPSAccuracyDefault = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, recordingGPSAccuracyDefault);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testIntegration_completeRecordingSession() throws TimeoutException {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
List<Track> tracks = contentProviderUtils.getAllTracks();
|
||||
Assert.assertTrue(tracks.isEmpty());
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
private void fullRecordingSession() throws TimeoutException {
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
// Start a track.
|
||||
long id = service.startNewTrack();
|
||||
Assert.assertTrue(id >= 0);
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Track track = providerUtils.getTrack(id);
|
||||
Track track = contentProviderUtils.getTrack(id);
|
||||
Assert.assertNotNull(track);
|
||||
Assert.assertEquals(id, track.getId());
|
||||
Assert.assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(id, PreferencesUtils.getRecordingTrackId(context));
|
||||
Assert.assertEquals(id, service.getRecordingTrackId());
|
||||
|
||||
// Insert a few points, markers and statistics.
|
||||
@@ -240,10 +217,8 @@ public class TrackRecordingServiceTestLooper {
|
||||
location.setBearing(3.0f);
|
||||
service.insertTrackPoint(location);
|
||||
|
||||
if (i % 10 == 0) {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
} else if (i % 7 == 0) {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
if (i % 7 == 0) {
|
||||
service.insertWaypoint(null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +226,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
service.endCurrentTrack();
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
track = providerUtils.getTrack(id);
|
||||
track = contentProviderUtils.getTrack(id);
|
||||
Assert.assertNotNull(track);
|
||||
Assert.assertEquals(id, track.getId());
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
|
||||
@@ -1,557 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.same;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnouncementPeriodicTask}.
|
||||
*
|
||||
* WARNING: I'm not responsible if your eyes start bleeding while reading this code.
|
||||
* You have been warned.
|
||||
* It's still better than no test, though.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AnnouncementPeriodicTaskTest {
|
||||
|
||||
// Use something other than our hardcoded value
|
||||
private static final Locale DEFAULT_LOCALE = Locale.KOREAN;
|
||||
private static final String ANNOUNCEMENT = "I can haz cheeseburger?";
|
||||
private Locale oldDefaultLocale;
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private AnnouncementPeriodicTask task;
|
||||
@Mock
|
||||
private AnnouncementPeriodicTask mockTask;
|
||||
private ArgumentCaptor<OnInitListener> initListenerCapture = ArgumentCaptor.forClass(OnInitListener.class);
|
||||
private ArgumentCaptor<PhoneStateListener> phoneListenerCapture = ArgumentCaptor.forClass(PhoneStateListener.class);
|
||||
private TextToSpeechDelegate ttsDelegate;
|
||||
@Mock
|
||||
private TextToSpeechInterface tts;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
// Prepare looper for Android's message queue
|
||||
if (Looper.myLooper() == null) Looper.prepare();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void finalTearDown() {
|
||||
if (Looper.myLooper() != null) Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
oldDefaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(DEFAULT_LOCALE);
|
||||
|
||||
// Eww, the effort required just to mock TextToSpeech is insane
|
||||
final AtomicBoolean listenerCalled = new AtomicBoolean();
|
||||
OnInitListener blockingListener = new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
synchronized (this) {
|
||||
listenerCalled.set(true);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ttsDelegate = new TextToSpeechDelegate(context, blockingListener);
|
||||
|
||||
// Wait for all async operations done in the constructor to finish.
|
||||
synchronized (blockingListener) {
|
||||
while (!listenerCalled.get()) {
|
||||
// Releases the synchronized lock until we're woken up.
|
||||
blockingListener.wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Create a partial forwarding mock
|
||||
task = new AnnouncementPeriodicTask(context) {
|
||||
@Override
|
||||
protected TextToSpeech newTextToSpeech(Context ctx, OnInitListener onInitListener) {
|
||||
return mockTask.newTextToSpeech(ctx, onInitListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAnnouncement(TripStatistics stats) {
|
||||
return mockTask.getAnnouncement(stats);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
mockTask.listenToPhoneState(listener, events);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
Locale.setDefault(oldDefaultLocale);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStart() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
Assert.assertNotNull(ttsInitListener);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStart_notReady() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
Assert.assertNotNull(ttsInitListener);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.ERROR);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShutdown() {
|
||||
// First, start
|
||||
doStart();
|
||||
verify(mockTask);
|
||||
reset(mockTask);
|
||||
|
||||
// Then, shut down
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
mockTask.listenToPhoneState(
|
||||
same(phoneListener), eq(PhoneStateListener.LISTEN_NONE));
|
||||
tts.shutdown();
|
||||
task.shutdown();
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun() {
|
||||
// when service data calls
|
||||
TripStatistics stats = new TripStatistics();
|
||||
|
||||
// when announcement building call
|
||||
when(mockTask.getAnnouncement(same(stats))).thenReturn(ANNOUNCEMENT);
|
||||
|
||||
// Put task in "ready" state
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
when(tts.isLanguageAvailable(DEFAULT_LOCALE)).thenReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
when(tts.setLanguage(DEFAULT_LOCALE)).thenReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
when(tts.setSpeechRate(AnnouncementPeriodicTask.TTS_SPEECH_RATE)).thenReturn(TextToSpeech.SUCCESS);
|
||||
when(tts.setOnUtteranceCompletedListener((OnUtteranceCompletedListener) any())).thenReturn(0);
|
||||
|
||||
// Run the announcement
|
||||
task.announce(stats);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_notReady() {
|
||||
// Put task in "not ready" state
|
||||
startTask(TextToSpeech.ERROR);
|
||||
|
||||
// Run the announcement
|
||||
task.run(null);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_duringCall() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
when(tts.isSpeaking()).thenReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
||||
task.run(null);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_ringWhileSpeaking() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
when(tts.isSpeaking()).thenReturn(true);
|
||||
when(tts.stop()).thenReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
// Update the state to ringing - this should stop the current announcement.
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
|
||||
// Run the announcement - this should do nothing.
|
||||
task.run(null);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_whileRinging() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
when(tts.isSpeaking()).thenReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
task.run(null);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_noService() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
task.run(null);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRun_noStats() {
|
||||
// when service data calls
|
||||
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
task.run(null);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time zero.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_time_zero() {
|
||||
long time = 0; // 0 seconds
|
||||
Assert.assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time one.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_time_one() {
|
||||
long time = 1000; // 1 second
|
||||
Assert.assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||
* numbers with the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_singular_has_hour() {
|
||||
long time = (60 * 60 * 1000) + (60 * 1000) + (1000); // 1 hour 1 minute 1 second
|
||||
Assert.assertEquals("1 hour 1 minute 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||
* with the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_plural_has_hour() {
|
||||
long time = (2 * 60 * 60 * 1000) + (2 * 60 * 1000) + (2 * 1000); // 2 hours 2 minutes 2 seconds
|
||||
Assert.assertEquals("2 hours 2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||
* numbers without the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_singular_no_hour() {
|
||||
long time = (1 * 60 * 1000) + (1 * 1000); // 1 minute 1 second
|
||||
Assert.assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||
* without the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_plural_no_hour() {
|
||||
long time = (2 * 60 * 1000) + (2 * 1000); // 2 minutes 2 seconds
|
||||
Assert.assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
private void startTask(int state) {
|
||||
reset(tts);
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
ttsInitListener.onInit(state);
|
||||
//TODO
|
||||
reset(tts);
|
||||
//AndroidMock.resetToDefault(tts);
|
||||
}
|
||||
|
||||
private void doStart() {
|
||||
mockTask.listenToPhoneState(phoneListenerCapture.capture(), eq(PhoneStateListener.LISTEN_CALL_STATE));
|
||||
when(mockTask.newTextToSpeech(same(context), initListenerCapture.capture())).thenReturn(ttsDelegate);
|
||||
|
||||
task.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mockable interface that we delegate TTS calls to.
|
||||
*/
|
||||
interface TextToSpeechInterface {
|
||||
int addEarcon(String earcon, String packagename, int resourceId);
|
||||
|
||||
int addEarcon(String earcon, String filename);
|
||||
|
||||
int addSpeech(String text, String packagename, int resourceId);
|
||||
|
||||
int addSpeech(String text, String filename);
|
||||
|
||||
boolean areDefaultsEnforced();
|
||||
|
||||
String getDefaultEngine();
|
||||
|
||||
Locale getLanguage();
|
||||
|
||||
int isLanguageAvailable(Locale loc);
|
||||
|
||||
boolean isSpeaking();
|
||||
|
||||
int playEarcon(String earcon, int queueMode,
|
||||
HashMap<String, String> params);
|
||||
|
||||
int playSilence(long durationInMs, int queueMode, HashMap<String, String> params);
|
||||
|
||||
int setLanguage(Locale loc);
|
||||
|
||||
int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener);
|
||||
|
||||
int setPitch(float pitch);
|
||||
|
||||
int setSpeechRate(float speechRate);
|
||||
|
||||
void shutdown();
|
||||
|
||||
int speak(String text, int queueMode, HashMap<String, String> params);
|
||||
|
||||
int stop();
|
||||
|
||||
int synthesizeToFile(String text, HashMap<String, String> params, String filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of {@link TextToSpeech} which delegates calls to the interface
|
||||
* above.
|
||||
* The logic here is stupid and the author is ashamed of having to write it
|
||||
* like this, but basically the issue is that TextToSpeech cannot be mocked
|
||||
* without running its constructor, its constructor runs async operations
|
||||
* which call other methods (and then if the methods are part of a mock we'd
|
||||
* have to set a behavior, but we can't 'cause the object hasn't been fully
|
||||
* built yet).
|
||||
* The logic is that calls made during the constructor (when tts is not yet
|
||||
* set) will go up to the original class, but after tts is set we'll forward
|
||||
* them all to the mock.
|
||||
*/
|
||||
private class TextToSpeechDelegate extends TextToSpeech implements TextToSpeechInterface {
|
||||
public TextToSpeechDelegate(Context context, OnInitListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEarcon(String earcon, String packagename, int resourceId) {
|
||||
if (tts == null) {
|
||||
return super.addEarcon(earcon, packagename, resourceId);
|
||||
}
|
||||
return tts.addEarcon(earcon, packagename, resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEarcon(String earcon, String filename) {
|
||||
if (tts == null) {
|
||||
return super.addEarcon(earcon, filename);
|
||||
}
|
||||
return tts.addEarcon(earcon, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addSpeech(String text, String packagename, int resourceId) {
|
||||
if (tts == null) {
|
||||
return super.addSpeech(text, packagename, resourceId);
|
||||
}
|
||||
return tts.addSpeech(text, packagename, resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addSpeech(String text, String filename) {
|
||||
if (tts == null) {
|
||||
return super.addSpeech(text, filename);
|
||||
}
|
||||
return tts.addSpeech(text, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLanguage() {
|
||||
if (tts == null) {
|
||||
return super.getLanguage();
|
||||
}
|
||||
return tts.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isLanguageAvailable(Locale loc) {
|
||||
if (tts == null) {
|
||||
return super.isLanguageAvailable(loc);
|
||||
}
|
||||
return tts.isLanguageAvailable(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpeaking() {
|
||||
if (tts == null) {
|
||||
return super.isSpeaking();
|
||||
}
|
||||
return tts.isSpeaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playEarcon(String earcon, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playEarcon(earcon, queueMode, params);
|
||||
}
|
||||
return tts.playEarcon(earcon, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playSilence(long durationInMs, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playSilence(durationInMs, queueMode, params);
|
||||
}
|
||||
return tts.playSilence(durationInMs, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setLanguage(Locale loc) {
|
||||
if (tts == null) {
|
||||
return super.setLanguage(loc);
|
||||
}
|
||||
return tts.setLanguage(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener) {
|
||||
if (tts == null) {
|
||||
return super.setOnUtteranceCompletedListener(listener);
|
||||
}
|
||||
return tts.setOnUtteranceCompletedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setPitch(float pitch) {
|
||||
if (tts == null) {
|
||||
return super.setPitch(pitch);
|
||||
}
|
||||
return tts.setPitch(pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setSpeechRate(float speechRate) {
|
||||
if (tts == null) {
|
||||
return super.setSpeechRate(speechRate);
|
||||
}
|
||||
return tts.setSpeechRate(speechRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (tts == null) {
|
||||
super.shutdown();
|
||||
return;
|
||||
}
|
||||
tts.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int speak(
|
||||
String text, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.speak(text, queueMode, params);
|
||||
}
|
||||
return tts.speak(text, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stop() {
|
||||
if (tts == null) {
|
||||
return super.stop();
|
||||
}
|
||||
return tts.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int synthesizeToFile(String text, HashMap<String, String> params, String filename) {
|
||||
if (tts == null) {
|
||||
return super.synthesizeToFile(text, params, filename);
|
||||
}
|
||||
return tts.synthesizeToFile(text, params, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.stats;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* Tests for {@link TripStatistics}.
|
||||
@@ -23,26 +27,17 @@ import junit.framework.TestCase;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TripStatisticsTest extends TestCase {
|
||||
@RunWith(JUnit4.class)
|
||||
public class TripStatisticsTest {
|
||||
|
||||
private TripStatistics statistics;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
statistics = new TripStatistics();
|
||||
}
|
||||
|
||||
public void testSetBounds() {
|
||||
// This is not a trivial setter, conversion happens in it
|
||||
statistics.setBounds(12345, -34567, 56789, -98765);
|
||||
assertEquals(12345, statistics.getLeft());
|
||||
assertEquals(-34567, statistics.getTop());
|
||||
assertEquals(56789, statistics.getRight());
|
||||
assertEquals(-98765, statistics.getBottom());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMerge() {
|
||||
TripStatistics statistics2 = new TripStatistics();
|
||||
statistics.setStartTime(1000L); // Resulting start time
|
||||
@@ -68,38 +63,32 @@ public class TripStatisticsTest extends TestCase {
|
||||
statistics2.setMaxGrade(35.0); // Resulting max grade
|
||||
statistics2.setMinGrade(0.0);
|
||||
|
||||
// Resulting bounds: -10000, 35000, 30000, -40000
|
||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
||||
statistics2.setBounds(-5000, 35000, 0, 20000);
|
||||
|
||||
statistics.merge(statistics2);
|
||||
|
||||
assertEquals(1000L, statistics.getStartTime());
|
||||
assertEquals(4000L, statistics.getStopTime());
|
||||
assertEquals(2500L, statistics.getTotalTime());
|
||||
assertEquals(1300L, statistics.getMovingTime());
|
||||
assertEquals(1100.0, statistics.getTotalDistance());
|
||||
assertEquals(900.0, statistics.getTotalElevationGain());
|
||||
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed());
|
||||
assertEquals(-10000, statistics.getLeft());
|
||||
assertEquals(30000, statistics.getRight());
|
||||
assertEquals(35000, statistics.getTop());
|
||||
assertEquals(-40000, statistics.getBottom());
|
||||
assertEquals(1200.0, statistics.getMinElevation());
|
||||
assertEquals(3575.0, statistics.getMaxElevation());
|
||||
assertEquals(-25.0, statistics.getMinGrade());
|
||||
assertEquals(35.0, statistics.getMaxGrade());
|
||||
Assert.assertEquals(1000L, statistics.getStartTime());
|
||||
Assert.assertEquals(4000L, statistics.getStopTime());
|
||||
Assert.assertEquals(2500L, statistics.getTotalTime());
|
||||
Assert.assertEquals(1300L, statistics.getMovingTime());
|
||||
Assert.assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
||||
Assert.assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
||||
Assert.assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
|
||||
Assert.assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
||||
Assert.assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
|
||||
Assert.assertEquals(-25.0, statistics.getMinGrade(), 0.001);
|
||||
Assert.assertEquals(35.0, statistics.getMaxGrade(), 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAverageSpeed() {
|
||||
statistics.setTotalDistance(1000.0);
|
||||
statistics.setTotalTime(50000); // in milliseconds
|
||||
assertEquals(20.0, statistics.getAverageSpeed());
|
||||
Assert.assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAverageMovingSpeed() {
|
||||
statistics.setTotalDistance(1000.0);
|
||||
statistics.setMovingTime(20000); // in milliseconds
|
||||
assertEquals(50.0, statistics.getAverageMovingSpeed());
|
||||
Assert.assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -26,78 +27,83 @@ import java.io.File;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class FileUtilsTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#getPath(String...)}.
|
||||
*/
|
||||
public void testBuildExternalDirectoryPath() {
|
||||
String expectedName = Environment.getExternalStorageDirectory() + File.separator + FileUtils.SDCARD_TOP_DIR + File.separator + "a" + File.separator + "b" + File.separator + "c";
|
||||
String dirName = FileUtils.getPath("a", "b", "c");
|
||||
assertEquals(expectedName, dirName);
|
||||
}
|
||||
@RunWith(JUnit4.class)
|
||||
public class FileUtilsTest {
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file is new.
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_new() {
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
||||
assertEquals("Filename.ext", filename);
|
||||
Assert.assertEquals("Filename.ext", filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_exist() {
|
||||
// Expect "/default.prop" to exist on the phone/emulator
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
||||
assertEquals("default(1).prop", filename);
|
||||
Assert.assertEquals("default(1).prop", filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
|
||||
* Verifies that they are sanitized.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeFileName() {
|
||||
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
|
||||
String expected = "Swim_ming-^across_the_ pacific (ocean)_";
|
||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
String expected = "Swim_ming-^across_the_ pacific (ocean).";
|
||||
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSanitizeFileNameWithSuffix() {
|
||||
String name = "FileName.jpeg";
|
||||
Assert.assertEquals(name, FileUtils.sanitizeFileName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in Chinese and Russian).
|
||||
* Verifies that they are allowed.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeFileName_i18n() {
|
||||
String name = "您好-привет";
|
||||
String expected = "您好-привет";
|
||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32 characters.
|
||||
* Verifies that they are allowed.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeFileName_special_characters() {
|
||||
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped characters in a row.
|
||||
* Verifies that they are collapsed into one underscore.
|
||||
*/
|
||||
@Test
|
||||
public void testSanitizeFileName_collapse() {
|
||||
String name = "hello//there";
|
||||
String expected = "hello_there";
|
||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link FileUtils#truncateFileName(File, String, String)}.
|
||||
* Verifies the a long file name is truncated.
|
||||
*/
|
||||
@Test
|
||||
public void testTruncateFileName() {
|
||||
File directory = new File("/dir1/dir2/");
|
||||
String suffix = ".gpx";
|
||||
@@ -109,8 +115,8 @@ public class FileUtilsTest extends TestCase {
|
||||
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,27 +115,16 @@ public class StringUtilsTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime() {
|
||||
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
||||
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds.
|
||||
* Tests {@link StringUtils#parseTime(String)} with fractional seconds.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_fractional() {
|
||||
assertGetTime("2010-05-04T03:02:01.3", 2010, 5, 4, 3, 2, 1, 300);
|
||||
assertGetTime("2010-05-04T03:02:01.35", 2010, 5, 4, 3, 2, 1, 350);
|
||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||
assertGetTime("2010-05-04T03:02:01.3525Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||
assertGetTime("2010-05-04T03:02:01.3529Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)} with time zone.
|
||||
* Tests {@link StringUtils#parseTime(String)} with time zone.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_timezone() {
|
||||
@@ -149,8 +138,7 @@ public class StringUtilsTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time
|
||||
* zone.
|
||||
* Tests {@link StringUtils#parseTime(String)} with fractional seconds and time zone.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_fractionalAndTimezone() {
|
||||
@@ -161,7 +149,7 @@ public class StringUtilsTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the {@link StringUtils#getTime(String)} returns the expected values.
|
||||
* Asserts the {@link StringUtils#parseTime(String)} returns the expected values.
|
||||
*
|
||||
* @param xmlDateTime the xml date time string
|
||||
* @param year the expected year
|
||||
@@ -176,14 +164,14 @@ public class StringUtilsTest {
|
||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
calendar.set(year, month - 1, day, hour, minute, second);
|
||||
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
||||
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.getTime(xmlDateTime));
|
||||
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTimeParts_postive() {
|
||||
public void testGetTimeParts_positive() {
|
||||
int[] parts = StringUtils.getTimeParts(61000);
|
||||
Assert.assertEquals(1, parts[0]);
|
||||
Assert.assertEquals(1, parts[1]);
|
||||
@@ -200,4 +188,24 @@ public class StringUtilsTest {
|
||||
Assert.assertEquals(-1, parts[1]);
|
||||
Assert.assertEquals(0, parts[2]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormatDecimal() {
|
||||
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 0));
|
||||
Assert.assertEquals("0", StringUtils.formatDecimal(0.1, 0));
|
||||
Assert.assertEquals("1", StringUtils.formatDecimal(1.1, 0));
|
||||
Assert.assertEquals("10", StringUtils.formatDecimal(10, 0));
|
||||
Assert.assertEquals("10", StringUtils.formatDecimal(10.1, 0));
|
||||
Assert.assertEquals("0", StringUtils.formatDecimal(-0.1, 0));
|
||||
|
||||
Assert.assertEquals("0", StringUtils.formatDecimal(0.0, 2));
|
||||
Assert.assertEquals("0.1", StringUtils.formatDecimal(0.1, 2));
|
||||
Assert.assertEquals("1.1", StringUtils.formatDecimal(1.1, 2));
|
||||
Assert.assertEquals("10", StringUtils.formatDecimal(10, 2));
|
||||
Assert.assertEquals("10.1", StringUtils.formatDecimal(10.1, 2));
|
||||
Assert.assertEquals("10.11", StringUtils.formatDecimal(10.111, 2));
|
||||
Assert.assertEquals("-0.1", StringUtils.formatDecimal(-0.1, 2));
|
||||
|
||||
Assert.assertEquals("1", StringUtils.formatDecimal(0.99, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,25 +16,26 @@ limitations under the License.
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.dennisguse.opentracks"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="3054"
|
||||
android:versionName="v3.2.0"> <!-- For FDroid. -->
|
||||
android:versionCode="3272"
|
||||
android:versionName="v3.3.2"> <!-- For FDroid. -->
|
||||
<!-- Permission to initialize services -->
|
||||
<application
|
||||
android:name=".Startup"
|
||||
android:allowBackup="false"
|
||||
android:hasFragileUserData="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_logo_color_24dp"
|
||||
android:label="@string/app_name"
|
||||
android:icon="${applicationLogo}"
|
||||
android:label="${applicationName}"
|
||||
android:theme="@style/ThemeCustom">
|
||||
|
||||
<!-- Activities -->
|
||||
<activity android:name=".AboutActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".AggregatedStatsActivity"
|
||||
android:label="@string/menu_aggregated_statistics" />
|
||||
<activity android:name=".TrackDeleteActivity" />
|
||||
|
||||
<activity android:name=".DeleteActivity" />
|
||||
<activity
|
||||
android:name=".HelpActivity"
|
||||
android:label="@string/menu_help" />
|
||||
|
||||
<activity
|
||||
android:name=".io.file.importer.ImportActivity"
|
||||
@@ -54,10 +55,10 @@ limitations under the License.
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".MarkerEditActivity" />
|
||||
|
||||
<activity android:name=".MarkerDetailActivity" />
|
||||
|
||||
<activity android:name=".MarkerEditActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".MarkerListActivity"
|
||||
android:label="@string/menu_markers">
|
||||
@@ -97,15 +98,10 @@ limitations under the License.
|
||||
android:name=".io.file.exporter.ExportActivity"
|
||||
android:theme="@style/ThemeCustomTransparent" />
|
||||
|
||||
<activity android:name=".settings.RecordingSettingsActivity" />
|
||||
|
||||
<activity android:name=".settings.SettingsActivity" />
|
||||
|
||||
<activity android:name=".settings.StatsSettingsActivity" />
|
||||
|
||||
<!-- Services -->
|
||||
<provider
|
||||
android:name=".content.ShareContentProvider"
|
||||
android:name=".content.provider.ShareContentProvider"
|
||||
android:authorities="${applicationId}.content"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" />
|
||||
@@ -120,19 +116,11 @@ limitations under the License.
|
||||
android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<!-- Receivers -->
|
||||
<receiver android:name=".BootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Providers -->
|
||||
|
||||
<service
|
||||
android:name=".services.TrackRecordingService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="location|connectedDevice"
|
||||
android:icon="@drawable/ic_logo_color_24dp"
|
||||
android:label="@string/recording_service" />
|
||||
</application>
|
||||
@@ -147,44 +135,21 @@ limitations under the License.
|
||||
android:name="android.hardware.location.gps"
|
||||
android:required="true" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:name="android.hardware.android.hardware.bluetooth_le"
|
||||
android:required="false" />
|
||||
<!-- Permissions to access sensors -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.faketouch"
|
||||
android:required="false" />
|
||||
<!-- Permission to read phone state so voice announcement will not interrupt ongoing calls -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.sensor.compass"
|
||||
android:required="false" />
|
||||
<!-- Permission to read/write external storage -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony.gsm"
|
||||
android:required="false" />
|
||||
<!-- Required features -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
<!-- Optional features -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen.multitouch"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
</manifest>
|
||||
|
||||
@@ -17,23 +17,28 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import de.dennisguse.opentracks.util.TTSUtils;
|
||||
|
||||
/**
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public abstract class AbstractActivity extends FragmentActivity {
|
||||
public abstract class AbstractActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Set volume control stream for text to speech
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
setVolumeControlStream(TTSUtils.getTTSStream());
|
||||
|
||||
setContentView(getLayoutResId());
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,24 +16,7 @@
|
||||
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
||||
@@ -58,115 +41,6 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
protected static final int GPS_REQUEST_CODE = 6;
|
||||
private static final int DELETE_REQUEST_CODE = 3;
|
||||
|
||||
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
||||
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
||||
mode.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
||||
mode.finish();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the checked positions in a list view.
|
||||
*
|
||||
* @param list the list view
|
||||
*/
|
||||
private int[] getCheckedPositions(ListView list) {
|
||||
SparseBooleanArray positions = list.getCheckedItemPositions();
|
||||
ArrayList<Integer> arrayList = new ArrayList<>();
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
int key = positions.keyAt(i);
|
||||
if (positions.valueAt(i)) {
|
||||
arrayList.add(key);
|
||||
}
|
||||
}
|
||||
int[] result = new int[arrayList.size()];
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
result[i] = arrayList.get(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void configureSearchWidget(Activity activity, final MenuItem menuItem, final TrackController trackController) {
|
||||
final SearchView searchView = (SearchView) menuItem.getActionView();
|
||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||
if (searchManager != null) {
|
||||
//NOTE: for some reason activity.getComponentName() did not trigger the SearchListActivity
|
||||
searchView.setSearchableInfo(searchManager.getSearchableInfo(new ComponentName(activity, SearchListActivity.class)));
|
||||
} else {
|
||||
Log.w(TAG, "Could not retrieve SearchManager.");
|
||||
}
|
||||
searchView.setQueryRefinementEnabled(true);
|
||||
searchView.setSubmitButtonEnabled(true);
|
||||
searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
// Hide and show trackController when searchable widget has focus/no focus
|
||||
if (trackController != null) {
|
||||
if (hasFocus) {
|
||||
trackController.hide();
|
||||
} else {
|
||||
trackController.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) {
|
||||
menuItem.collapseActionView();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() {
|
||||
@Override
|
||||
public boolean onSuggestionSelect(int position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSuggestionClick(int position) {
|
||||
menuItem.collapseActionView();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == DELETE_REQUEST_CODE) {
|
||||
@@ -182,8 +56,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
protected void deleteTracks(long[] trackIds) {
|
||||
ConfirmDeleteDialogFragment.newInstance(trackIds)
|
||||
.show(getSupportFragmentManager(), ConfirmDeleteDialogFragment.CONFIRM_DELETE_DIALOG_TAG);
|
||||
ConfirmDeleteDialogFragment.showDialog(getSupportFragmentManager(), trackIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,7 +65,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||
stopRecording = true;
|
||||
} else {
|
||||
long recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||
long recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
|
||||
for (long trackId : trackIds) {
|
||||
if (trackId == recordingTrackId) {
|
||||
stopRecording = true;
|
||||
@@ -203,20 +76,19 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
if (stopRecording) {
|
||||
getTrackRecordingServiceConnection().stopRecording(this, false);
|
||||
}
|
||||
Intent intent = IntentUtils.newIntent(this, DeleteActivity.class);
|
||||
intent.putExtra(DeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
||||
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
|
||||
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
||||
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track recording service connection. For stopping the current
|
||||
* recording if need to delete the current recording track.
|
||||
* Gets the track recording service connection.
|
||||
* For stopping the current recording if need to delete the current recording track.
|
||||
*/
|
||||
@NonNull
|
||||
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
||||
|
||||
/**
|
||||
* Called after {@link DeleteActivity} returns its result.
|
||||
* Called after {@link TrackDeleteActivity} returns its result.
|
||||
*/
|
||||
abstract protected void onDeleted();
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.StatsUtils;
|
||||
|
||||
/**
|
||||
* An activity to view aggregated stats from all recorded tracks.
|
||||
*
|
||||
* @author Fergus Nelson
|
||||
*/
|
||||
public class AggregatedStatsActivity extends AbstractActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatsUtils.setTripStatisticsValues(this, getTripStatistics(), null);
|
||||
StatsUtils.setLocationValues(this, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the aggregated trip statistics for all the recorded tracks or null if
|
||||
* there is no track.
|
||||
*/
|
||||
private TripStatistics getTripStatistics() {
|
||||
List<Track> tracks = ContentProviderUtils.Factory.get(this).getAllTracks();
|
||||
TripStatistics tripStatistics = null;
|
||||
if (!tracks.isEmpty()) {
|
||||
tripStatistics = new TripStatistics(tracks.iterator().next().getTripStatistics());
|
||||
for (int i = 1; i < tracks.size(); i++) {
|
||||
tripStatistics.merge(tracks.get(i).getTripStatistics());
|
||||
}
|
||||
}
|
||||
return tripStatistics;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
import static android.content.Intent.ACTION_BOOT_COMPLETED;
|
||||
|
||||
/**
|
||||
* This class react to the BOOT_COMPLETED broadcast.
|
||||
* <p>
|
||||
* One example of a broadcast message that this class is interested in,
|
||||
* is notification about the phone boot. We may want to resume a previously
|
||||
* started tracking session if the phone crashed (hopefully not), or the user
|
||||
* decided to swap the battery or some external event occurred which forced
|
||||
* a phone reboot.
|
||||
* <p>
|
||||
* This class simply delegates to {@link TrackRecordingService} to make a
|
||||
* decision whether to continue with the previous track (if any), or just
|
||||
* abandon it.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = BootReceiver.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d(TAG, "BootReceiver.onReceive: " + intent.getAction());
|
||||
if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
|
||||
Intent startIntent = new Intent(context, TrackRecordingService.class)
|
||||
.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
context.startService(startIntent);
|
||||
} else {
|
||||
Log.w(TAG, "BootReceiver: unsupported action");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package de.dennisguse.opentracks;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
|
||||
/**
|
||||
* Async Task to delete tracks.
|
||||
@@ -30,7 +30,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private final long[] trackIds;
|
||||
private final Context context;
|
||||
private DeleteActivity deleteActivity;
|
||||
private TrackDeleteActivity deleteActivity;
|
||||
// true if the AsyncTask has completed
|
||||
private boolean completed;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
||||
* trackIds[0] == -1L
|
||||
*/
|
||||
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
||||
public DeleteAsyncTask(TrackDeleteActivity deleteActivity, long[] trackIds) {
|
||||
this.deleteActivity = deleteActivity;
|
||||
this.trackIds = trackIds;
|
||||
context = deleteActivity.getApplicationContext();
|
||||
@@ -53,7 +53,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
*
|
||||
* @param deleteActivity the current activity, can be null
|
||||
*/
|
||||
public void setActivity(DeleteActivity deleteActivity) {
|
||||
public void setActivity(TrackDeleteActivity deleteActivity) {
|
||||
this.deleteActivity = deleteActivity;
|
||||
if (completed && deleteActivity != null) {
|
||||
deleteActivity.onAsyncTaskCompleted();
|
||||
@@ -69,7 +69,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
public class HelpActivity extends AbstractActivity {
|
||||
|
||||
@Override
|
||||
protected int getLayoutResId() {
|
||||
return R.layout.help;
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,8 @@ import androidx.viewpager.widget.ViewPager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
import de.dennisguse.opentracks.fragments.MarkerDetailFragment;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
||||
return;
|
||||
}
|
||||
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
||||
Waypoint waypoint = contentProviderUtils.getWaypoint(markerId);
|
||||
|
||||
markerIds = new ArrayList<>();
|
||||
@@ -71,12 +71,9 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
|
||||
|
||||
if (!currentMarker.isTripStatistics()) {
|
||||
markerIds.add(currentMarker.getId());
|
||||
if (currentMarker.getId() == markerId) {
|
||||
markerIndex = markerIds.size() - 1;
|
||||
}
|
||||
markerIds.add(currentMarker.getId());
|
||||
if (currentMarker.getId() == markerId) {
|
||||
markerIndex = markerIds.size() - 1;
|
||||
}
|
||||
|
||||
cursor.moveToNext();
|
||||
|
||||
@@ -25,10 +25,8 @@ import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
|
||||
/**
|
||||
@@ -48,9 +46,6 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
private Waypoint waypoint;
|
||||
|
||||
// UI elements
|
||||
private View statisticsSection;
|
||||
private EditText statisticsName;
|
||||
private View waypointSection;
|
||||
private EditText waypointName;
|
||||
private AutoCompleteTextView waypointMarkerType;
|
||||
private EditText waypointDescription;
|
||||
@@ -65,14 +60,9 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
|
||||
// Setup UI elements
|
||||
statisticsSection = findViewById(R.id.marker_edit_statistics_section);
|
||||
statisticsName = findViewById(R.id.marker_edit_statistics_name);
|
||||
|
||||
waypointSection = findViewById(R.id.marker_edit_waypoint_section);
|
||||
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||
this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.waypoint_types, android.R.layout.simple_dropdown_item_1line);
|
||||
waypointMarkerType.setAdapter(adapter);
|
||||
waypointDescription = findViewById(R.id.marker_edit_waypoint_description);
|
||||
|
||||
@@ -96,7 +86,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,9 +115,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
});
|
||||
|
||||
if (newMarker) {
|
||||
statisticsSection.setVisibility(View.GONE);
|
||||
waypointSection.setVisibility(View.VISIBLE);
|
||||
int nextWaypointNumber = trackId == -1L ? -1 : ContentProviderUtils.Factory.get(this).getNextWaypointNumber(trackId, WaypointType.WAYPOINT);
|
||||
int nextWaypointNumber = trackId == -1L ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId);
|
||||
if (nextWaypointNumber == -1) {
|
||||
nextWaypointNumber = 0;
|
||||
}
|
||||
@@ -136,22 +124,15 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
waypointMarkerType.setText("");
|
||||
waypointDescription.setText("");
|
||||
} else {
|
||||
waypoint = ContentProviderUtils.Factory.get(this).getWaypoint(markerId);
|
||||
waypoint = new ContentProviderUtils(this).getWaypoint(markerId);
|
||||
if (waypoint == null) {
|
||||
Log.d(TAG, "waypoint is null");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
statisticsSection.setVisibility(statistics ? View.VISIBLE : View.GONE);
|
||||
waypointSection.setVisibility(statistics ? View.GONE : View.VISIBLE);
|
||||
if (statistics) {
|
||||
statisticsName.setText(waypoint.getName());
|
||||
} else {
|
||||
waypointName.setText(waypoint.getName());
|
||||
waypointMarkerType.setText(waypoint.getCategory());
|
||||
waypointDescription.setText(waypoint.getDescription());
|
||||
}
|
||||
waypointName.setText(waypoint.getName());
|
||||
waypointMarkerType.setText(waypoint.getCategory());
|
||||
waypointDescription.setText(waypoint.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,29 +140,21 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
* Adds a marker.
|
||||
*/
|
||||
private void addMarker() {
|
||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
||||
WaypointType.WAYPOINT,
|
||||
false,
|
||||
trackRecordingServiceConnection.addMarker(this,
|
||||
waypointName.getText().toString(),
|
||||
waypointMarkerType.getText().toString(),
|
||||
waypointDescription.getText().toString(),
|
||||
null,
|
||||
null);
|
||||
trackRecordingServiceConnection.addMarker(this, waypointCreationRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a marker.
|
||||
*/
|
||||
private void saveMarker() {
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
if (statistics) {
|
||||
waypoint.setName(statisticsName.getText().toString());
|
||||
} else {
|
||||
waypoint.setName(waypointName.getText().toString());
|
||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||
waypoint.setDescription(waypointDescription.getText().toString());
|
||||
}
|
||||
ContentProviderUtils.Factory.get(this).updateWaypoint(waypoint);
|
||||
waypoint.setName(waypointName.getText().toString());
|
||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||
waypoint.setDescription(waypointDescription.getText().toString());
|
||||
|
||||
new ContentProviderUtils(this).updateWaypoint(waypoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,11 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
|
||||
@@ -40,14 +37,15 @@ import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
||||
import androidx.loader.content.CursorLoader;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -71,21 +69,17 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListener cannot be an anonymous inner
|
||||
* class. Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
private boolean recordingTrackPaused;
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
// Note that the key can be null
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(MarkerListActivity.this, R.string.recording_track_id_key);
|
||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(MarkerListActivity.this);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(MarkerListActivity.this, R.string.recording_track_paused_key))) {
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(MarkerListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(MarkerListActivity.this);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -102,7 +96,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
// UI elements
|
||||
private ListView listView;
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
@Override
|
||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||
boolean isSingleSelection = ids.length == 1;
|
||||
@@ -129,15 +123,14 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
||||
final long trackFirstWaypointId = trackId != -1 ? contentProviderUtils.getFirstWaypointId(trackId) : -1;
|
||||
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
listView = findViewById(R.id.marker_list);
|
||||
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
||||
@@ -152,7 +145,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
int typeIndex = cursor.getColumnIndex(WaypointsColumns.TYPE);
|
||||
int nameIndex = cursor.getColumnIndex(WaypointsColumns.NAME);
|
||||
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||
int categoryIndex = cursor.getColumnIndex(WaypointsColumns.CATEGORY);
|
||||
@@ -161,13 +153,13 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
int latitudeIndex = cursor.getColumnIndex(WaypointsColumns.LATITUDE);
|
||||
int longitudeIndex = cursor.getColumnIndex(WaypointsColumns.LONGITUDE);
|
||||
|
||||
boolean statistics = WaypointType.values()[cursor.getInt(typeIndex)] == WaypointType.STATISTICS;
|
||||
int iconId = statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin;
|
||||
int iconId = MarkerUtils.ICON_ID;
|
||||
String name = cursor.getString(nameIndex);
|
||||
long time = cursor.getLong(timeIndex);
|
||||
String category = statistics ? null : cursor.getString(categoryIndex);
|
||||
String description = statistics ? null : cursor.getString(descriptionIndex);
|
||||
String category = cursor.getString(categoryIndex);
|
||||
String description = cursor.getString(descriptionIndex);
|
||||
String photoUrl = cursor.getString(photoUrlIndex);
|
||||
//TODO also show latitude and longitude in list
|
||||
double latitude = cursor.getDouble(latitudeIndex);
|
||||
double longitude = cursor.getDouble(longitudeIndex);
|
||||
|
||||
@@ -175,7 +167,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
}
|
||||
};
|
||||
listView.setAdapter(resourceCursorAdapter);
|
||||
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
|
||||
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||
@NonNull
|
||||
@@ -183,8 +175,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
if (track != null) {
|
||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||
WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?",
|
||||
new String[]{String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId)}, null);
|
||||
WaypointsColumns.TRACKID + "=?",
|
||||
new String[]{String.valueOf(track.getId())}, null);
|
||||
} else {
|
||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||
WaypointsColumns.STARTTIME + " IS NULL", null, null);
|
||||
@@ -234,7 +226,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker);
|
||||
|
||||
searchMenuItem = menu.findItem(R.id.marker_list_search);
|
||||
AbstractListActivity.configureSearchWidget(this, searchMenuItem, null);
|
||||
ActivityUtils.configureSearchWidget(this, searchMenuItem, null);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -246,7 +238,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (track != null && item.getItemId() == R.id.marker_list_insert_marker) {
|
||||
Intent intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, track.getId());
|
||||
@@ -256,24 +248,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||
if (handleContextItem(item.getItemId(), new long[]{info.id})) {
|
||||
return true;
|
||||
}
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a context item selection.
|
||||
*
|
||||
@@ -300,8 +274,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
if (markerIds.length > 1 && markerIds.length == listView.getCount()) {
|
||||
markerIds = new long[]{-1L};
|
||||
}
|
||||
DeleteMarkerDialogFragment.newInstance(markerIds)
|
||||
.show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), markerIds);
|
||||
return true;
|
||||
case R.id.list_context_menu_select_all:
|
||||
int size = listView.getCount();
|
||||
|
||||
@@ -22,14 +22,11 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
@@ -43,20 +40,21 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
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.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
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.TripStatistics;
|
||||
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;
|
||||
@@ -103,34 +101,35 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private boolean recordingTrackPaused;
|
||||
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
@Override
|
||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
||||
boolean isSingleSelection = positions.length == 1;
|
||||
|
||||
boolean isSingleSelectionTrack;
|
||||
if (isSingleSelection) {
|
||||
Map<String, Object> item = arrayAdapter.getItem(positions[0]);
|
||||
Long trackId = (Long) item.get(TRACK_ID_FIELD);
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
isSingleSelectionTrack = item.get(MARKER_ID_FIELD) == null;
|
||||
} else {
|
||||
isSingleSelectionTrack = false;
|
||||
}
|
||||
|
||||
// Not recording, one item, item is a track
|
||||
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection && isSingleSelectionTrack);
|
||||
//TODO Setup shareIntent.
|
||||
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));
|
||||
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);
|
||||
}
|
||||
@@ -144,14 +143,14 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.stats_units_key))) {
|
||||
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.stats_units_key, key)) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(SearchListActivity.this);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(SearchListActivity.this, R.string.recording_track_id_key);
|
||||
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(SearchListActivity.this);
|
||||
}
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(SearchListActivity.this, R.string.recording_track_paused_key))) {
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(SearchListActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
if (PreferencesUtils.isKey(SearchListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(SearchListActivity.this);
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -169,7 +168,9 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
super.onCreate(savedInstanceState);
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
searchEngine = new SearchEngine(contentProviderUtils);
|
||||
@@ -222,7 +223,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
ActivityUtils.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
|
||||
@@ -244,7 +245,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection.unbind();
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,24 +260,6 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
handleIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
|
||||
if (handleContextItem(item.getItemId(), new int[]{info.position})) {
|
||||
return true;
|
||||
}
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
@@ -301,13 +284,14 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
Long markerId = (Long) item.get(MARKER_ID_FIELD);
|
||||
Intent intent;
|
||||
switch (itemId) {
|
||||
case R.id.list_context_menu_share:
|
||||
//TODO
|
||||
Log.e(TAG, "Not implemented");
|
||||
return true;
|
||||
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, new long[]{trackId});
|
||||
intent = Intent.createChooser(intent, null);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.list_context_menu_edit:
|
||||
if (markerId != null) {
|
||||
intent = IntentUtils.newIntent(this, MarkerEditActivity.class)
|
||||
@@ -323,7 +307,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
return true;
|
||||
case R.id.list_context_menu_delete:
|
||||
if (markerId != null) {
|
||||
DeleteMarkerDialogFragment.newInstance(new long[]{markerId}).show(getSupportFragmentManager(), DeleteMarkerDialogFragment.DELETE_MARKER_DIALOG_TAG);
|
||||
DeleteMarkerDialogFragment.showDialog(getSupportFragmentManager(), new long[]{markerId});
|
||||
} else {
|
||||
deleteTracks(new long[]{trackId});
|
||||
}
|
||||
@@ -409,11 +393,9 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
}
|
||||
}
|
||||
|
||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
||||
|
||||
resultMap.put(IS_RECORDING_FIELD, false);
|
||||
resultMap.put(IS_PAUSED_FIELD, true);
|
||||
resultMap.put(ICON_ID_FIELD, statistics ? R.drawable.ic_marker_yellow_pushpin : R.drawable.ic_marker_blue_pushpin);
|
||||
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
|
||||
@@ -421,8 +403,8 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
||||
resultMap.put(TOTAL_DISTANCE_FIELD, null);
|
||||
resultMap.put(MARKER_COUNT_FIELD, 0);
|
||||
resultMap.put(START_TIME_FIELD, waypoint.getLocation().getTime());
|
||||
resultMap.put(CATEGORY_FIELD, statistics ? null : waypoint.getCategory());
|
||||
resultMap.put(DESCRIPTION_FIELD, statistics ? null : waypoint.getDescription());
|
||||
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());
|
||||
|
||||
@@ -14,7 +14,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
*/
|
||||
public class Startup extends Application {
|
||||
|
||||
private final static String TAG = Startup.class.getSimpleName();
|
||||
private static final String TAG = Startup.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013q 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.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabHost.TabContentFactory;
|
||||
import android.widget.TabWidget;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This is a helper class that implements the management of tabs and all details
|
||||
* of connecting a ViewPager with associated TabHost. It relies on a trick.
|
||||
* Normally a tab host has a simple API for supplying a View or Intent that each
|
||||
* tab will show. This is not sufficient for switching between pages. So instead
|
||||
* we make the content part of the tab host 0dp high (it is not shown) and the
|
||||
* TabsAdapter supplies its own dummy view to show as the tab content. It
|
||||
* listens to changes in tabs, and takes care of switch to the correct paged in
|
||||
* the ViewPager whenever the selected tab changes.
|
||||
* <p>
|
||||
* Copied from the FragmentTabsPager sample in the support library.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
//TODO Rework or better remove.
|
||||
public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
|
||||
|
||||
private final Context context;
|
||||
private final TabHost tabHost;
|
||||
private final ViewPager viewPager;
|
||||
private final ArrayList<TabInfo> tabInfos = new ArrayList<>();
|
||||
|
||||
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager viewPager) {
|
||||
super(activity.getSupportFragmentManager());
|
||||
this.context = activity;
|
||||
this.tabHost = tabHost;
|
||||
this.viewPager = viewPager;
|
||||
this.tabHost.setOnTabChangedListener(this);
|
||||
this.viewPager.setAdapter(this);
|
||||
this.viewPager.setOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle bundle) {
|
||||
tabSpec.setContent(new DummyTabFactory(context));
|
||||
|
||||
TabInfo info = new TabInfo(clss, bundle);
|
||||
|
||||
tabInfos.add(info);
|
||||
tabHost.addTab(tabSpec);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tabInfos.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
TabInfo info = tabInfos.get(position);
|
||||
return Fragment.instantiate(context, info.clss.getName(), info.bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabChanged(String tabId) {
|
||||
int position = tabHost.getCurrentTab();
|
||||
viewPager.setCurrentItem(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
/*
|
||||
* Unfortunately when TabHost changes the current tab, it kindly also takes care of putting focus on it when not in touch mode.
|
||||
* The jerk. This hack tries to prevent this from pulling focus out of our ViewPager.
|
||||
*/
|
||||
TabWidget tabWidget = tabHost.getTabWidget();
|
||||
int oldFocusability = tabWidget.getDescendantFocusability();
|
||||
tabWidget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
|
||||
tabHost.setCurrentTab(position);
|
||||
tabWidget.setDescendantFocusability(oldFocusability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* An object to hold a tab's info.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private static final class TabInfo {
|
||||
|
||||
private final Class<?> clss;
|
||||
private final Bundle bundle;
|
||||
|
||||
TabInfo(Class<?> clss, Bundle bundle) {
|
||||
this.clss = clss;
|
||||
this.bundle = bundle;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy {@link TabContentFactory} that creates an empty view to satisfy the {@link TabHost} API.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private static class DummyTabFactory implements TabHost.TabContentFactory {
|
||||
|
||||
private final Context context;
|
||||
|
||||
DummyTabFactory(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createTabContent(String tag) {
|
||||
View view = new View(context);
|
||||
view.setMinimumWidth(0);
|
||||
view.setMinimumHeight(0);
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,8 @@ import android.view.View.OnClickListener;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class TrackController {
|
||||
public void run() {
|
||||
if (isResumed && isRecording && !isPaused) {
|
||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND);
|
||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -115,7 +115,7 @@ public class TrackController {
|
||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||
if (!isPaused) {
|
||||
totalTimeTimestamp = System.currentTimeMillis();
|
||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND);
|
||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,11 +130,11 @@ public class TrackController {
|
||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||
}
|
||||
|
||||
void hide() {
|
||||
public void hide() {
|
||||
containerView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
void show() {
|
||||
public void show() {
|
||||
containerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class TrackController {
|
||||
* Gets the total time for the current recording track.
|
||||
*/
|
||||
private long getTotalTime() {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,13 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* An activity for delete tracks.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class DeleteActivity extends Activity {
|
||||
public class TrackDeleteActivity extends Activity {
|
||||
|
||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||
|
||||
@@ -31,30 +31,28 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ShareActionProvider;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabHost.TabSpec;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
|
||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.fragments.ChartFragment;
|
||||
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.ChartDistanceFragment;
|
||||
import de.dennisguse.opentracks.fragments.ChartTimeFragment;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.FrequencyDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.StatsFragment;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
@@ -66,7 +64,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
@@ -88,7 +86,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackDataHub trackDataHub;
|
||||
private TabHost tabHost;
|
||||
private ViewPager pager;
|
||||
private TrackController trackController;
|
||||
|
||||
// From intent
|
||||
@@ -96,8 +94,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
|
||||
// Preferences
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private boolean showOnLockScreen = PreferencesUtils.SHOW_TRACKDETAIL_WHILE_RECORDING_ON_LOCKSCREEN;
|
||||
private boolean recordingTrackPaused;
|
||||
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
@@ -109,36 +106,34 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
trackController.update(isRecording(), recordingTrackPaused);
|
||||
if (hasPhoto && photoUri != null) {
|
||||
hasPhoto = false;
|
||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString());
|
||||
long markerId = trackRecordingServiceConnection.addMarker(TrackDetailActivity.this, waypointCreationRequest);
|
||||
if (markerId != -1L) {
|
||||
//TODO: Make configurable.
|
||||
FileUtils.updateMediaScanner(TrackDetailActivity.this, photoUri);
|
||||
}
|
||||
long markerId = trackRecordingServiceConnection.addMarker(TrackDetailActivity.this, null, null, null, photoUri.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListener cannot be an anonymous inner
|
||||
* class. Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(TrackDetailActivity.this, R.string.recording_track_id_key);
|
||||
}
|
||||
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.recording_track_paused_key))) {
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
}
|
||||
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.trackdetail_show_on_lockscreen_while_recording_key))) {
|
||||
showOnLockScreen = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.trackdetail_show_on_lockscreen_while_recording_key, PreferencesUtils.SHOW_TRACKDETAIL_WHILE_RECORDING_ON_LOCKSCREEN);
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_id_key, key)) {
|
||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackDetailActivity.this);
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.recording_track_paused_key, key)) {
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackDetailActivity.this);
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
||||
setLockscreenPolicy();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(TrackDetailActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
|
||||
if (key == null) return;
|
||||
@@ -158,8 +153,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
private MenuItem insertPhotoMenuItem;
|
||||
private MenuItem markerListMenuItem;
|
||||
private MenuItem shareMenuItem;
|
||||
private MenuItem voiceFrequencyMenuItem;
|
||||
private MenuItem splitFrequencyMenuItem;
|
||||
|
||||
private final OnClickListener recordListener = new OnClickListener() {
|
||||
@Override
|
||||
@@ -189,37 +182,60 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||
|
||||
hasCamera = getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
|
||||
photoUri = savedInstanceState != null ? (Uri) savedInstanceState.getParcelable(PHOTO_URI_KEY) : null;
|
||||
hasPhoto = savedInstanceState != null && savedInstanceState.getBoolean(HAS_PHOTO_KEY, false);
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
handleIntent(getIntent());
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
||||
trackDataHub = TrackDataHub.newInstance(this);
|
||||
trackDataHub = new TrackDataHub(this);
|
||||
|
||||
tabHost = findViewById(R.id.tackdetail_tabhost);
|
||||
tabHost.setup();
|
||||
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
ViewPager viewPager = findViewById(R.id.pager);
|
||||
TabsAdapter tabsAdapter = new TabsAdapter(this, tabHost, viewPager);
|
||||
|
||||
TabSpec statsTabSpec = tabHost.newTabSpec(StatsFragment.STATS_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_stats_tab));
|
||||
tabsAdapter.addTab(statsTabSpec, StatsFragment.class, null);
|
||||
|
||||
TabSpec chartTabSpec = tabHost.newTabSpec(ChartFragment.CHART_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_chart_tab));
|
||||
tabsAdapter.addTab(chartTabSpec, ChartFragment.class, null);
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return new StatsFragment();
|
||||
case 1:
|
||||
return new ChartTimeFragment();
|
||||
case 2:
|
||||
return new ChartDistanceFragment();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@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) {
|
||||
tabHost.setCurrentTabByTag(savedInstanceState.getString(CURRENT_TAB_TAG_KEY));
|
||||
}
|
||||
|
||||
// Set the background after all three tabs are added
|
||||
for (int i = 0; i < tabHost.getTabWidget().getChildCount(); i++) {
|
||||
tabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.tab_indicator);
|
||||
pager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
||||
}
|
||||
|
||||
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
||||
@@ -228,16 +244,31 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
setLockscreenPolicy();
|
||||
setScreenOnPolicy();
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
private void setLockscreenPolicy() {
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackDetailActivity.this)
|
||||
&& PreferencesUtils.isRecording(this);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(showOnLockScreen);
|
||||
} else if (showOnLockScreen) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
} else {
|
||||
if (showOnLockScreen)
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
else getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
private void setScreenOnPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackDetailActivity.this)
|
||||
&& PreferencesUtils.isRecording(this);
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,14 +303,15 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection.unbind();
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
trackDataHub.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(CURRENT_TAB_TAG_KEY, tabHost.getCurrentTabTag());
|
||||
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
|
||||
|
||||
if (photoUri != null) {
|
||||
outState.putParcelable(PHOTO_URI_KEY, photoUri);
|
||||
}
|
||||
@@ -317,13 +349,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
insertMarkerMenuItem = menu.findItem(R.id.track_detail_insert_marker);
|
||||
insertPhotoMenuItem = menu.findItem(R.id.track_detail_insert_photo);
|
||||
insertPhotoMenuItem.setVisible(new Intent(MediaStore.ACTION_IMAGE_CAPTURE).resolveActivity(getPackageManager()) != null);
|
||||
|
||||
shareMenuItem = menu.findItem(R.id.track_detail_share);
|
||||
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();
|
||||
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, new long[]{trackId}));
|
||||
|
||||
voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency);
|
||||
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
|
||||
markerListMenuItem = menu.findItem(R.id.track_detail_markers);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
@@ -345,11 +371,12 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
.putExtra(MarkerEditActivity.EXTRA_TRACK_ID, trackId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.track_detail_share:
|
||||
intent = IntentUtils.newShareFileIntent(this, new long[]{trackId});
|
||||
intent = Intent.createChooser(intent, null);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.track_detail_insert_photo:
|
||||
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||
return false;
|
||||
}
|
||||
createWaypointWithPicture();
|
||||
return true;
|
||||
case R.id.track_detail_menu_show_on_map:
|
||||
@@ -360,14 +387,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
.putExtra(MarkerListActivity.EXTRA_TRACK_ID, trackId);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.track_detail_voice_frequency:
|
||||
FrequencyDialogFragment.newInstance(R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.string.menu_voice_frequency)
|
||||
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.track_detail_split_frequency:
|
||||
FrequencyDialogFragment.newInstance(R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.string.menu_split_frequency)
|
||||
.show(getSupportFragmentManager(), FrequencyDialogFragment.FREQUENCY_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.track_detail_edit:
|
||||
intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
||||
.putExtra(TrackEditActivity.EXTRA_TRACK_ID, trackId);
|
||||
@@ -395,6 +414,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@@ -420,16 +440,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
return trackDataHub;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the track id.
|
||||
*/
|
||||
public long getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the data in the intent.
|
||||
*/
|
||||
private void handleIntent(Intent intent) {
|
||||
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
long markerId = intent.getLongExtra(EXTRA_MARKER_ID, -1L);
|
||||
@@ -468,8 +478,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
insertPhotoMenuItem.setVisible(hasCamera && isRecording() && !isPaused);
|
||||
shareMenuItem.setVisible(!isRecording());
|
||||
markerListMenuItem.setShowAsAction(isRecording() ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
voiceFrequencyMenuItem.setVisible(isRecording());
|
||||
splitFrequencyMenuItem.setVisible(isRecording());
|
||||
String title;
|
||||
if (isRecording()) {
|
||||
title = getString(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||
@@ -487,7 +495,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
||||
}
|
||||
|
||||
public void chooseActivityType(String category) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), category);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,10 +28,11 @@ import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
@@ -41,7 +42,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeCaller {
|
||||
public class TrackEditActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_NEW_TRACK = "new_track";
|
||||
@@ -72,7 +73,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
return;
|
||||
}
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
track = contentProviderUtils.getTrack(trackId);
|
||||
if (track == null) {
|
||||
Log.e(TAG, "No track for " + trackId);
|
||||
@@ -119,9 +120,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||
getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -130,9 +129,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(activityType.getText().toString()).show(
|
||||
getSupportFragmentManager(),
|
||||
ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
ChooseActivityTypeDialogFragment.showDialog(getSupportFragmentManager(), activityType.getText().toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -178,11 +175,11 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind();
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(ICON_VALUE_KEY, iconValue);
|
||||
}
|
||||
|
||||