forked from upstream-mirrors/OpenTracks
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 928781391d | |||
| 05cd672464 | |||
| d3109bbc37 | |||
| eabdb7c721 | |||
| ad990e8cd7 | |||
| db3eef0104 | |||
| c4f1ba7962 | |||
| 7451fbd413 | |||
| b6c55de55a | |||
| dd3ee6d99e | |||
| bbb6a7c22c | |||
| dc1b50a7bf | |||
| 21bfb88e0b | |||
| aaab8adce9 | |||
| 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 |
@@ -0,0 +1,21 @@
|
|||||||
|
#https://github.com/marketplace/actions/android-emulator-runner
|
||||||
|
name: Test
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
api-level: [21, 29]
|
||||||
|
target: [default]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: run tests
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
arch: x86_64
|
||||||
|
script: ./gradlew connectedCheck
|
||||||
+10
-10
@@ -22,18 +22,18 @@ android:
|
|||||||
- android-29
|
- android-29
|
||||||
|
|
||||||
# Emulator Management: Create, Start and Wait
|
# Emulator Management: Create, Start and Wait
|
||||||
#install:
|
install:
|
||||||
# - echo y | sdkmanager "platform-tools"
|
- echo y | sdkmanager "platform-tools"
|
||||||
# - echo y | sdkmanager --channel=4 "emulator"
|
- echo y | sdkmanager --channel=4 "emulator"
|
||||||
# - echo y | sdkmanager "system-images;android-$API;$EMU_FLAVOR;$ABI"
|
- 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
|
- 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 &
|
- 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
|
- android-wait-for-emulator
|
||||||
# - adb shell input keyevent 82 &
|
- adb shell input keyevent 82 &
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./gradlew clean build
|
- ./gradlew build
|
||||||
# - ./gradlew connectedCheck
|
- ./gradlew connectedCheck
|
||||||
|
|
||||||
sbefore_cache:
|
sbefore_cache:
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
## Features:
|
## Features:
|
||||||
* __Tracking:__ track your sport and outdoor activities
|
* __Tracking:__ track your sport and outdoor activities
|
||||||
* __Photos and Markers:__ mark interesting locations while tracking (also with photos)
|
* __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
|
* __Export data:__ export tracks either as KMZ (incl. photos), KML or GPX
|
||||||
* __No Internet access:__ Internet is not used
|
* __No Internet access:__ Internet is not used
|
||||||
* __Voice annoucements__
|
* __Voice announcements__
|
||||||
* __No advertising__
|
* __No advertising__
|
||||||
* __Privacy:__
|
* __Privacy:__
|
||||||
* _does not contain any in-app analytics_
|
* _does not contain any in-app analytics_
|
||||||
@@ -47,6 +47,9 @@ _Please note:_
|
|||||||
_Tracks_ does not contain a _map_ (as this would required Internet access).
|
_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.
|
To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)) needs to be installed.
|
||||||
|
|
||||||
|
## Custom Dashboards
|
||||||
|
As of v3.3.1 OpenTracks enables to use custom dashboards.
|
||||||
|
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# OpenTracks Data API
|
||||||
|
|
||||||
|
## Reason
|
||||||
|
OpenTracks by itself only provides basic features for data visualization and also lacks functionality for analytics.
|
||||||
|
By itself OpenTracks provides a basic dashboard that can show summary of a track (while recording as well as afterwards).
|
||||||
|
Although this might be (mostly) sufficient, it has some limitations.
|
||||||
|
This include limited configurability (e.g., ordering of shown elements) and the lack of Internet access.
|
||||||
|
In fact, the later is considered a feature but severely limits the implementation presenting a map (at least in an easy way).
|
||||||
|
|
||||||
|
_NOTE:_ for showing a track on a map a workaround was implemented in OpenTracks.
|
||||||
|
Data could be exported as KMZ and presented in 3rd-party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
|
||||||
|
|
||||||
|
For the time being, the OpenTracks Data API can be used to implement mainly custom dashboards.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
OpenTracks by default does not expose any data to 3rd-party applications.
|
||||||
|
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s of one or more selected tracks.
|
||||||
|
For each, one URI is provided.
|
||||||
|
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
* only tracks selected by the user are exposed,
|
||||||
|
* `Waypoints` are not exposed,
|
||||||
|
* access is only granted temporarily and automatically revoked (see `FLAG_GRANT_READ_URI_PERMISSION`),
|
||||||
|
* no write access is possible, and
|
||||||
|
* neither track recordings can be started, paused, or stopped.
|
||||||
|
|
||||||
|
All required information can be found in `IntentDashboardUtils.java`.
|
||||||
|
|
||||||
|
A _reference implementation_ is the [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard).
|
||||||
@@ -24,12 +24,13 @@ Application id: de.dennisguse.opentracks.playstore
|
|||||||
|
|
||||||
Requirement: `gem install fastlane`
|
Requirement: `gem install fastlane`
|
||||||
|
|
||||||
1. Update store meta data (e.g., description and screenshots)
|
1. Build signed apk
|
||||||
`fastlane supply --version_code $(git rev-list HEAD --count master) --skip_upload_apk`
|
|
||||||
(requires API key)
|
|
||||||
2. Build signed apk
|
|
||||||
(requires keystore)
|
(requires keystore)
|
||||||
3. Upload signed apk to [Google Play Console](https://play.google.com/apps/publish)
|
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 (localization)
|
||||||
|
|
||||||
+12
-1
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +54,11 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
|
manifestPlaceholders = [
|
||||||
|
applicationLogo: "@drawable/ic_logo_color_24dp",
|
||||||
|
applicationName: "@string/app_name"
|
||||||
|
]
|
||||||
|
|
||||||
//Useful for debugging.
|
//Useful for debugging.
|
||||||
//versionCode getVersionCode()
|
//versionCode getVersionCode()
|
||||||
|
|
||||||
@@ -69,6 +74,10 @@ android {
|
|||||||
debug {
|
debug {
|
||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
versionNameSuffix "-debug"
|
versionNameSuffix "-debug"
|
||||||
|
manifestPlaceholders = [
|
||||||
|
applicationLogo: "@drawable/ic_logo_black_24dp",
|
||||||
|
applicationName: "OpenTracks (Debug)"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
release {
|
release {
|
||||||
@@ -89,6 +98,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.2.0'
|
androidTestImplementation 'androidx.test:core:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
@@ -100,4 +110,5 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation 'androidx.test:core:1.2.0'
|
testImplementation 'androidx.test:core:1.2.0'
|
||||||
testImplementation 'org.mockito:mockito-core:3.0.0'
|
testImplementation 'org.mockito:mockito-core:3.0.0'
|
||||||
|
implementation 'com.google.android.material:material:1.1.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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v3.2.11: OpenTracks
|
||||||
|
|
||||||
|
Bug:
|
||||||
|
- Max speed is identical to avg speed
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash on heart rate sensor selection
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Replaced TabHost by TabsLayout
|
||||||
|
- Removed special first waypoint
|
||||||
|
- Removed duplicate translations
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
v3.3.0: OpenTracks
|
||||||
|
|
||||||
|
Breaking changes:
|
||||||
|
- Pictures are now stored in app-private storage (do not show up other picture apps)
|
||||||
|
NOTE: Pictures taking with previous version cannot be shown
|
||||||
|
- Localization for Spanish (Latin America) only works on Android 7.0 or later
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Import/Export via Storage Access Framework (avoids READ/WRITE_EXTERNAL_STORAGE permission)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Cleaned internal data structures
|
||||||
|
- Use separate icons/app names for debug version
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v3.3.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Android 10: on uninstall allow to keep app data
|
||||||
|
- Introduced Dashboard API: map icon can be used to open current track in external app
|
||||||
|
(requires installation of another app like OSMDashboard)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- App label was broken (introduced in v3.3.0)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Documentation for Dashboard API can be found in README_DEVELOPER.md
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
v3.3.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Export: overwrites files now
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Always remove recording notification
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- On API 26+ java.time is used for parsing and writing dates
|
||||||
|
- Refactoring of TrackRecordingService
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v3.4.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Dashboard API: multiple tracks
|
||||||
|
- Updated help
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fixed crash on fragment re-creation
|
||||||
|
- When paused: prevent stopping app
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Enable testing via CI (Github Actions and TravisCI)
|
||||||
@@ -1 +1 @@
|
|||||||
OpenTracks est une application de suivi d'activités sportives qui respecte totalement votre vie privée.
|
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,16 +60,16 @@ public class TrackStubUtils {
|
|||||||
*
|
*
|
||||||
* @return a track stub.
|
* @return a track stub.
|
||||||
*/
|
*/
|
||||||
public static SensorDataSetLocation createSensorDataSetLocation() {
|
public static TrackPoint createSensorDataSetLocation() {
|
||||||
return createSensorDataSetLocation(INITIAL_LATITUDE, INITIAL_LONGITUDE, INITIAL_ALTITUDE);
|
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.
|
* @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
|
// Initial Location
|
||||||
Location loc = new Location(LOCATION_PROVIDER);
|
Location loc = new Location(LOCATION_PROVIDER);
|
||||||
loc.setLatitude(latitude);
|
loc.setLatitude(latitude);
|
||||||
@@ -81,6 +81,6 @@ public class TrackStubUtils {
|
|||||||
loc.setBearing(INITIAL_BEARING);
|
loc.setBearing(INITIAL_BEARING);
|
||||||
SensorDataSet sd = new SensorDataSet(Float.NaN, Float.NaN);
|
SensorDataSet sd = new SensorDataSet(Float.NaN, Float.NaN);
|
||||||
|
|
||||||
return new SensorDataSetLocation(loc, sd);
|
return new TrackPoint(loc, sd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-49
@@ -27,29 +27,30 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link DescriptionGeneratorImpl}.
|
* Tests for {@link DescriptionGenerator}.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class DescriptionGeneratorImplTest {
|
public class DescriptionGeneratorTest {
|
||||||
|
|
||||||
private static final long START_TIME = 1288721514000L;
|
private static final long START_TIME = 1288721514000L;
|
||||||
private DescriptionGeneratorImpl descriptionGenerator;
|
private DescriptionGenerator descriptionGenerator;
|
||||||
|
|
||||||
private Context context = ApplicationProvider.getApplicationContext();
|
private Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
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
|
@Test
|
||||||
public void testGenerateTrackDescription() {
|
public void testGenerateTrackDescription() {
|
||||||
@@ -68,7 +69,7 @@ public class DescriptionGeneratorImplTest {
|
|||||||
track.setTripStatistics(stats);
|
track.setTripStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setCategory("hiking");
|
||||||
String expected = //"Created by"
|
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>"
|
+ "Name: -<br>"
|
||||||
+ "Activity type: hiking<br>"
|
+ "Activity type: hiking<br>"
|
||||||
+ "Description: -<br>"
|
+ "Description: -<br>"
|
||||||
@@ -91,44 +92,9 @@ public class DescriptionGeneratorImplTest {
|
|||||||
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
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
|
@Test
|
||||||
public void testWriteDistance() {
|
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
|
@Test
|
||||||
public void testWriteTime() {
|
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
|
@Test
|
||||||
public void testWriteSpeed() {
|
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
|
@Test
|
||||||
public void testWriteElevation() {
|
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
|
@Test
|
||||||
public void testWritePace() {
|
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
|
@Test
|
||||||
public void testWriteGrade() {
|
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
|
@Test
|
||||||
public void testWriteGrade_nan() {
|
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
|
@Test
|
||||||
public void testWriteGrade_infinite() {
|
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.ScoredResult;
|
||||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
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;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,10 +83,6 @@ public class SearchEngineTest {
|
|||||||
stats.setStopTime(NOW);
|
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);
|
Uri uri = providerUtils.insertTrack(track);
|
||||||
return ContentUris.parseId(uri);
|
return ContentUris.parseId(uri);
|
||||||
}
|
}
|
||||||
@@ -209,20 +208,6 @@ public class SearchEngineTest {
|
|||||||
assertWaypointResult(descriptionWaypointId, results.get(2));
|
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
|
@Test
|
||||||
public void testSearchWaypointDistance() {
|
public void testSearchWaypointDistance() {
|
||||||
// All results match text, but they're at difference distances from the user.
|
// All results match text, but they're at difference distances from the user.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-7
@@ -14,8 +14,9 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -26,7 +27,9 @@ import org.junit.Assert;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link CustomContentProvider}.
|
* Tests {@link CustomContentProvider}.
|
||||||
@@ -44,13 +47,13 @@ public class CustomContentProviderTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
context.deleteDatabase(DATABASE_NAME);
|
context.deleteDatabase(DATABASE_NAME);
|
||||||
db = (new DatabaseHelper(context, DATABASE_NAME)).getWritableDatabase();
|
db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase();
|
||||||
customContentProvider = new CustomContentProvider() {
|
customContentProvider = new CustomContentProvider() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
|
* Tests {@link CustomSQLiteOpenHelper#onCreate(SQLiteDatabase)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDatabaseHelper_OnCreate() {
|
public void testDatabaseHelper_OnCreate() {
|
||||||
@@ -73,8 +76,13 @@ public class CustomContentProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetType() {
|
public void testGetType() {
|
||||||
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
||||||
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
Assert.assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
|
||||||
|
|
||||||
|
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
|
||||||
|
Assert.assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
|
||||||
|
|
||||||
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||||
|
Assert.assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,7 +147,7 @@ public class CustomContentProviderTest {
|
|||||||
createTable(TrackPointsColumns.TABLE_NAME);
|
createTable(TrackPointsColumns.TABLE_NAME);
|
||||||
createTable(WaypointsColumns.TABLE_NAME);
|
createTable(WaypointsColumns.TABLE_NAME);
|
||||||
|
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
CustomSQLiteOpenHelper databaseHelper = new CustomSQLiteOpenHelper(context);
|
||||||
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
|
databaseHelper.onUpgrade(db, oldVersion, CustomSQLiteOpenHelper.DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+149
-284
@@ -13,7 +13,7 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
@@ -35,7 +35,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
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 de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
|
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -52,12 +58,9 @@ public class CustomContentProviderUtilsTest {
|
|||||||
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
|
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
|
||||||
private static final String TEST_DESC = "Test Desc!";
|
private static final String TEST_DESC = "Test Desc!";
|
||||||
private static final String TEST_DESC_NEW = "Test Desc new!";
|
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 Context context = ApplicationProvider.getApplicationContext();
|
||||||
private ContentProviderUtils providerUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Cursor cursorMock;
|
private Cursor cursorMock;
|
||||||
@@ -67,22 +70,22 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
providerUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noPoints() {
|
public void testLocationIterator_noPoints() {
|
||||||
testIterator(1, 0, 1, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(1, 0, 1, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_customFactory() {
|
public void testLocationIterator_customFactory() {
|
||||||
final Location location = new Location("test_location");
|
final TrackPoint location = new TrackPoint("test_location");
|
||||||
final AtomicInteger counter = new AtomicInteger();
|
final AtomicInteger counter = new AtomicInteger();
|
||||||
testIterator(1, 15, 4, false, new LocationFactory() {
|
testIterator(1, 15, 4, false, new TrackPointFactory() {
|
||||||
@Override
|
@Override
|
||||||
public Location createLocation() {
|
public TrackPoint createLocation() {
|
||||||
counter.incrementAndGet();
|
counter.incrementAndGet();
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
@@ -103,45 +106,45 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noBatchAscending() {
|
public void testLocationIterator_noBatchAscending() {
|
||||||
testIterator(1, 50, 100, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(1, 50, 100, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
testIterator(2, 50, 50, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(2, 50, 50, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noBatchDescending() {
|
public void testLocationIterator_noBatchDescending() {
|
||||||
testIterator(1, 50, 100, true, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(1, 50, 100, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
testIterator(2, 50, 50, true, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(2, 50, 50, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_batchAscending() {
|
public void testLocationIterator_batchAscending() {
|
||||||
testIterator(1, 50, 11, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(1, 50, 11, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
testIterator(2, 50, 25, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(2, 50, 25, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_batchDescending() {
|
public void testLocationIterator_batchDescending() {
|
||||||
testIterator(1, 50, 11, true, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(1, 50, 11, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
testIterator(2, 50, 25, true, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
testIterator(2, 50, 25, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_largeTrack() {
|
public void testLocationIterator_largeTrack() {
|
||||||
testIterator(1, 20000, 2000, false, LocationFactory.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);
|
long lastPointId = initializeTrack(trackId, numPoints);
|
||||||
((ContentProviderUtils) providerUtils).setDefaultCursorBatchSize(batchSize);
|
((ContentProviderUtils) contentProviderUtils).setDefaultCursorBatchSize(batchSize);
|
||||||
List<Location> locations = new ArrayList<Location>(numPoints);
|
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()) {
|
while (it.hasNext()) {
|
||||||
Location loc = it.next();
|
Location loc = it.next();
|
||||||
Assert.assertNotNull(loc);
|
Assert.assertNotNull(loc);
|
||||||
locations.add(loc);
|
locations.add(loc);
|
||||||
// Make sure the IDs are returned in the right order.
|
// Make sure the IDs are returned in the right order.
|
||||||
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
|
Assert.assertEquals(descending ? lastPointId - locations.size() + 1
|
||||||
: lastPointId - numPoints + locations.size(), it.getLocationId());
|
: lastPointId - numPoints + locations.size(), it.getTrackPointId());
|
||||||
}
|
}
|
||||||
Assert.assertEquals(numPoints, locations.size());
|
Assert.assertEquals(numPoints, locations.size());
|
||||||
}
|
}
|
||||||
@@ -153,8 +156,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
track.setId(id);
|
track.setId(id);
|
||||||
track.setName("Test: " + id);
|
track.setName("Test: " + id);
|
||||||
track.setNumberOfPoints(numPoints);
|
track.setNumberOfPoints(numPoints);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
track = providerUtils.getTrack(id);
|
track = contentProviderUtils.getTrack(id);
|
||||||
Assert.assertNotNull(track);
|
Assert.assertNotNull(track);
|
||||||
|
|
||||||
Location[] locations = new Location[numPoints];
|
Location[] locations = new Location[numPoints];
|
||||||
@@ -166,15 +169,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
loc.setAltitude(i * 2.5);
|
loc.setAltitude(i * 2.5);
|
||||||
locations[i] = loc;
|
locations[i] = loc;
|
||||||
}
|
}
|
||||||
providerUtils.bulkInsertTrackPoint(locations, numPoints, id);
|
contentProviderUtils.bulkInsertTrackPoint(locations, numPoints, id);
|
||||||
|
|
||||||
// Load all inserted locations.
|
// Load all inserted locations.
|
||||||
long lastPointId = -1;
|
long lastPointId = -1;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
try (LocationIterator it = providerUtils.getTrackPointLocationIterator(id, -1L, false, LocationFactory.DEFAULT_LOCATION_FACTORY)) {
|
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, -1L, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY)) {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
it.next();
|
it.next();
|
||||||
lastPointId = it.getLocationId();
|
lastPointId = it.getTrackPointId();
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +211,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
String name = NAME_PREFIX + trackId;
|
String name = NAME_PREFIX + trackId;
|
||||||
when(cursorMock.getString(columnIndex++)).thenReturn(name);
|
when(cursorMock.getString(columnIndex++)).thenReturn(name);
|
||||||
|
|
||||||
Track track = providerUtils.createTrack(cursorMock);
|
Track track = contentProviderUtils.createTrack(cursorMock);
|
||||||
Assert.assertEquals(trackId, track.getId());
|
Assert.assertEquals(trackId, track.getId());
|
||||||
Assert.assertEquals(name, track.getName());
|
Assert.assertEquals(name, track.getName());
|
||||||
}
|
}
|
||||||
@@ -220,23 +223,23 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testDeleteAllTracks() {
|
public void testDeleteAllTracks() {
|
||||||
// Insert track, points and waypoint at first.
|
// Insert track, points and waypoint at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
insertTrackWithLocations(track);
|
insertTrackWithLocations(track);
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
providerUtils.insertWaypoint(waypoint);
|
contentProviderUtils.insertWaypoint(waypoint);
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(1, tracksCursor.getCount());
|
Assert.assertEquals(1, tracksCursor.getCount());
|
||||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(1, waypointCursor.getCount());
|
Assert.assertEquals(1, waypointCursor.getCount());
|
||||||
// Delete all.
|
// Delete all.
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
// Check whether all have been deleted.
|
// Check whether all have been deleted.
|
||||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(0, tracksCursor.getCount());
|
Assert.assertEquals(0, tracksCursor.getCount());
|
||||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
@@ -249,29 +252,29 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testDeleteTrack() {
|
public void testDeleteTrack() {
|
||||||
// Insert three tracks, points of two tracks and way point of one track.
|
// Insert three tracks, points of two tracks and way point of one track.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
|
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
insertTrackWithLocations(getTrack(trackId + 1, 10));
|
insertTrackWithLocations(TestDataUtil.getTrack(trackId + 1, 10));
|
||||||
insertTrackWithLocations(getTrack(trackId + 2, 10));
|
insertTrackWithLocations(TestDataUtil.getTrack(trackId + 2, 10));
|
||||||
|
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
waypoint.setTrackId(trackId);
|
waypoint.setTrackId(trackId);
|
||||||
providerUtils.insertWaypoint(waypoint);
|
contentProviderUtils.insertWaypoint(waypoint);
|
||||||
|
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(3, tracksCursor.getCount());
|
Assert.assertEquals(3, tracksCursor.getCount());
|
||||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(1, waypointCursor.getCount());
|
Assert.assertEquals(1, waypointCursor.getCount());
|
||||||
// Delete one track.
|
// Delete one track.
|
||||||
providerUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
// Check whether all data of a track has been deleted.
|
// Check whether all data of a track has been deleted.
|
||||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(2, tracksCursor.getCount());
|
Assert.assertEquals(2, tracksCursor.getCount());
|
||||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
@@ -282,10 +285,10 @@ public class CustomContentProviderUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetAllTracks() {
|
public void testGetAllTracks() {
|
||||||
int initialTrackNumber = providerUtils.getAllTracks().size();
|
int initialTrackNumber = contentProviderUtils.getAllTracks().size();
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||||
List<Track> allTracks = providerUtils.getAllTracks();
|
List<Track> allTracks = contentProviderUtils.getAllTracks();
|
||||||
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
|
Assert.assertEquals(initialTrackNumber + 1, allTracks.size());
|
||||||
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
Assert.assertEquals(trackId, allTracks.get(allTracks.size() - 1).getId());
|
||||||
}
|
}
|
||||||
@@ -296,8 +299,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetLastTrack() {
|
public void testGetLastTrack() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||||
Assert.assertEquals(trackId, providerUtils.getLastTrack().getId());
|
Assert.assertEquals(trackId, contentProviderUtils.getLastTrack().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,8 +309,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetTrack() {
|
public void testGetTrack() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
contentProviderUtils.insertTrack(TestDataUtil.getTrack(trackId, 0));
|
||||||
Assert.assertNotNull(providerUtils.getTrack(trackId));
|
Assert.assertNotNull(contentProviderUtils.getTrack(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -316,15 +319,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testUpdateTrack() {
|
public void testUpdateTrack() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 0);
|
Track track = TestDataUtil.getTrack(trackId, 0);
|
||||||
String nameOld = "name1";
|
String nameOld = "name1";
|
||||||
String nameNew = "name2";
|
String nameNew = "name2";
|
||||||
track.setName(nameOld);
|
track.setName(nameOld);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
Assert.assertEquals(nameOld, providerUtils.getTrack(trackId).getName());
|
Assert.assertEquals(nameOld, contentProviderUtils.getTrack(trackId).getName());
|
||||||
track.setName(nameNew);
|
track.setName(nameNew);
|
||||||
providerUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
Assert.assertEquals(nameNew, providerUtils.getTrack(trackId).getName());
|
Assert.assertEquals(nameNew, contentProviderUtils.getTrack(trackId).getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -333,8 +336,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateContentValues_waypoint() {
|
public void testCreateContentValues_waypoint() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
|
||||||
// Bottom
|
// Bottom
|
||||||
long startTime = 1000L;
|
long startTime = 1000L;
|
||||||
// AverageSpeed
|
// AverageSpeed
|
||||||
@@ -351,12 +353,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
statistics.setMinElevation(1200.0);
|
statistics.setMinElevation(1200.0);
|
||||||
statistics.setMaxGrade(15.0);
|
statistics.setMaxGrade(15.0);
|
||||||
statistics.setMinGrade(minGrade);
|
statistics.setMinGrade(minGrade);
|
||||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
|
||||||
|
track.setTripStatistics(statistics);
|
||||||
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
waypoint.setType(WaypointType.STATISTICS);
|
|
||||||
waypoint.setTripStatistics(statistics);
|
|
||||||
|
|
||||||
Location location = new Location("test");
|
Location location = new Location("test");
|
||||||
location.setLatitude(22);
|
location.setLatitude(22);
|
||||||
@@ -364,7 +367,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
location.setAccuracy((float) 1 / 100.0f);
|
location.setAccuracy((float) 1 / 100.0f);
|
||||||
location.setAltitude(2.5);
|
location.setAltitude(2.5);
|
||||||
waypoint.setLocation(location);
|
waypoint.setLocation(location);
|
||||||
providerUtils.insertWaypoint(waypoint);
|
contentProviderUtils.insertWaypoint(waypoint);
|
||||||
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(contentResolverMock);
|
||||||
|
|
||||||
@@ -374,8 +377,6 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
|
Assert.assertEquals(waypointId, contentValues.get(WaypointsColumns._ID));
|
||||||
Assert.assertEquals(22 * 1000000, contentValues.get(WaypointsColumns.LONGITUDE));
|
Assert.assertEquals(22 * 1000000, contentValues.get(WaypointsColumns.LONGITUDE));
|
||||||
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
|
Assert.assertEquals(TEST_DESC, contentValues.get(WaypointsColumns.DESCRIPTION));
|
||||||
Assert.assertEquals(startTime, contentValues.get(WaypointsColumns.STARTTIME));
|
|
||||||
Assert.assertEquals(minGrade, contentValues.get(WaypointsColumns.MINGRADE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -406,7 +407,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
long trackId = 11L;
|
long trackId = 11L;
|
||||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||||
|
|
||||||
Waypoint waypoint = providerUtils.createWaypoint(cursorMock);
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursorMock);
|
||||||
Assert.assertEquals(id, waypoint.getId());
|
Assert.assertEquals(id, waypoint.getId());
|
||||||
Assert.assertEquals(name, waypoint.getName());
|
Assert.assertEquals(name, waypoint.getName());
|
||||||
Assert.assertEquals(trackId, waypoint.getTrackId());
|
Assert.assertEquals(trackId, waypoint.getTrackId());
|
||||||
@@ -414,50 +415,36 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method
|
* Tests the method
|
||||||
* {@link ContentProviderUtils#deleteWaypoint(Context, long, DescriptionGenerator)}
|
* {@link ContentProviderUtils#deleteWaypoint(long)}
|
||||||
* when there is only one waypoint in the track.
|
* when there is only one waypoint in the track.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteWaypoint_onlyOneWayPoint() {
|
public void testDeleteWaypoint_onlyOneWayPoint() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Waypoint waypoint1 = new Waypoint();
|
Waypoint waypoint1 = new Waypoint();
|
||||||
waypoint1.setDescription(TEST_DESC);
|
waypoint1.setDescription(TEST_DESC);
|
||||||
waypoint1.setTrackId(trackId);
|
waypoint1.setTrackId(trackId);
|
||||||
waypoint1.setType(WaypointType.STATISTICS);
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
providerUtils.insertWaypoint(waypoint1);
|
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
|
contentProviderUtils.deleteWaypoint(1);
|
||||||
|
|
||||||
@Override
|
Assert.assertNull(contentProviderUtils.getWaypoint(1));
|
||||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
|
||||||
return CustomContentProviderUtilsTest.MOCK_DESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateTrackDescription(Track aTrack, boolean html) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
providerUtils.deleteWaypoint(context, 1, descriptionGenerator);
|
|
||||||
|
|
||||||
Assert.assertNull(providerUtils.getWaypoint(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method
|
* Tests the method
|
||||||
* {@link ContentProviderUtils#deleteWaypoint(Context, long, DescriptionGenerator)}
|
* {@link ContentProviderUtils#deleteWaypoint(long)}
|
||||||
* when there is more than one waypoint in the track.
|
* when there is more than one waypoint in the track.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteWaypoint_hasNextWayPoint() {
|
public void testDeleteWaypoint_hasNextWayPoint() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
|
||||||
|
|
||||||
TripStatistics statistics = new TripStatistics();
|
TripStatistics statistics = new TripStatistics();
|
||||||
statistics.setStartTime(1000L);
|
statistics.setStartTime(1000L);
|
||||||
@@ -471,124 +458,53 @@ public class CustomContentProviderUtilsTest {
|
|||||||
statistics.setMinElevation(1200.0);
|
statistics.setMinElevation(1200.0);
|
||||||
statistics.setMaxGrade(15.0);
|
statistics.setMaxGrade(15.0);
|
||||||
statistics.setMinGrade(-25.0);
|
statistics.setMinGrade(-25.0);
|
||||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
|
||||||
|
track.setTripStatistics(statistics);
|
||||||
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Waypoint waypoint1 = new Waypoint();
|
Waypoint waypoint1 = new Waypoint();
|
||||||
waypoint1.setDescription(Long.toString(trackId));
|
waypoint1.setDescription(MOCK_DESC);
|
||||||
waypoint1.setTrackId(trackId);
|
waypoint1.setTrackId(trackId);
|
||||||
waypoint1.setType(WaypointType.STATISTICS);
|
long waypoint1Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint1));
|
||||||
waypoint1.setTripStatistics(statistics);
|
|
||||||
long waypoint1Id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint1));
|
|
||||||
|
|
||||||
Waypoint waypoint2 = new Waypoint();
|
Waypoint waypoint2 = new Waypoint();
|
||||||
waypoint2.setDescription(Long.toString(trackId));
|
waypoint2.setDescription(MOCK_DESC);
|
||||||
waypoint2.setTrackId(trackId);
|
waypoint2.setTrackId(trackId);
|
||||||
waypoint2.setType(WaypointType.STATISTICS);
|
long waypoint2Id = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint2));
|
||||||
waypoint2.setTripStatistics(statistics);
|
|
||||||
long waypoint2Id = ContentUris.parseId(providerUtils.insertWaypoint(waypoint2));
|
|
||||||
|
|
||||||
|
|
||||||
// Delete
|
// Delete
|
||||||
DescriptionGenerator descriptionGenerator = new DescriptionGenerator() {
|
Assert.assertNotNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
@Override
|
contentProviderUtils.deleteWaypoint(waypoint1Id);
|
||||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
Assert.assertNull(contentProviderUtils.getWaypoint(waypoint1Id));
|
||||||
return CustomContentProviderUtilsTest.MOCK_DESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Assert.assertEquals(MOCK_DESC, contentProviderUtils.getWaypoint(waypoint2Id).getDescription());
|
||||||
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(CustomContentProviderUtilsTest.MOCK_DESC, providerUtils.getWaypoint(waypoint2Id)
|
|
||||||
.getDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#getFirstWaypointId(long)}.
|
* Tests the method {@link ContentProviderUtils#getNextWaypointNumber(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 ContentProviderUtils#getNextWaypointNumber(long, WaypointType)}.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetNextWaypointNumber() {
|
public void testGetNextWaypointNumber() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
Waypoint waypoint1 = new Waypoint();
|
Waypoint waypoint1 = new Waypoint();
|
||||||
waypoint1.setType(WaypointType.STATISTICS);
|
|
||||||
waypoint1.setTrackId(trackId);
|
waypoint1.setTrackId(trackId);
|
||||||
Waypoint waypoint2 = new Waypoint();
|
Waypoint waypoint2 = new Waypoint();
|
||||||
waypoint2.setType(WaypointType.WAYPOINT);
|
|
||||||
waypoint2.setTrackId(trackId);
|
waypoint2.setTrackId(trackId);
|
||||||
Waypoint waypoint3 = new Waypoint();
|
Waypoint waypoint3 = new Waypoint();
|
||||||
waypoint3.setType(WaypointType.STATISTICS);
|
|
||||||
waypoint3.setTrackId(trackId);
|
waypoint3.setTrackId(trackId);
|
||||||
Waypoint waypoint4 = new Waypoint();
|
Waypoint waypoint4 = new Waypoint();
|
||||||
waypoint4.setType(WaypointType.WAYPOINT);
|
|
||||||
waypoint4.setTrackId(trackId);
|
waypoint4.setTrackId(trackId);
|
||||||
providerUtils.insertWaypoint(waypoint1);
|
contentProviderUtils.insertWaypoint(waypoint1);
|
||||||
providerUtils.insertWaypoint(waypoint2);
|
contentProviderUtils.insertWaypoint(waypoint2);
|
||||||
providerUtils.insertWaypoint(waypoint3);
|
contentProviderUtils.insertWaypoint(waypoint3);
|
||||||
providerUtils.insertWaypoint(waypoint4);
|
contentProviderUtils.insertWaypoint(waypoint4);
|
||||||
|
|
||||||
Assert.assertEquals(2, providerUtils.getNextWaypointNumber(trackId, WaypointType.STATISTICS));
|
Assert.assertEquals(4, contentProviderUtils.getNextWaypointNumber(trackId));
|
||||||
Assert.assertEquals(3, providerUtils.getNextWaypointNumber(trackId, WaypointType.WAYPOINT));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method
|
|
||||||
* {@link ContentProviderUtils#getLastWaypoint(long, WaypointType)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetLastStatisticsWaypoint() {
|
|
||||||
long trackId = System.currentTimeMillis();
|
|
||||||
Track track = getTrack(trackId, 10);
|
|
||||||
providerUtils.insertTrack(track);
|
|
||||||
|
|
||||||
Waypoint waypoint1 = new Waypoint();
|
|
||||||
waypoint1.setTrackId(trackId);
|
|
||||||
waypoint1.setType(WaypointType.STATISTICS);
|
|
||||||
waypoint1.setDescription("Desc1");
|
|
||||||
Waypoint waypoint2 = new Waypoint();
|
|
||||||
waypoint2.setTrackId(trackId);
|
|
||||||
waypoint2.setType(WaypointType.STATISTICS);
|
|
||||||
waypoint2.setDescription("Desc2");
|
|
||||||
Waypoint waypoint3 = new Waypoint();
|
|
||||||
waypoint3.setTrackId(trackId);
|
|
||||||
waypoint3.setType(WaypointType.WAYPOINT);
|
|
||||||
waypoint3.setDescription("Desc3");
|
|
||||||
providerUtils.insertWaypoint(waypoint1);
|
|
||||||
providerUtils.insertWaypoint(waypoint2);
|
|
||||||
providerUtils.insertWaypoint(waypoint3);
|
|
||||||
|
|
||||||
Assert.assertEquals("Desc2", providerUtils.getLastWaypoint(trackId, WaypointType.STATISTICS).getDescription());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -598,15 +514,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testInsertAndGetWaypoint() {
|
public void testInsertAndGetWaypoint() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
waypoint.setTrackId(trackId);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -615,20 +531,20 @@ public class CustomContentProviderUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testUpdateWaypoint() {
|
public void testUpdateWaypoint() {
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
waypoint.setDescription(TEST_DESC);
|
waypoint.setDescription(TEST_DESC);
|
||||||
waypoint.setTrackId(trackId);
|
waypoint.setTrackId(trackId);
|
||||||
long waypointId = ContentUris.parseId(providerUtils.insertWaypoint(waypoint));
|
long waypointId = ContentUris.parseId(contentProviderUtils.insertWaypoint(waypoint));
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
waypoint = providerUtils.getWaypoint(waypointId);
|
waypoint = contentProviderUtils.getWaypoint(waypointId);
|
||||||
waypoint.setDescription(TEST_DESC_NEW);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -638,13 +554,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testBulkInsertTrackPoint() {
|
public void testBulkInsertTrackPoint() {
|
||||||
// Insert track, point at first.
|
// Insert track, point at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
insertTrackWithLocations(track);
|
insertTrackWithLocations(track);
|
||||||
|
|
||||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), -1, trackId);
|
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), -1, trackId);
|
||||||
Assert.assertEquals(20, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
Assert.assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), 8, trackId);
|
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), 8, trackId);
|
||||||
Assert.assertEquals(28, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
Assert.assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -699,7 +615,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
byte[] sensor = "Sensor state".getBytes();
|
byte[] sensor = "Sensor state".getBytes();
|
||||||
when(cursorMock.getBlob(index++)).thenReturn(sensor);
|
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) longitude, location.getLongitude(), 0.01);
|
||||||
Assert.assertEquals((double) latitude, location.getLatitude(), 0.01);
|
Assert.assertEquals((double) latitude, location.getLatitude(), 0.01);
|
||||||
Assert.assertEquals(time, location.getTime(), 0.01);
|
Assert.assertEquals(time, location.getTime(), 0.01);
|
||||||
@@ -714,37 +630,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testInsertTrackPoint() {
|
public void testInsertTrackPoint() {
|
||||||
// Insert track, point at first.
|
// Insert track, point at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
insertTrackWithLocations(track);
|
insertTrackWithLocations(track);
|
||||||
|
|
||||||
providerUtils.insertTrackPoint(createLocation(22), trackId);
|
contentProviderUtils.insertTrackPoint(TestDataUtil.createLocation(22), trackId);
|
||||||
Assert.assertEquals(11, providerUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
Assert.assertEquals(11, contentProviderUtils.getTrackPointCursor(trackId, -1L, 1000, false).getCount());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the method {@link ContentProviderUtils#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 ContentProviderUtils#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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -754,10 +644,10 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testGetLastValidTrackPoint() {
|
public void testGetLastValidTrackPoint() {
|
||||||
// Insert track, points at first.
|
// Insert track, points at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
insertTrackWithLocations(track);
|
insertTrackWithLocations(track);
|
||||||
|
|
||||||
Location lastLocation = providerUtils.getLastValidTrackPoint(trackId);
|
Location lastLocation = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
checkLocation(9, lastLocation);
|
checkLocation(9, lastLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,15 +658,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testGetTrackPointCursor_desc() {
|
public void testGetTrackPointCursor_desc() {
|
||||||
// Insert track, points at first.
|
// Insert track, points at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
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());
|
Assert.assertEquals(2, cursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,117 +677,84 @@ public class CustomContentProviderUtilsTest {
|
|||||||
public void testGetTrackPointCursor_asc() {
|
public void testGetTrackPointCursor_asc() {
|
||||||
// Insert track, points at first.
|
// Insert track, points at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
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());
|
Assert.assertEquals(2, cursor.getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in descending.
|
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, TrackPointFactory)} in descending.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetTrackPointLocationIterator_desc() {
|
public void testGetTrackPointLocationIterator_desc() {
|
||||||
// Insert track, points at first.
|
// Insert track, points at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
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];
|
long startTrackPointId = trackpointIds[9];
|
||||||
|
|
||||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
for (int i = 0; i < trackpointIds.length; i++) {
|
||||||
Assert.assertTrue(locationIterator.hasNext());
|
Assert.assertTrue(locationIterator.hasNext());
|
||||||
Location location = locationIterator.next();
|
Location location = locationIterator.next();
|
||||||
Assert.assertEquals(startTrackPointId - i, locationIterator.getLocationId());
|
Assert.assertEquals(startTrackPointId - i, locationIterator.getTrackPointId());
|
||||||
checkLocation((trackpointIds.length - 1) - i, location);
|
checkLocation((trackpointIds.length - 1) - i, location);
|
||||||
}
|
}
|
||||||
Assert.assertFalse(locationIterator.hasNext());
|
Assert.assertFalse(locationIterator.hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in ascending.
|
* Tests the method {@link ContentProviderUtils#getTrackPointLocationIterator(long, long, boolean, TrackPointFactory)} in ascending.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetTrackPointLocationIterator_asc() {
|
public void testGetTrackPointLocationIterator_asc() {
|
||||||
// Insert track, point at first.
|
// Insert track, point at first.
|
||||||
long trackId = System.currentTimeMillis();
|
long trackId = System.currentTimeMillis();
|
||||||
Track track = getTrack(trackId, 10);
|
Track track = TestDataUtil.getTrack(trackId, 10);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
|
|
||||||
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
long[] trackpointIds = new long[track.getNumberOfPoints()];
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
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];
|
long startTrackPointId = trackpointIds[0];
|
||||||
|
|
||||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, LocationFactory.DEFAULT_LOCATION_FACTORY);
|
TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY);
|
||||||
for (int i = 0; i < trackpointIds.length; i++) {
|
for (int i = 0; i < trackpointIds.length; i++) {
|
||||||
Assert.assertTrue(locationIterator.hasNext());
|
Assert.assertTrue(locationIterator.hasNext());
|
||||||
Location location = locationIterator.next();
|
Location location = locationIterator.next();
|
||||||
Assert.assertEquals(startTrackPointId + i, locationIterator.getLocationId());
|
Assert.assertEquals(startTrackPointId + i, locationIterator.getTrackPointId());
|
||||||
|
|
||||||
checkLocation(i, location);
|
checkLocation(i, location);
|
||||||
}
|
}
|
||||||
Assert.assertFalse(locationIterator.hasNext());
|
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.
|
* Checks the value of a location.
|
||||||
*
|
*
|
||||||
* @param i the index of this location which created in the method {@link CustomContentProviderUtilsTest#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
|
* @param location the location to be checked
|
||||||
*/
|
*/
|
||||||
private void checkLocation(int i, Location location) {
|
private void checkLocation(int i, Location location) {
|
||||||
Assert.assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
Assert.assertEquals(TestDataUtil.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_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
||||||
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -906,7 +763,15 @@ public class CustomContentProviderUtilsTest {
|
|||||||
* @param track track to be inserted
|
* @param track track to be inserted
|
||||||
*/
|
*/
|
||||||
private void insertTrackWithLocations(Track track) {
|
private void insertTrackWithLocations(Track track) {
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatIdListForUri() {
|
||||||
|
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri(new long[]{}));
|
||||||
|
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new long[]{12}));
|
||||||
|
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new long[]{42, 43, 44}));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.TrackStubUtils;
|
import de.dennisguse.opentracks.TrackStubUtils;
|
||||||
import de.dennisguse.opentracks.chart.ChartView;
|
import de.dennisguse.opentracks.chart.ChartView;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
@@ -61,8 +61,7 @@ public class ChartFragmentTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
boolean chartByDistance = false;
|
boolean chartByDistance = false;
|
||||||
chartFragment = new ChartFragment(chartByDistance) {
|
chartFragment = (ChartFragment) ChartFragment.newInstance(chartByDistance);
|
||||||
};
|
|
||||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
||||||
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
||||||
}
|
}
|
||||||
@@ -72,7 +71,7 @@ public class ChartFragmentTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFillDataPoint_sensorIncorrect() {
|
public void testFillDataPoint_sensorIncorrect() {
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
|
|
||||||
// No input.
|
// No input.
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
@@ -96,7 +95,7 @@ public class ChartFragmentTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFillDataPoint_sensorCorrect() {
|
public void testFillDataPoint_sensorCorrect() {
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
// No input.
|
// No input.
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||||
@@ -123,17 +122,17 @@ public class ChartFragmentTest {
|
|||||||
// By distance.
|
// By distance.
|
||||||
chartFragment.setChartByDistance(true);
|
chartFragment.setChartByDistance(true);
|
||||||
// Resets last location and writes first location.
|
// Resets last location and writes first location.
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[0], 0.01);
|
Assert.assertEquals(0.0, point[0], 0.01);
|
||||||
|
|
||||||
// The second is a same location, just different time.
|
// The second is a same location, just different time.
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
Assert.assertEquals(0.0, point[0], 0.01);
|
Assert.assertEquals(0.0, point[0], 0.01);
|
||||||
|
|
||||||
// The third location is a new location, and use metric.
|
// The third location is a new location, and use metric.
|
||||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation3.setLatitude(23);
|
sensorDataSetLocation3.setLatitude(23);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||||
|
|
||||||
@@ -145,7 +144,7 @@ public class ChartFragmentTest {
|
|||||||
Assert.assertEquals(distance1, point[0], 0.01);
|
Assert.assertEquals(distance1, point[0], 0.01);
|
||||||
|
|
||||||
// The fourth location is a new location, and use metric.
|
// The fourth location is a new location, and use metric.
|
||||||
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation4.setLatitude(24);
|
sensorDataSetLocation4.setLatitude(24);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation4);
|
point = fillDataPointTestHelper(sensorDataSetLocation4);
|
||||||
|
|
||||||
@@ -167,12 +166,12 @@ public class ChartFragmentTest {
|
|||||||
chartFragment.setMetricUnits(false);
|
chartFragment.setMetricUnits(false);
|
||||||
|
|
||||||
// The first is a same location, just different time.
|
// The first is a same location, just different time.
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[0], 0.01);
|
Assert.assertEquals(0.0, point[0], 0.01);
|
||||||
|
|
||||||
// The second location is a new location, and use imperial.
|
// The second location is a new location, and use imperial.
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation2.setLatitude(23);
|
sensorDataSetLocation2.setLatitude(23);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
|
|
||||||
@@ -186,7 +185,7 @@ public class ChartFragmentTest {
|
|||||||
Assert.assertEquals(distance1, point[0], 0.01);
|
Assert.assertEquals(distance1, point[0], 0.01);
|
||||||
|
|
||||||
// The third location is a new location, and use imperial.
|
// The third location is a new location, and use imperial.
|
||||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation3.setLatitude(24);
|
sensorDataSetLocation3.setLatitude(24);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
point = fillDataPointTestHelper(sensorDataSetLocation3);
|
||||||
|
|
||||||
@@ -206,11 +205,11 @@ public class ChartFragmentTest {
|
|||||||
public void testFillDataPoint_time() {
|
public void testFillDataPoint_time() {
|
||||||
// By time
|
// By time
|
||||||
chartFragment.setChartByDistance(false);
|
chartFragment.setChartByDistance(false);
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[0], 0.01);
|
Assert.assertEquals(0.0, point[0], 0.01);
|
||||||
long timeSpan = 222;
|
long timeSpan = 222;
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
Assert.assertEquals((double) timeSpan, point[0], 0.01);
|
Assert.assertEquals((double) timeSpan, point[0], 0.01);
|
||||||
@@ -221,7 +220,7 @@ public class ChartFragmentTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFillDataPoint_elevation() {
|
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.
|
* At first, clear old points of elevation, so give true to the second parameter.
|
||||||
@@ -233,7 +232,7 @@ public class ChartFragmentTest {
|
|||||||
/*
|
/*
|
||||||
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
||||||
*/
|
*/
|
||||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
||||||
@@ -251,7 +250,7 @@ public class ChartFragmentTest {
|
|||||||
* At first, clear old points of speed, so give true to the second parameter.
|
* At first, clear old points of speed, so give true to the second parameter.
|
||||||
* It will not be filled in to the speed buffer.
|
* It will not be filled in to the speed buffer.
|
||||||
*/
|
*/
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation1.setSpeed(128.5f);
|
sensorDataSetLocation1.setSpeed(128.5f);
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||||
@@ -260,7 +259,7 @@ public class ChartFragmentTest {
|
|||||||
* Tests the logic when both metricUnits and reportSpeed are true.
|
* Tests the logic when both metricUnits and reportSpeed are true.
|
||||||
* This location will be filled into speed buffer.
|
* 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.
|
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
|
||||||
@@ -280,12 +279,12 @@ public class ChartFragmentTest {
|
|||||||
chartFragment.setMetricUnits(false);
|
chartFragment.setMetricUnits(false);
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
// First data point is not added to the speed buffer
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation1.setSpeed(100.0f);
|
sensorDataSetLocation1.setSpeed(100.0f);
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
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.
|
* Add a time span here to make sure the second point and the speed is valid.
|
||||||
@@ -306,12 +305,12 @@ public class ChartFragmentTest {
|
|||||||
chartFragment.setReportSpeed(false);
|
chartFragment.setReportSpeed(false);
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
// First data point is not added to the speed buffer
|
||||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation1.setSpeed(100.0f);
|
sensorDataSetLocation1.setSpeed(100.0f);
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
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.
|
* Add a time span here to make sure the second point and the speed is valid.
|
||||||
@@ -330,7 +329,7 @@ public class ChartFragmentTest {
|
|||||||
public void testFillDataPoint_pace_zeroSpeed() {
|
public void testFillDataPoint_pace_zeroSpeed() {
|
||||||
// Setups reportSpeed to false.
|
// Setups reportSpeed to false.
|
||||||
chartFragment.setReportSpeed(false);
|
chartFragment.setReportSpeed(false);
|
||||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
TrackPoint sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||||
sensorDataSetLocation.setSpeed(0);
|
sensorDataSetLocation.setSpeed(0);
|
||||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
|
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
|
||||||
|
|||||||
+8
-12
@@ -32,11 +32,11 @@ import java.text.SimpleDateFormat;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.SimpleTimeZone;
|
import java.util.SimpleTimeZone;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.content.TracksColumns;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.eq;
|
import static org.mockito.Mockito.eq;
|
||||||
@@ -57,9 +57,9 @@ public abstract class AbstractTestFileTrackImporter {
|
|||||||
static final double TRACK_ELEVATION = 324.0;
|
static final double TRACK_ELEVATION = 324.0;
|
||||||
|
|
||||||
static final String TRACK_TIME_0 = "2010-04-22T18:21:00Z";
|
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_1 = "2010-04-22T18:21:50.123Z";
|
||||||
static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123";
|
static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123Z";
|
||||||
static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123";
|
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_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);
|
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) {
|
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.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,7 +126,5 @@ public abstract class AbstractTestFileTrackImporter {
|
|||||||
if (time != -1L) {
|
if (time != -1L) {
|
||||||
Assert.assertEquals(time, track.getTripStatistics().getStartTime());
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-3
@@ -27,7 +27,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
@@ -208,8 +208,6 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
|||||||
|
|
||||||
// For the following, use StubReturn since we don't care whether they are invoked or not.
|
// 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.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.getRecordingTrackId(context))).thenReturn(null);
|
when(contentProviderUtils.getTrack(PreferencesUtils.getRecordingTrackId(context))).thenReturn(null);
|
||||||
contentProviderUtils.deleteTrack(context, TRACK_ID_0);
|
contentProviderUtils.deleteTrack(context, TRACK_ID_0);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
|
|||||||
+23
-19
@@ -15,14 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.services;
|
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}.
|
* Tests the {@link AdaptiveLocationListenerPolicy}.
|
||||||
*
|
*
|
||||||
* @author youtaol
|
* @author youtaol
|
||||||
*/
|
*/
|
||||||
public class AdaptiveLocationListenerPolicyTest extends TestCase {
|
@RunWith(JUnit4.class)
|
||||||
|
public class AdaptiveLocationListenerPolicyTest {
|
||||||
|
|
||||||
private static final long MIN = 1000;
|
private static final long MIN = 1000;
|
||||||
private static final long MAX = 3000;
|
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_NORMAL = 5000;
|
||||||
private static final long NEW_IDLE_TIME_SMALL = 2000;
|
private static final long NEW_IDLE_TIME_SMALL = 2000;
|
||||||
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
|
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
|
||||||
private AdaptiveLocationListenerPolicy adocationListenerPolicy;
|
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
protected void setUp() throws Exception {
|
public void setUp() {
|
||||||
super.setUp();
|
adaptiveLocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||||
adocationListenerPolicy = 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>
|
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testGetDesiredPollingInterval() {
|
public void testGetDesiredPollingInterval() {
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||||
assertEquals(MAX, adocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals(MAX, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the
|
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
|
||||||
// nearest second.
|
Assert.assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000,
|
|
||||||
adocationListenerPolicy.getDesiredPollingInterval());
|
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|
||||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
Assert.assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance_m()}.
|
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance_m()}.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testGetMinDistance() {
|
public void testGetMinDistance() {
|
||||||
assertEquals(MIN_DISTANCE, adocationListenerPolicy.getMinDistance_m());
|
Assert.assertEquals(MIN_DISTANCE, adaptiveLocationListenerPolicy.getMinDistance_m());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-28
@@ -42,12 +42,10 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
@@ -56,7 +54,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
*
|
*
|
||||||
* @author Bartlomiej Niechwiej
|
* @author Bartlomiej Niechwiej
|
||||||
* <p>
|
* <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.
|
* So, if it is executed on a real device, data might be lost.
|
||||||
*/
|
*/
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -69,7 +67,7 @@ public class TrackRecordingServiceTest {
|
|||||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
|
||||||
private Context context = ApplicationProvider.getApplicationContext();
|
private Context context = ApplicationProvider.getApplicationContext();
|
||||||
private ContentProviderUtils providerUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private final long trackId = Math.abs(new Random().nextLong());
|
private final long trackId = Math.abs(new Random().nextLong());
|
||||||
|
|
||||||
@@ -84,26 +82,26 @@ public class TrackRecordingServiceTest {
|
|||||||
};
|
};
|
||||||
customContentProvider.attachInfo(context, null);
|
customContentProvider.attachInfo(context, null);
|
||||||
|
|
||||||
providerUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
sharedPreferences.edit().clear().apply();
|
sharedPreferences.edit().clear().apply();
|
||||||
|
|
||||||
// Ensure that the database is empty before every test
|
// Ensure that the database is empty before every test
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws TimeoutException {
|
public void tearDown() throws TimeoutException {
|
||||||
// Reset service (if some previous test failed)
|
// 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()) {
|
if (service.isRecording() || service.isPaused()) {
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the database is empty after every test
|
// Ensure that the database is empty after every test
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@@ -123,12 +121,12 @@ public class TrackRecordingServiceTest {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_noTracks() throws Exception {
|
public void testRecording_noTracks() throws Exception {
|
||||||
List<Track> tracks = providerUtils.getAllTracks();
|
List<Track> tracks = contentProviderUtils.getAllTracks();
|
||||||
Assert.assertTrue(tracks.isEmpty());
|
Assert.assertTrue(tracks.isEmpty());
|
||||||
|
|
||||||
Intent startIntent = createStartIntent(context);
|
Intent startIntent = createStartIntent(context);
|
||||||
mServiceRule.startService(startIntent);
|
mServiceRule.startService(startIntent);
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(startIntent));
|
||||||
|
|
||||||
// Test if we start in no-recording mode by default.
|
// Test if we start in no-recording mode by default.
|
||||||
Assert.assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
@@ -140,7 +138,7 @@ public class TrackRecordingServiceTest {
|
|||||||
public void testRecording_oldTracks() throws Exception {
|
public void testRecording_oldTracks() throws Exception {
|
||||||
createDummyTrack(trackId, -1L, false);
|
createDummyTrack(trackId, -1L, false);
|
||||||
|
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
Assert.assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||||
}
|
}
|
||||||
@@ -149,7 +147,7 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||||
Intent startIntent = createStartIntent(context);
|
Intent startIntent = createStartIntent(context);
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(startIntent));
|
||||||
|
|
||||||
// Just set recording track to a bogus value.
|
// Just set recording track to a bogus value.
|
||||||
// Make sure that the service will not start recording and will clear the bogus track.
|
// Make sure that the service will not start recording and will clear the bogus track.
|
||||||
@@ -163,7 +161,7 @@ public class TrackRecordingServiceTest {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testStartNewTrack_alreadyRecording() throws Exception {
|
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
service.startNewTrack();
|
service.startNewTrack();
|
||||||
Assert.assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
long trackId = service.getRecordingTrackId();
|
long trackId = service.getRecordingTrackId();
|
||||||
@@ -180,7 +178,7 @@ public class TrackRecordingServiceTest {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testEndCurrentTrack_noRecording() throws Exception {
|
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
Assert.assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
// Ending the current track when there is no recording should not result in any error.
|
// Ending the current track when there is no recording should not result in any error.
|
||||||
@@ -203,8 +201,8 @@ public class TrackRecordingServiceTest {
|
|||||||
|
|
||||||
private void addTrack(Track track, boolean isRecording) {
|
private void addTrack(Track track, boolean isRecording) {
|
||||||
Assert.assertTrue(track.getId() >= 0);
|
Assert.assertTrue(track.getId() >= 0);
|
||||||
providerUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
Assert.assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
Assert.assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
||||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
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);
|
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
||||||
}
|
}
|
||||||
@@ -212,7 +210,7 @@ public class TrackRecordingServiceTest {
|
|||||||
/**
|
/**
|
||||||
* Inserts a location and waits for 200ms.
|
* 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 location = new Location("gps");
|
||||||
location.setLongitude(35.0f);
|
location.setLongitude(35.0f);
|
||||||
location.setLatitude(45.0f);
|
location.setLatitude(45.0f);
|
||||||
@@ -228,32 +226,30 @@ public class TrackRecordingServiceTest {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
Assert.assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
long waypointId = service.insertWaypoint(null, null, null, null);
|
||||||
Assert.assertEquals(-1L, waypointId);
|
Assert.assertEquals(-1L, waypointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
||||||
service.startNewTrack();
|
service.startNewTrack();
|
||||||
Assert.assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
insertLocation(service);
|
insertLocation(service);
|
||||||
|
|
||||||
long trackId = service.getRecordingTrackId();
|
long trackId = service.getRecordingTrackId();
|
||||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
long waypointId = service.insertWaypoint(null, null, null, null);
|
||||||
Assert.assertNotEquals(-1L, waypointId);
|
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_waypoint_icon_url), wpt.getIcon());
|
||||||
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
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(trackId, wpt.getTrackId());
|
||||||
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||||
Assert.assertNotNull(wpt.getLocation());
|
Assert.assertNotNull(wpt.getLocation());
|
||||||
Assert.assertNull(wpt.getTripStatistics());
|
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-14
@@ -25,10 +25,9 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
|
|
||||||
private Context context = ApplicationProvider.getApplicationContext();
|
private Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
private ContentProviderUtils providerUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void preSetUp() {
|
public static void preSetUp() {
|
||||||
@@ -68,26 +67,26 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
};
|
};
|
||||||
customContentProvider.attachInfo(context, null);
|
customContentProvider.attachInfo(context, null);
|
||||||
|
|
||||||
providerUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
sharedPreferences.edit().clear().apply();
|
sharedPreferences.edit().clear().apply();
|
||||||
|
|
||||||
// Ensure that the database is empty before every test
|
// Ensure that the database is empty before every test
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws TimeoutException {
|
public void tearDown() throws TimeoutException {
|
||||||
// Reset service (if some previous test failed)
|
// 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()) {
|
if (service.isRecording() || service.isPaused()) {
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the database is empty after every test
|
// Ensure that the database is empty after every test
|
||||||
providerUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -187,20 +186,20 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testIntegration_completeRecordingSession() throws TimeoutException {
|
public void testIntegration_completeRecordingSession() throws TimeoutException {
|
||||||
List<Track> tracks = providerUtils.getAllTracks();
|
List<Track> tracks = contentProviderUtils.getAllTracks();
|
||||||
Assert.assertTrue(tracks.isEmpty());
|
Assert.assertTrue(tracks.isEmpty());
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fullRecordingSession() throws TimeoutException {
|
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());
|
Assert.assertFalse(service.isRecording());
|
||||||
|
|
||||||
// Start a track.
|
// Start a track.
|
||||||
long id = service.startNewTrack();
|
long id = service.startNewTrack();
|
||||||
Assert.assertTrue(id >= 0);
|
Assert.assertTrue(id >= 0);
|
||||||
Assert.assertTrue(service.isRecording());
|
Assert.assertTrue(service.isRecording());
|
||||||
Track track = providerUtils.getTrack(id);
|
Track track = contentProviderUtils.getTrack(id);
|
||||||
Assert.assertNotNull(track);
|
Assert.assertNotNull(track);
|
||||||
Assert.assertEquals(id, track.getId());
|
Assert.assertEquals(id, track.getId());
|
||||||
Assert.assertEquals(id, PreferencesUtils.getRecordingTrackId(context));
|
Assert.assertEquals(id, PreferencesUtils.getRecordingTrackId(context));
|
||||||
@@ -219,7 +218,7 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
service.insertTrackPoint(location);
|
service.insertTrackPoint(location);
|
||||||
|
|
||||||
if (i % 7 == 0) {
|
if (i % 7 == 0) {
|
||||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
service.insertWaypoint(null, null, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +226,7 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
Assert.assertFalse(service.isRecording());
|
Assert.assertFalse(service.isRecording());
|
||||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||||
track = providerUtils.getTrack(id);
|
track = contentProviderUtils.getTrack(id);
|
||||||
Assert.assertNotNull(track);
|
Assert.assertNotNull(track);
|
||||||
Assert.assertEquals(id, track.getId());
|
Assert.assertEquals(id, track.getId());
|
||||||
TripStatistics tripStatistics = track.getTripStatistics();
|
TripStatistics tripStatistics = track.getTripStatistics();
|
||||||
|
|||||||
@@ -15,7 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.stats;
|
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}.
|
* Tests for {@link TripStatistics}.
|
||||||
@@ -23,26 +27,17 @@ import junit.framework.TestCase;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class TripStatisticsTest extends TestCase {
|
@RunWith(JUnit4.class)
|
||||||
|
public class TripStatisticsTest {
|
||||||
|
|
||||||
private TripStatistics statistics;
|
private TripStatistics statistics;
|
||||||
|
|
||||||
@Override
|
@Before
|
||||||
protected void setUp() throws Exception {
|
public void setUp() {
|
||||||
super.setUp();
|
|
||||||
|
|
||||||
statistics = new TripStatistics();
|
statistics = new TripStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSetBounds() {
|
@Test
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMerge() {
|
public void testMerge() {
|
||||||
TripStatistics statistics2 = new TripStatistics();
|
TripStatistics statistics2 = new TripStatistics();
|
||||||
statistics.setStartTime(1000L); // Resulting start time
|
statistics.setStartTime(1000L); // Resulting start time
|
||||||
@@ -68,38 +63,32 @@ public class TripStatisticsTest extends TestCase {
|
|||||||
statistics2.setMaxGrade(35.0); // Resulting max grade
|
statistics2.setMaxGrade(35.0); // Resulting max grade
|
||||||
statistics2.setMinGrade(0.0);
|
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);
|
statistics.merge(statistics2);
|
||||||
|
|
||||||
assertEquals(1000L, statistics.getStartTime());
|
Assert.assertEquals(1000L, statistics.getStartTime());
|
||||||
assertEquals(4000L, statistics.getStopTime());
|
Assert.assertEquals(4000L, statistics.getStopTime());
|
||||||
assertEquals(2500L, statistics.getTotalTime());
|
Assert.assertEquals(2500L, statistics.getTotalTime());
|
||||||
assertEquals(1300L, statistics.getMovingTime());
|
Assert.assertEquals(1300L, statistics.getMovingTime());
|
||||||
assertEquals(1100.0, statistics.getTotalDistance());
|
Assert.assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
||||||
assertEquals(900.0, statistics.getTotalElevationGain());
|
Assert.assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
||||||
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed());
|
Assert.assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
|
||||||
assertEquals(-10000, statistics.getLeft());
|
Assert.assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
||||||
assertEquals(30000, statistics.getRight());
|
Assert.assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
|
||||||
assertEquals(35000, statistics.getTop());
|
Assert.assertEquals(-25.0, statistics.getMinGrade(), 0.001);
|
||||||
assertEquals(-40000, statistics.getBottom());
|
Assert.assertEquals(35.0, statistics.getMaxGrade(), 0.001);
|
||||||
assertEquals(1200.0, statistics.getMinElevation());
|
|
||||||
assertEquals(3575.0, statistics.getMaxElevation());
|
|
||||||
assertEquals(-25.0, statistics.getMinGrade());
|
|
||||||
assertEquals(35.0, statistics.getMaxGrade());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetAverageSpeed() {
|
public void testGetAverageSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(1000.0);
|
||||||
statistics.setTotalTime(50000); // in milliseconds
|
statistics.setTotalTime(50000); // in milliseconds
|
||||||
assertEquals(20.0, statistics.getAverageSpeed());
|
Assert.assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testGetAverageMovingSpeed() {
|
public void testGetAverageMovingSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(1000.0);
|
||||||
statistics.setMovingTime(20000); // in milliseconds
|
statistics.setMovingTime(20000); // in milliseconds
|
||||||
assertEquals(50.0, statistics.getAverageMovingSpeed());
|
Assert.assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.util;
|
package de.dennisguse.opentracks.util;
|
||||||
|
|
||||||
import android.os.Environment;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
import junit.framework.TestCase;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -26,83 +27,83 @@ import java.io.File;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class FileUtilsTest extends TestCase {
|
@RunWith(JUnit4.class)
|
||||||
|
public class FileUtilsTest {
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file is new.
|
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file is new.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testBuildUniqueFileName_new() {
|
public void testBuildUniqueFileName_new() {
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
||||||
assertEquals("Filename.ext", filename);
|
Assert.assertEquals("Filename.ext", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testBuildUniqueFileName_exist() {
|
public void testBuildUniqueFileName_exist() {
|
||||||
// Expect "/default.prop" to exist on the phone/emulator
|
// Expect "/default.prop" to exist on the phone/emulator
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
||||||
assertEquals("default(1).prop", filename);
|
Assert.assertEquals("default(1).prop", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
|
* Tests {@link FileUtils#sanitizeFileName(String)} with special characters.
|
||||||
* Verifies that they are sanitized.
|
* Verifies that they are sanitized.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testSanitizeFileName() {
|
public void testSanitizeFileName() {
|
||||||
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
|
String name = "Swim\10ming-^across:/the/ pacific (ocean).";
|
||||||
String expected = "Swim_ming-^across_the_ pacific (ocean).";
|
String expected = "Swim_ming-^across_the_ pacific (ocean).";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanitizeFileNameWithSuffix() {
|
public void testSanitizeFileNameWithSuffix() {
|
||||||
String name = "FileName.jpeg";
|
String name = "FileName.jpeg";
|
||||||
assertEquals(name, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(name, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in Chinese and Russian).
|
* Tests {@link FileUtils#sanitizeFileName(String)} with i18n characters (in Chinese and Russian).
|
||||||
* Verifies that they are allowed.
|
* Verifies that they are allowed.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testSanitizeFileName_i18n() {
|
public void testSanitizeFileName_i18n() {
|
||||||
String name = "您好-привет";
|
String name = "您好-привет";
|
||||||
String expected = "您好-привет";
|
String expected = "您好-привет";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32 characters.
|
* Tests {@link FileUtils#sanitizeFileName(String)} with special FAT32 characters.
|
||||||
* Verifies that they are allowed.
|
* Verifies that they are allowed.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testSanitizeFileName_special_characters() {
|
public void testSanitizeFileName_special_characters() {
|
||||||
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
String name = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
String expected = "$%'-_@~`!(){}^#&+,;=[] ";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped characters in a row.
|
* Tests {@link FileUtils#sanitizeFileName(String)} with multiple escaped characters in a row.
|
||||||
* Verifies that they are collapsed into one underscore.
|
* Verifies that they are collapsed into one underscore.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testSanitizeFileName_collapse() {
|
public void testSanitizeFileName_collapse() {
|
||||||
String name = "hello//there";
|
String name = "hello//there";
|
||||||
String expected = "hello_there";
|
String expected = "hello_there";
|
||||||
assertEquals(expected, FileUtils.sanitizeFileName(name));
|
Assert.assertEquals(expected, FileUtils.sanitizeFileName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FileUtils#truncateFileName(File, String, String)}.
|
* Tests {@link FileUtils#truncateFileName(File, String, String)}.
|
||||||
* Verifies the a long file name is truncated.
|
* Verifies the a long file name is truncated.
|
||||||
*/
|
*/
|
||||||
|
@Test
|
||||||
public void testTruncateFileName() {
|
public void testTruncateFileName() {
|
||||||
File directory = new File("/dir1/dir2/");
|
File directory = new File("/dir1/dir2/");
|
||||||
String suffix = ".gpx";
|
String suffix = ".gpx";
|
||||||
@@ -114,8 +115,8 @@ public class FileUtilsTest extends TestCase {
|
|||||||
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
|
String truncated = FileUtils.truncateFileName(directory, nameString, suffix);
|
||||||
|
|
||||||
for (int i = 0; i < truncated.length(); i++) {
|
for (int i = 0; i < truncated.length(); i++) {
|
||||||
assertEquals('a', truncated.charAt(i));
|
Assert.assertEquals('a', truncated.charAt(i));
|
||||||
}
|
}
|
||||||
assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
|
Assert.assertEquals(FileUtils.MAX_FAT32_PATH_LENGTH, new File(directory, truncated + suffix).getPath().length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,27 +115,16 @@ public class StringUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#getTime(String)}.
|
* Tests {@link StringUtils#parseTime(String)} with fractional seconds.
|
||||||
*/
|
|
||||||
@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.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testGetTime_fractional() {
|
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.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
assertGetTime("2010-05-04T03:02:01.3529Z", 2010, 5, 4, 3, 2, 1, 353);
|
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
|
@Test
|
||||||
public void testGetTime_timezone() {
|
public void testGetTime_timezone() {
|
||||||
@@ -149,7 +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
|
@Test
|
||||||
public void testGetTime_fractionalAndTimezone() {
|
public void testGetTime_fractionalAndTimezone() {
|
||||||
@@ -160,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 xmlDateTime the xml date time string
|
||||||
* @param year the expected year
|
* @param year the expected year
|
||||||
@@ -175,7 +164,10 @@ public class StringUtilsTest {
|
|||||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
calendar.set(year, month - 1, day, hour, minute, second);
|
calendar.set(year, month - 1, day, hour, minute, second);
|
||||||
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
||||||
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.getTime(xmlDateTime));
|
|
||||||
|
// This comparision tends to be flaky (difference of 1ms)
|
||||||
|
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
||||||
|
Assert.assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,22 +16,22 @@ limitations under the License.
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="de.dennisguse.opentracks"
|
package="de.dennisguse.opentracks"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="3195"
|
android:versionCode="3287"
|
||||||
android:versionName="v3.2.10"> <!-- For FDroid. -->
|
android:versionName="v3.4.0"> <!-- For FDroid. -->
|
||||||
<!-- Permission to initialize services -->
|
<!-- Permission to initialize services -->
|
||||||
<application
|
<application
|
||||||
android:name=".Startup"
|
android:name=".Startup"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
android:hasFragileUserData="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:icon="@drawable/ic_logo_color_24dp"
|
android:icon="${applicationLogo}"
|
||||||
android:label="@string/app_name"
|
android:label="${applicationName}"
|
||||||
android:requestLegacyExternalStorage="true"
|
|
||||||
android:theme="@style/ThemeCustom">
|
android:theme="@style/ThemeCustom">
|
||||||
|
|
||||||
<!-- Activities -->
|
<!-- Activities -->
|
||||||
<activity android:name=".AboutActivity" />
|
<activity android:name=".AboutActivity" />
|
||||||
|
|
||||||
<activity android:name=".DeleteActivity" />
|
<activity android:name=".TrackDeleteActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".HelpActivity"
|
android:name=".HelpActivity"
|
||||||
@@ -101,7 +101,7 @@ limitations under the License.
|
|||||||
<activity android:name=".settings.SettingsActivity" />
|
<activity android:name=".settings.SettingsActivity" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".content.ShareContentProvider"
|
android:name=".content.provider.ShareContentProvider"
|
||||||
android:authorities="${applicationId}.content"
|
android:authorities="${applicationId}.content"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:grantUriPermissions="true" />
|
android:grantUriPermissions="true" />
|
||||||
@@ -152,6 +152,4 @@ limitations under the License.
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
if (stopRecording) {
|
if (stopRecording) {
|
||||||
getTrackRecordingServiceConnection().stopRecording(this, false);
|
getTrackRecordingServiceConnection().stopRecording(this, false);
|
||||||
}
|
}
|
||||||
Intent intent = IntentUtils.newIntent(this, DeleteActivity.class);
|
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
|
||||||
intent.putExtra(DeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
||||||
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
abstract protected TrackRecordingServiceConnection getTrackRecordingServiceConnection();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after {@link DeleteActivity} returns its result.
|
* Called after {@link TrackDeleteActivity} returns its result.
|
||||||
*/
|
*/
|
||||||
abstract protected void onDeleted();
|
abstract protected void onDeleted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package de.dennisguse.opentracks;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async Task to delete tracks.
|
* Async Task to delete tracks.
|
||||||
@@ -30,7 +30,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
|
|
||||||
private final long[] trackIds;
|
private final long[] trackIds;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private DeleteActivity deleteActivity;
|
private TrackDeleteActivity deleteActivity;
|
||||||
// true if the AsyncTask has completed
|
// true if the AsyncTask has completed
|
||||||
private boolean 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
|
* @param trackIds the track ids to delete. To delete all, set to size 1 with
|
||||||
* trackIds[0] == -1L
|
* trackIds[0] == -1L
|
||||||
*/
|
*/
|
||||||
public DeleteAsyncTask(DeleteActivity deleteActivity, long[] trackIds) {
|
public DeleteAsyncTask(TrackDeleteActivity deleteActivity, long[] trackIds) {
|
||||||
this.deleteActivity = deleteActivity;
|
this.deleteActivity = deleteActivity;
|
||||||
this.trackIds = trackIds;
|
this.trackIds = trackIds;
|
||||||
context = deleteActivity.getApplicationContext();
|
context = deleteActivity.getApplicationContext();
|
||||||
@@ -53,7 +53,7 @@ public class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
*
|
*
|
||||||
* @param deleteActivity the current activity, can be null
|
* @param deleteActivity the current activity, can be null
|
||||||
*/
|
*/
|
||||||
public void setActivity(DeleteActivity deleteActivity) {
|
public void setActivity(TrackDeleteActivity deleteActivity) {
|
||||||
this.deleteActivity = deleteActivity;
|
this.deleteActivity = deleteActivity;
|
||||||
if (completed && deleteActivity != null) {
|
if (completed && deleteActivity != null) {
|
||||||
deleteActivity.onAsyncTaskCompleted();
|
deleteActivity.onAsyncTaskCompleted();
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ import androidx.viewpager.widget.ViewPager;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
import de.dennisguse.opentracks.fragments.MarkerDetailFragment;
|
import de.dennisguse.opentracks.fragments.MarkerDetailFragment;
|
||||||
|
|
||||||
@@ -71,12 +71,9 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
|
|||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
for (int i = 0; i < cursor.getCount(); i++) {
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
|
Waypoint currentMarker = contentProviderUtils.createWaypoint(cursor);
|
||||||
|
markerIds.add(currentMarker.getId());
|
||||||
if (!currentMarker.isTripStatistics()) {
|
if (currentMarker.getId() == markerId) {
|
||||||
markerIds.add(currentMarker.getId());
|
markerIndex = markerIds.size() - 1;
|
||||||
if (currentMarker.getId() == markerId) {
|
|
||||||
markerIndex = markerIds.size() - 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ import android.widget.AutoCompleteTextView;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,9 +46,6 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
private Waypoint waypoint;
|
private Waypoint waypoint;
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
private View statisticsSection;
|
|
||||||
private EditText statisticsName;
|
|
||||||
private View waypointSection;
|
|
||||||
private EditText waypointName;
|
private EditText waypointName;
|
||||||
private AutoCompleteTextView waypointMarkerType;
|
private AutoCompleteTextView waypointMarkerType;
|
||||||
private EditText waypointDescription;
|
private EditText waypointDescription;
|
||||||
@@ -65,10 +60,6 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||||
|
|
||||||
// Setup UI elements
|
// 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);
|
waypointName = findViewById(R.id.marker_edit_waypoint_name);
|
||||||
waypointMarkerType = findViewById(R.id.marker_edit_waypoint_marker_type);
|
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);
|
||||||
@@ -95,7 +86,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -124,9 +115,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (newMarker) {
|
if (newMarker) {
|
||||||
statisticsSection.setVisibility(View.GONE);
|
int nextWaypointNumber = trackId == -1L ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId);
|
||||||
waypointSection.setVisibility(View.VISIBLE);
|
|
||||||
int nextWaypointNumber = trackId == -1L ? -1 : new ContentProviderUtils(this).getNextWaypointNumber(trackId, WaypointType.WAYPOINT);
|
|
||||||
if (nextWaypointNumber == -1) {
|
if (nextWaypointNumber == -1) {
|
||||||
nextWaypointNumber = 0;
|
nextWaypointNumber = 0;
|
||||||
}
|
}
|
||||||
@@ -141,16 +130,9 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
waypointName.setText(waypoint.getName());
|
||||||
statisticsSection.setVisibility(statistics ? View.VISIBLE : View.GONE);
|
waypointMarkerType.setText(waypoint.getCategory());
|
||||||
waypointSection.setVisibility(statistics ? View.GONE : View.VISIBLE);
|
waypointDescription.setText(waypoint.getDescription());
|
||||||
if (statistics) {
|
|
||||||
statisticsName.setText(waypoint.getName());
|
|
||||||
} else {
|
|
||||||
waypointName.setText(waypoint.getName());
|
|
||||||
waypointMarkerType.setText(waypoint.getCategory());
|
|
||||||
waypointDescription.setText(waypoint.getDescription());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,29 +140,21 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
* Adds a marker.
|
* Adds a marker.
|
||||||
*/
|
*/
|
||||||
private void addMarker() {
|
private void addMarker() {
|
||||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(
|
trackRecordingServiceConnection.addMarker(this,
|
||||||
WaypointType.WAYPOINT,
|
|
||||||
false,
|
|
||||||
waypointName.getText().toString(),
|
waypointName.getText().toString(),
|
||||||
waypointMarkerType.getText().toString(),
|
waypointMarkerType.getText().toString(),
|
||||||
waypointDescription.getText().toString(),
|
waypointDescription.getText().toString(),
|
||||||
null,
|
|
||||||
null);
|
null);
|
||||||
trackRecordingServiceConnection.addMarker(this, waypointCreationRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a marker.
|
* Saves a marker.
|
||||||
*/
|
*/
|
||||||
private void saveMarker() {
|
private void saveMarker() {
|
||||||
boolean statistics = waypoint.getType() == WaypointType.STATISTICS;
|
waypoint.setName(waypointName.getText().toString());
|
||||||
if (statistics) {
|
waypoint.setCategory(waypointMarkerType.getText().toString());
|
||||||
waypoint.setName(statisticsName.getText().toString());
|
waypoint.setDescription(waypointDescription.getText().toString());
|
||||||
} else {
|
|
||||||
waypoint.setName(waypointName.getText().toString());
|
|
||||||
waypoint.setCategory(waypointMarkerType.getText().toString());
|
|
||||||
waypoint.setDescription(waypointDescription.getText().toString());
|
|
||||||
}
|
|
||||||
new ContentProviderUtils(this).updateWaypoint(waypoint);
|
new ContentProviderUtils(this).updateWaypoint(waypoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ import androidx.loader.app.LoaderManager.LoaderCallbacks;
|
|||||||
import androidx.loader.content.CursorLoader;
|
import androidx.loader.content.CursorLoader;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
@@ -70,10 +70,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
|
|
||||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
/*
|
|
||||||
* 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
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
@@ -99,7 +96,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
// UI elements
|
// UI elements
|
||||||
private ListView listView;
|
private ListView listView;
|
||||||
// Callback when an item is selected in the contextual action mode
|
// Callback when an item is selected in the contextual action mode
|
||||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||||
boolean isSingleSelection = ids.length == 1;
|
boolean isSingleSelection = ids.length == 1;
|
||||||
@@ -126,17 +123,14 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
|
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
||||||
|
|
||||||
contentProviderUtils = new ContentProviderUtils(this);
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
|
||||||
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
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 = findViewById(R.id.marker_list);
|
||||||
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
||||||
@@ -181,8 +175,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||||
WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + "!=?",
|
WaypointsColumns.TRACKID + "=?",
|
||||||
new String[]{String.valueOf(track.getId()), String.valueOf(trackFirstWaypointId)}, null);
|
new String[]{String.valueOf(track.getId())}, null);
|
||||||
} else {
|
} else {
|
||||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||||
WaypointsColumns.STARTTIME + " IS NULL", null, null);
|
WaypointsColumns.STARTTIME + " IS NULL", null, null);
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.content.SearchEngine;
|
import de.dennisguse.opentracks.content.SearchEngine;
|
||||||
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
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.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
@@ -104,7 +104,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
|||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
// Callback when an item is selected in the contextual action mode
|
// Callback when an item is selected in the contextual action mode
|
||||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
||||||
@@ -245,7 +245,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
|
|||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
*/
|
*/
|
||||||
public class Startup extends Application {
|
public class Startup extends Application {
|
||||||
|
|
||||||
private final static String TAG = Startup.class.getSimpleName();
|
private static final String TAG = Startup.class.getSimpleName();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
|
|||||||
@@ -1,153 +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.annotation.NonNull;
|
|
||||||
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
|
|
||||||
@NonNull
|
|
||||||
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.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ public class TrackController {
|
|||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed && isRecording && !isPaused) {
|
if (isResumed && isRecording && !isPaused) {
|
||||||
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
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));
|
totalTimeTextView.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
totalTimeTimestamp = System.currentTimeMillis();
|
totalTimeTimestamp = System.currentTimeMillis();
|
||||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND);
|
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ public class TrackController {
|
|||||||
* Gets the total time for the current recording track.
|
* Gets the total time for the current recording track.
|
||||||
*/
|
*/
|
||||||
private long getTotalTime() {
|
private long getTotalTime() {
|
||||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -23,14 +23,13 @@ import android.content.Intent;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.util.DialogUtils;
|
import de.dennisguse.opentracks.util.DialogUtils;
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity for delete tracks.
|
* An activity for delete tracks.
|
||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public class DeleteActivity extends Activity {
|
public class TrackDeleteActivity extends Activity {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||||
|
|
||||||
@@ -31,28 +31,26 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.TabHost;
|
|
||||||
import android.widget.TabHost.TabSpec;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
|
||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.ChartDistanceFragment;
|
import de.dennisguse.opentracks.fragments.ChartFragment;
|
||||||
import de.dennisguse.opentracks.fragments.ChartTimeFragment;
|
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.StatsFragment;
|
import de.dennisguse.opentracks.fragments.StatsFragment;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
@@ -86,7 +84,7 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
private TabHost tabHost;
|
private ViewPager pager;
|
||||||
private TrackController trackController;
|
private TrackController trackController;
|
||||||
|
|
||||||
// From intent
|
// From intent
|
||||||
@@ -106,19 +104,13 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
trackController.update(isRecording(), recordingTrackPaused);
|
trackController.update(isRecording(), recordingTrackPaused);
|
||||||
if (hasPhoto && photoUri != null) {
|
if (hasPhoto && photoUri != null) {
|
||||||
hasPhoto = false;
|
hasPhoto = false;
|
||||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString());
|
long markerId = trackRecordingServiceConnection.addMarker(TrackDetailActivity.this, null, null, null, photoUri.toString());
|
||||||
long markerId = trackRecordingServiceConnection.addMarker(TrackDetailActivity.this, waypointCreationRequest);
|
|
||||||
if (markerId != -1L) {
|
|
||||||
//TODO: Make configurable.
|
|
||||||
FileUtils.updateMediaScanner(TrackDetailActivity.this, photoUri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
@@ -200,30 +192,46 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
||||||
trackDataHub = TrackDataHub.newInstance(this);
|
trackDataHub = new TrackDataHub(this);
|
||||||
|
|
||||||
tabHost = findViewById(R.id.tackdetail_tabhost);
|
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
|
||||||
tabHost.setup();
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
ViewPager viewPager = findViewById(R.id.pager);
|
@NonNull
|
||||||
TabsAdapter tabsAdapter = new TabsAdapter(this, tabHost, viewPager);
|
@Override
|
||||||
|
public Fragment getItem(int position) {
|
||||||
TabSpec statsTabSpec = tabHost.newTabSpec(StatsFragment.STATS_FRAGMENT_TAG).setIndicator(getString(R.string.track_detail_stats_tab));
|
switch (position) {
|
||||||
tabsAdapter.addTab(statsTabSpec, StatsFragment.class, null);
|
case 1:
|
||||||
|
return ChartFragment.newInstance(false);
|
||||||
TabSpec chartTimeTabSpec = tabHost.newTabSpec(ChartTimeFragment.CHART_FRAGMENT_TAG).setIndicator(getString(R.string.settings_chart_by_time));
|
case 2:
|
||||||
tabsAdapter.addTab(chartTimeTabSpec, ChartTimeFragment.class, null);
|
return ChartFragment.newInstance(true);
|
||||||
|
default: //0
|
||||||
TabSpec chartDistanceTabSpec = tabHost.newTabSpec(ChartDistanceFragment.CHART_FRAGMENT_TAG).setIndicator(getString(R.string.settings_chart_by_distance));
|
return new StatsFragment();
|
||||||
tabsAdapter.addTab(chartDistanceTabSpec, ChartDistanceFragment.class, null);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getPageTitle(int position) {
|
||||||
|
switch (position) {
|
||||||
|
case 0:
|
||||||
|
return getString(R.string.track_detail_stats_tab);
|
||||||
|
case 1:
|
||||||
|
return getString(R.string.settings_chart_by_time);
|
||||||
|
case 2:
|
||||||
|
return getString(R.string.settings_chart_by_distance);
|
||||||
|
}
|
||||||
|
return "Unknown Tab";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pager = findViewById(R.id.track_detail_activity_view_pager);
|
||||||
|
pager.setAdapter(adapter);
|
||||||
|
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
|
||||||
|
tabs.setupWithViewPager(pager);
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
tabHost.setCurrentTabByTag(savedInstanceState.getString(CURRENT_TAB_TAG_KEY));
|
pager.setCurrentItem(savedInstanceState.getInt(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
||||||
@@ -291,14 +299,15 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(this);
|
||||||
trackDataHub.stop();
|
trackDataHub.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putString(CURRENT_TAB_TAG_KEY, tabHost.getCurrentTabTag());
|
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
|
||||||
|
|
||||||
if (photoUri != null) {
|
if (photoUri != null) {
|
||||||
outState.putParcelable(PHOTO_URI_KEY, photoUri);
|
outState.putParcelable(PHOTO_URI_KEY, photoUri);
|
||||||
}
|
}
|
||||||
@@ -364,10 +373,6 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
return true;
|
return true;
|
||||||
case R.id.track_detail_insert_photo:
|
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();
|
createWaypointWithPicture();
|
||||||
return true;
|
return true;
|
||||||
case R.id.track_detail_menu_show_on_map:
|
case R.id.track_detail_menu_show_on_map:
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ import android.widget.Spinner;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
@@ -175,7 +175,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -50,11 +50,11 @@ import androidx.loader.content.Loader;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.content.TracksColumns;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
@@ -127,10 +127,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that sharedPreferenceChangeListener cannot be an anonymous inner class.
|
|
||||||
* Anonymous inner class will get garbage collected.
|
|
||||||
*/
|
|
||||||
private final OnSharedPreferenceChangeListener
|
private final OnSharedPreferenceChangeListener
|
||||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -141,7 +137,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_id_key, key)) {
|
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_id_key, key)) {
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackListActivity.this);
|
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackListActivity.this);
|
||||||
if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
|
if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
|
||||||
trackRecordingServiceConnection.startAndBind();
|
trackRecordingServiceConnection.startAndBind(TrackListActivity.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_paused_key, key)) {
|
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_paused_key, key)) {
|
||||||
@@ -196,7 +192,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ITrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.d(TAG, "service not available to start gps or a new recording");
|
Log.d(TAG, "service not available to start gps or a new recording");
|
||||||
return;
|
return;
|
||||||
@@ -344,7 +340,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
super.onStop();
|
super.onStop();
|
||||||
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -406,14 +402,14 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
// Invoke trackRecordingService
|
// Invoke trackRecordingService
|
||||||
if (startGps) {
|
if (startGps) {
|
||||||
trackRecordingServiceConnection.startAndBind();
|
trackRecordingServiceConnection.startAndBind(this);
|
||||||
bindChangedCallback.run();
|
bindChangedCallback.run();
|
||||||
} else {
|
} else {
|
||||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
trackRecordingService.stopGps();
|
trackRecordingService.stopGps();
|
||||||
}
|
}
|
||||||
trackRecordingServiceConnection.unbindAndStop();
|
trackRecordingServiceConnection.unbindAndStop(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update menu after starting or stopping gps
|
// Update menu after starting or stopping gps
|
||||||
@@ -498,7 +494,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
*/
|
*/
|
||||||
private void startRecording() {
|
private void startRecording() {
|
||||||
startNewRecording = true;
|
startNewRecording = true;
|
||||||
trackRecordingServiceConnection.startAndBind();
|
trackRecordingServiceConnection.startAndBind(this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the binding has happened, then invoke the callback to start a new recording.
|
* If the binding has happened, then invoke the callback to start a new recording.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import androidx.annotation.Nullable;
|
|||||||
*/
|
*/
|
||||||
public class ContentResolverWrapper implements IContentResolver {
|
public class ContentResolverWrapper implements IContentResolver {
|
||||||
|
|
||||||
private ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
|
|
||||||
public ContentResolverWrapper(ContentResolver contentResolver) {
|
public ContentResolverWrapper(ContentResolver contentResolver) {
|
||||||
this.contentResolver = contentResolver;
|
this.contentResolver = contentResolver;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.MarkerDetailActivity;
|
import de.dennisguse.opentracks.MarkerDetailActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.stats.ExtremityMonitor;
|
import de.dennisguse.opentracks.stats.ExtremityMonitor;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||||
@@ -122,7 +122,7 @@ public class ChartView extends View {
|
|||||||
private boolean reportSpeed = true;
|
private boolean reportSpeed = true;
|
||||||
private boolean showPointer = false;
|
private boolean showPointer = false;
|
||||||
|
|
||||||
private GestureDetectorCompat detectorScrollFlingTab = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
|
private final GestureDetectorCompat detectorScrollFlingTab = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onDown(MotionEvent e) {
|
public boolean onDown(MotionEvent e) {
|
||||||
@@ -176,7 +176,7 @@ public class ChartView extends View {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private ScaleGestureDetector detectorZoom = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
private final ScaleGestureDetector detectorZoom = new ScaleGestureDetector(getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onScale(ScaleGestureDetector detector) {
|
public boolean onScale(ScaleGestureDetector detector) {
|
||||||
@@ -555,7 +555,7 @@ public class ChartView extends View {
|
|||||||
canvas.save();
|
canvas.save();
|
||||||
float x = getX(getWaypointXValue(waypoint));
|
float x = getX(getWaypointXValue(waypoint));
|
||||||
canvas.drawLine(x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
|
canvas.drawLine(x, topBorder + spacer + markerHeight / 2, x, topBorder + effectiveHeight, markerPaint);
|
||||||
canvas.translate(x - (float) (markerWidth * WAYPOINT_X_ANCHOR), topBorder + spacer);
|
canvas.translate(x - (markerWidth * WAYPOINT_X_ANCHOR), topBorder + spacer);
|
||||||
|
|
||||||
waypointMarker.draw(canvas);
|
waypointMarker.draw(canvas);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|||||||
@@ -18,13 +18,9 @@ package de.dennisguse.opentracks.content;
|
|||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data source on the phone.
|
* Data source on the phone.
|
||||||
*
|
*
|
||||||
@@ -33,47 +29,16 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
class DataSource {
|
class DataSource {
|
||||||
|
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
private final SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
DataSource(Context context) {
|
DataSource(Context context) {
|
||||||
contentResolver = context.getContentResolver();
|
contentResolver = context.getContentResolver();
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a content observer.
|
|
||||||
*
|
|
||||||
* @param uri the uri
|
|
||||||
* @param observer the observer
|
|
||||||
*/
|
|
||||||
void registerContentObserver(Uri uri, ContentObserver observer) {
|
void registerContentObserver(Uri uri, ContentObserver observer) {
|
||||||
contentResolver.registerContentObserver(uri, false, observer);
|
contentResolver.registerContentObserver(uri, false, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a content observer.
|
|
||||||
*
|
|
||||||
* @param observer the observer
|
|
||||||
*/
|
|
||||||
void unregisterContentObserver(ContentObserver observer) {
|
void unregisterContentObserver(ContentObserver observer) {
|
||||||
contentResolver.unregisterContentObserver(observer);
|
contentResolver.unregisterContentObserver(observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a shared preference change listener.
|
|
||||||
*
|
|
||||||
* @param listener the listener
|
|
||||||
*/
|
|
||||||
void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters a shared preference change listener.
|
|
||||||
*
|
|
||||||
* @param listener the listener
|
|
||||||
*/
|
|
||||||
void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -38,11 +38,4 @@ public interface DataSourceListener {
|
|||||||
* Notifies when the track points table is updated.
|
* Notifies when the track points table is updated.
|
||||||
*/
|
*/
|
||||||
void notifyTrackPointsTableUpdated();
|
void notifyTrackPointsTableUpdated();
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies when a preference changes.
|
|
||||||
*
|
|
||||||
* @param key the preference key
|
|
||||||
*/
|
|
||||||
void notifyPreferenceChanged(String key);
|
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -25,6 +23,10 @@ import android.util.Log;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates observers/listeners and manages their registration with {@link DataSource}.
|
* Creates observers/listeners and manages their registration with {@link DataSource}.
|
||||||
* The observers/listeners calls {@link DataSourceListener} when data changes.
|
* The observers/listeners calls {@link DataSourceListener} when data changes.
|
||||||
@@ -45,7 +47,6 @@ class DataSourceManager {
|
|||||||
private final TracksTableObserver tracksTableObserver;
|
private final TracksTableObserver tracksTableObserver;
|
||||||
private final WaypointsTableObserver waypointsTableObserver;
|
private final WaypointsTableObserver waypointsTableObserver;
|
||||||
private final TrackPointsTableObserver trackPointsTableObserver;
|
private final TrackPointsTableObserver trackPointsTableObserver;
|
||||||
private final PreferenceListener preferenceListener;
|
|
||||||
|
|
||||||
DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
|
DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
@@ -55,7 +56,6 @@ class DataSourceManager {
|
|||||||
tracksTableObserver = new TracksTableObserver();
|
tracksTableObserver = new TracksTableObserver();
|
||||||
waypointsTableObserver = new WaypointsTableObserver();
|
waypointsTableObserver = new WaypointsTableObserver();
|
||||||
trackPointsTableObserver = new TrackPointsTableObserver();
|
trackPointsTableObserver = new TrackPointsTableObserver();
|
||||||
preferenceListener = new PreferenceListener();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,10 +66,7 @@ class DataSourceManager {
|
|||||||
void updateListeners(EnumSet<TrackDataType> listeners) {
|
void updateListeners(EnumSet<TrackDataType> listeners) {
|
||||||
EnumSet<TrackDataType> neededListeners = EnumSet.copyOf(listeners);
|
EnumSet<TrackDataType> neededListeners = EnumSet.copyOf(listeners);
|
||||||
|
|
||||||
/*
|
// Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to the same internal listener
|
||||||
* Map SAMPLED_OUT_POINT_UPDATES to POINT_UPDATES since they correspond to
|
|
||||||
* the same internal listener
|
|
||||||
*/
|
|
||||||
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
|
if (neededListeners.contains(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE)) {
|
||||||
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
|
neededListeners.remove(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE);
|
||||||
neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
|
neededListeners.add(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE);
|
||||||
@@ -114,15 +111,11 @@ class DataSourceManager {
|
|||||||
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
||||||
break;
|
break;
|
||||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||||
dataSource.registerContentObserver(
|
dataSource.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, trackPointsTableObserver);
|
||||||
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
|
|
||||||
break;
|
break;
|
||||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||||
break;
|
break;
|
||||||
case PREFERENCE:
|
|
||||||
dataSource.registerOnSharedPreferenceChangeListener(preferenceListener);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -147,9 +140,6 @@ class DataSourceManager {
|
|||||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||||
break;
|
break;
|
||||||
case PREFERENCE:
|
|
||||||
dataSource.unregisterOnSharedPreferenceChangeListener(preferenceListener);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -214,17 +204,4 @@ class DataSourceManager {
|
|||||||
dataSourceListener.notifyTrackPointsTableUpdated();
|
dataSourceListener.notifyTrackPointsTableUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for preference changes.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
private class PreferenceListener implements OnSharedPreferenceChangeListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
||||||
dataSourceListener.notifyPreferenceChanged(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010 Google Inc.
|
* Copyright 2012 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
* 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
|
* use this file except in compliance with the License. You may obtain a copy of
|
||||||
@@ -13,16 +13,37 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for an object that can generate descriptions of track and waypoint.
|
* Generates descriptions for tracks and waypoints.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
public interface DescriptionGenerator {
|
public class DescriptionGenerator {
|
||||||
|
|
||||||
|
private static final String HTML_LINE_BREAK = "<br>";
|
||||||
|
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
||||||
|
private static final String TEXT_LINE_BREAK = "\n";
|
||||||
|
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public DescriptionGenerator(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a track description.
|
* Generates a track description.
|
||||||
@@ -30,12 +51,196 @@ public interface DescriptionGenerator {
|
|||||||
* @param track the track
|
* @param track the track
|
||||||
* @param html true to output html, false to output plain text
|
* @param html true to output html, false to output plain text
|
||||||
*/
|
*/
|
||||||
String generateTrackDescription(Track track, boolean html);
|
public String generateTrackDescription(Track track, boolean html) {
|
||||||
|
String paragraphSeparator = html ? HTML_PARAGRAPH_SEPARATOR : TEXT_PARAGRAPH_SEPARATOR;
|
||||||
|
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
// Created by
|
||||||
|
String creator = html
|
||||||
|
? "<a href='" + context.getString(R.string.app_web_url) + "'>" + context.getString(R.string.app_name) + "</a>"
|
||||||
|
: context.getString(R.string.app_name);
|
||||||
|
builder.append(creator);
|
||||||
|
|
||||||
|
builder.append(paragraphSeparator);
|
||||||
|
|
||||||
|
writeString(track.getName(), builder, R.string.generic_name_line, lineBreak);
|
||||||
|
writeString(track.getCategory(), builder, R.string.description_activity_type, lineBreak);
|
||||||
|
writeString(track.getDescription(), builder, R.string.generic_description_line, lineBreak);
|
||||||
|
builder.append(generateTripStatisticsDescription(track.getTripStatistics(), html));
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a waypoint description from a trip statistics.
|
* Writes a string to a string builder.
|
||||||
*
|
*
|
||||||
* @param tripStatistics the trip statistics
|
* @param text the string
|
||||||
|
* @param builder the string builder
|
||||||
|
* @param resId the resource id containing one string placeholder
|
||||||
|
* @param lineBreak the line break
|
||||||
*/
|
*/
|
||||||
String generateWaypointDescription(TripStatistics tripStatistics);
|
private void writeString(String text, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
if (text == null || text.length() == 0) {
|
||||||
|
text = context.getString(R.string.value_unknown);
|
||||||
|
}
|
||||||
|
builder.append(context.getString(resId, text));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a description for a {@link TripStatistics}.
|
||||||
|
*
|
||||||
|
* @param stats the trip statistics
|
||||||
|
* @param html true to use "<br>" for line break instead of "\n"
|
||||||
|
*/
|
||||||
|
private String generateTripStatisticsDescription(TripStatistics stats, boolean html) {
|
||||||
|
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
// Total distance
|
||||||
|
writeDistance(stats.getTotalDistance(), builder, R.string.description_total_distance, lineBreak);
|
||||||
|
|
||||||
|
// Total time
|
||||||
|
writeTime(stats.getTotalTime(), builder, R.string.description_total_time, lineBreak);
|
||||||
|
|
||||||
|
// Moving time
|
||||||
|
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak);
|
||||||
|
|
||||||
|
// Average speed
|
||||||
|
writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
|
||||||
|
|
||||||
|
// Average moving speed
|
||||||
|
writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed, lineBreak);
|
||||||
|
|
||||||
|
// Max speed
|
||||||
|
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
|
||||||
|
|
||||||
|
// Average pace
|
||||||
|
writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Average moving pace
|
||||||
|
writePace(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Fastest pace
|
||||||
|
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
|
// Max elevation
|
||||||
|
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
|
||||||
|
|
||||||
|
// Min elevation
|
||||||
|
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
|
||||||
|
|
||||||
|
// Elevation gain
|
||||||
|
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
||||||
|
|
||||||
|
// Max grade
|
||||||
|
writeGrade(stats.getMaxGrade(), builder, R.string.description_max_grade, lineBreak);
|
||||||
|
|
||||||
|
// Min grade
|
||||||
|
writeGrade(stats.getMinGrade(), builder, R.string.description_min_grade, lineBreak);
|
||||||
|
|
||||||
|
// Recorded time
|
||||||
|
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime())));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes distance.
|
||||||
|
*
|
||||||
|
* @param distance distance in meters
|
||||||
|
* @param builder StringBuilder to append distance
|
||||||
|
* @param resId resource id of distance string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeDistance(double distance, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
double distanceInKm = distance * UnitConversions.M_TO_KM;
|
||||||
|
double distanceInMi = distanceInKm * UnitConversions.KM_TO_MI;
|
||||||
|
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes time.
|
||||||
|
*
|
||||||
|
* @param time time in milliseconds.
|
||||||
|
* @param builder StringBuilder to append time
|
||||||
|
* @param resId resource id of time string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeTime(long time, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
builder.append(context.getString(resId, StringUtils.formatElapsedTime(time)));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes speed.
|
||||||
|
*
|
||||||
|
* @param speed speed in meters per second
|
||||||
|
* @param builder StringBuilder to append speed
|
||||||
|
* @param resId resource id of speed string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeSpeed(double speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
|
||||||
|
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
|
||||||
|
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes pace.
|
||||||
|
*
|
||||||
|
* @param speed speed in meters per second
|
||||||
|
* @param builder StringBuilder to append pace
|
||||||
|
* @param resId resource id of pace string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
Pair<String, String> paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
||||||
|
Pair<String, String> paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
||||||
|
|
||||||
|
String formattedPaceMetrics = paceInMetrics.first != null ? paceInMetrics.first : context.getString(R.string.value_unknown);
|
||||||
|
String formattedPaceImperial = paceInImperial.first != null ? paceInImperial.first : context.getString(R.string.value_unknown);
|
||||||
|
|
||||||
|
builder.append(context.getString(resId, formattedPaceMetrics, formattedPaceImperial));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes elevation.
|
||||||
|
*
|
||||||
|
* @param elevation elevation in meters
|
||||||
|
* @param builder StringBuilder to append elevation
|
||||||
|
* @param resId resource id of elevation string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeElevation(double elevation, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
long elevationInM = Math.round(elevation);
|
||||||
|
long elevationInFt = Math.round(elevation * UnitConversions.M_TO_FT);
|
||||||
|
builder.append(context.getString(resId, elevationInM, elevationInFt));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes grade.
|
||||||
|
*
|
||||||
|
* @param grade grade in fraction
|
||||||
|
* @param builder StringBuilder to append grade
|
||||||
|
* @param resId resource id grade string
|
||||||
|
* @param lineBreak line break string
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
void writeGrade(double grade, StringBuilder builder, int resId, String lineBreak) {
|
||||||
|
long gradeInPercent = Double.isNaN(grade) || Double.isInfinite(grade) ? 0L : Math.round(grade * 100);
|
||||||
|
builder.append(context.getString(resId, gradeInPercent));
|
||||||
|
builder.append(lineBreak);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class DescriptionGeneratorImpl implements DescriptionGenerator {
|
|
||||||
|
|
||||||
private static final String HTML_LINE_BREAK = "<br>";
|
|
||||||
private static final String HTML_PARAGRAPH_SEPARATOR = "<p>";
|
|
||||||
private static final String TEXT_LINE_BREAK = "\n";
|
|
||||||
private static final String TEXT_PARAGRAPH_SEPARATOR = "\n\n";
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public DescriptionGeneratorImpl(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateTrackDescription(Track track, boolean html) {
|
|
||||||
String paragraphSeparator = html ? HTML_PARAGRAPH_SEPARATOR : TEXT_PARAGRAPH_SEPARATOR;
|
|
||||||
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
// Created by
|
|
||||||
String creator = html
|
|
||||||
? "<a href='" + context.getString(R.string.app_web_url) + "'>" + context.getString(R.string.app_name) + "</a>"
|
|
||||||
: context.getString(R.string.app_name);
|
|
||||||
builder.append(creator);
|
|
||||||
|
|
||||||
builder.append(paragraphSeparator);
|
|
||||||
|
|
||||||
writeString(track.getName(), builder, R.string.generic_name_line, lineBreak);
|
|
||||||
writeString(track.getCategory(), builder, R.string.description_activity_type, lineBreak);
|
|
||||||
writeString(track.getDescription(), builder, R.string.generic_description_line, lineBreak);
|
|
||||||
builder.append(generateTripStatisticsDescription(track.getTripStatistics(), html));
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String generateWaypointDescription(TripStatistics tripStatistics) {
|
|
||||||
return generateTripStatisticsDescription(tripStatistics, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes a string to a string builder.
|
|
||||||
*
|
|
||||||
* @param text the string
|
|
||||||
* @param builder the string builder
|
|
||||||
* @param resId the resource id containing one string placeholder
|
|
||||||
* @param lineBreak the line break
|
|
||||||
*/
|
|
||||||
private void writeString(String text, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
if (text == null || text.length() == 0) {
|
|
||||||
text = context.getString(R.string.value_unknown);
|
|
||||||
}
|
|
||||||
builder.append(context.getString(resId, text));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a description for a {@link TripStatistics}.
|
|
||||||
*
|
|
||||||
* @param stats the trip statistics
|
|
||||||
* @param html true to use "<br>" for line break instead of "\n"
|
|
||||||
*/
|
|
||||||
private String generateTripStatisticsDescription(TripStatistics stats, boolean html) {
|
|
||||||
String lineBreak = html ? HTML_LINE_BREAK : TEXT_LINE_BREAK;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
// Total distance
|
|
||||||
writeDistance(stats.getTotalDistance(), builder, R.string.description_total_distance, lineBreak);
|
|
||||||
|
|
||||||
// Total time
|
|
||||||
writeTime(stats.getTotalTime(), builder, R.string.description_total_time, lineBreak);
|
|
||||||
|
|
||||||
// Moving time
|
|
||||||
writeTime(stats.getMovingTime(), builder, R.string.description_moving_time, lineBreak);
|
|
||||||
|
|
||||||
// Average speed
|
|
||||||
writeSpeed(stats.getAverageSpeed(), builder, R.string.description_average_speed, lineBreak);
|
|
||||||
|
|
||||||
// Average moving speed
|
|
||||||
writeSpeed(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_speed, lineBreak);
|
|
||||||
|
|
||||||
// Max speed
|
|
||||||
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
|
|
||||||
|
|
||||||
// Average pace
|
|
||||||
writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Average moving pace
|
|
||||||
writePace(stats.getAverageMovingSpeed(), builder, R.string.description_average_moving_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Fastest pace
|
|
||||||
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
|
||||||
|
|
||||||
// Max elevation
|
|
||||||
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
|
|
||||||
|
|
||||||
// Min elevation
|
|
||||||
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
|
|
||||||
|
|
||||||
// Elevation gain
|
|
||||||
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
|
||||||
|
|
||||||
// Max grade
|
|
||||||
writeGrade(stats.getMaxGrade(), builder, R.string.description_max_grade, lineBreak);
|
|
||||||
|
|
||||||
// Min grade
|
|
||||||
writeGrade(stats.getMinGrade(), builder, R.string.description_min_grade, lineBreak);
|
|
||||||
|
|
||||||
// Recorded time
|
|
||||||
builder.append(context.getString(R.string.description_recorded_time, StringUtils.formatDateTime(context, stats.getStartTime())));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes distance.
|
|
||||||
*
|
|
||||||
* @param distance distance in meters
|
|
||||||
* @param builder StringBuilder to append distance
|
|
||||||
* @param resId resource id of distance string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeDistance(double distance, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
double distanceInKm = distance * UnitConversions.M_TO_KM;
|
|
||||||
double distanceInMi = distanceInKm * UnitConversions.KM_TO_MI;
|
|
||||||
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes time.
|
|
||||||
*
|
|
||||||
* @param time time in milliseconds.
|
|
||||||
* @param builder StringBuilder to append time
|
|
||||||
* @param resId resource id of time string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeTime(long time, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
builder.append(context.getString(resId, StringUtils.formatElapsedTime(time)));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes speed.
|
|
||||||
*
|
|
||||||
* @param speed speed in meters per second
|
|
||||||
* @param builder StringBuilder to append speed
|
|
||||||
* @param resId resource id of speed string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeSpeed(double speed, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
double speedInKmHr = speed * UnitConversions.MS_TO_KMH;
|
|
||||||
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
|
|
||||||
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes pace.
|
|
||||||
*
|
|
||||||
* @param speed speed in meters per second
|
|
||||||
* @param builder StringBuilder to append pace
|
|
||||||
* @param resId resource id of pace string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
Pair<String, String> paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
|
||||||
Pair<String, String> paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
|
||||||
|
|
||||||
String formattedPaceMetrics = paceInMetrics.first != null ? paceInMetrics.first : context.getString(R.string.value_unknown);
|
|
||||||
String formattedPaceImperial = paceInImperial.first != null ? paceInImperial.first : context.getString(R.string.value_unknown);
|
|
||||||
|
|
||||||
builder.append(context.getString(resId, formattedPaceMetrics, formattedPaceImperial));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes elevation.
|
|
||||||
*
|
|
||||||
* @param elevation elevation in meters
|
|
||||||
* @param builder StringBuilder to append elevation
|
|
||||||
* @param resId resource id of elevation string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeElevation(double elevation, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
long elevationInM = Math.round(elevation);
|
|
||||||
long elevationInFt = Math.round(elevation * UnitConversions.M_TO_FT);
|
|
||||||
builder.append(context.getString(resId, elevationInM, elevationInFt));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes grade.
|
|
||||||
*
|
|
||||||
* @param grade grade in fraction
|
|
||||||
* @param builder StringBuilder to append grade
|
|
||||||
* @param resId resource id grade string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void writeGrade(double grade, StringBuilder builder, int resId, String lineBreak) {
|
|
||||||
long gradeInPercent = Double.isNaN(grade) || Double.isInfinite(grade) ? 0L : Math.round(grade * 100);
|
|
||||||
builder.append(context.getString(resId, gradeInPercent));
|
|
||||||
builder.append(lineBreak);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.content;
|
|
||||||
|
|
||||||
import android.location.Location;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link SensorDataSetLocation}.
|
|
||||||
* An implementation can create new instances or reuse existing instances for optimization.
|
|
||||||
*/
|
|
||||||
public class LocationFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The default {@link LocationFactory} which creates a location each time.
|
|
||||||
*/
|
|
||||||
public static LocationFactory DEFAULT_LOCATION_FACTORY = new LocationFactory();
|
|
||||||
|
|
||||||
public Location createLocation() {
|
|
||||||
return new SensorDataSetLocation(LocationManager.GPS_PROVIDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.content;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.location.Location;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A lightweight wrapper around the original {@link Cursor} with a method to clean up.
|
|
||||||
*/
|
|
||||||
public interface LocationIterator extends Iterator<Location>, AutoCloseable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the most recently retrieved track point id by {@link #next()}.
|
|
||||||
*/
|
|
||||||
long getLocationId();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the iterator.
|
|
||||||
*/
|
|
||||||
void close();
|
|
||||||
}
|
|
||||||
@@ -28,6 +28,11 @@ import java.util.Locale;
|
|||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
@@ -101,7 +106,7 @@ public class SearchEngine {
|
|||||||
public static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
|
public static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comparador for scored results.
|
* Comparator for scored results.
|
||||||
*/
|
*/
|
||||||
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = new Comparator<ScoredResult>() {
|
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = new Comparator<ScoredResult>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -243,7 +248,6 @@ public class SearchEngine {
|
|||||||
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
|
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
|
||||||
|
|
||||||
TripStatistics statistics = track.getTripStatistics();
|
TripStatistics statistics = track.getTripStatistics();
|
||||||
score *= getDistanceBoost(query, statistics.getMeanLatitude(), statistics.getMeanLongitude());
|
|
||||||
|
|
||||||
long meanTimestamp = (statistics.getStartTime() + statistics.getStopTime()) / 2L;
|
long meanTimestamp = (statistics.getStartTime() + statistics.getStopTime()) / 2L;
|
||||||
score *= getTimeBoost(query, meanTimestamp);
|
score *= getTimeBoost(query, meanTimestamp);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -30,6 +31,11 @@ import java.util.EnumSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
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.TrackPointFactory;
|
||||||
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
@@ -38,11 +44,7 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class TrackDataHub implements DataSourceListener {
|
public class TrackDataHub implements DataSourceListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
public synchronized static TrackDataHub newInstance(Context context) {
|
|
||||||
return new TrackDataHub(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACK_POINTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target number of track points displayed by the map overlay.
|
* Target number of track points displayed by the map overlay.
|
||||||
@@ -75,24 +77,16 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
private long selectedTrackId;
|
private long selectedTrackId;
|
||||||
private long recordingTrackId;
|
private long recordingTrackId;
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
private boolean metricUnits;
|
|
||||||
private boolean reportSpeed;
|
|
||||||
private int recordingGpsAccuracy;
|
|
||||||
private int recordingDistanceInterval;
|
|
||||||
|
|
||||||
// Track points sampling state
|
// Track points sampling state
|
||||||
private int numLoadedPoints;
|
private int numLoadedPoints;
|
||||||
private long firstSeenLocationId;
|
private long firstSeenLocationId;
|
||||||
private long lastSeenLocationId;
|
private long lastSeenLocationId;
|
||||||
|
|
||||||
/**
|
public TrackDataHub(Context context) {
|
||||||
* Constructor.
|
this(context, new TrackDataManager(), new ContentProviderUtils(context), TARGET_DISPLAYED_TRACK_POINTS);
|
||||||
*
|
}
|
||||||
* @param context the context
|
|
||||||
* @param trackDataManager the track data manager
|
|
||||||
* @param contentProviderUtils the content tracks provider utils
|
|
||||||
* @param targetNumPoints the target number of points
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
private TrackDataHub(Context context, TrackDataManager trackDataManager, ContentProviderUtils contentProviderUtils, int targetNumPoints) {
|
private TrackDataHub(Context context, TrackDataManager trackDataManager, ContentProviderUtils contentProviderUtils, int targetNumPoints) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -114,7 +108,8 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
dataSource = newDataSource();
|
dataSource = newDataSource();
|
||||||
dataSourceManager = new DataSourceManager(dataSource, this);
|
dataSourceManager = new DataSourceManager(dataSource, this);
|
||||||
|
|
||||||
notifyPreferenceChanged(null);
|
PreferencesUtils.register(context, this);
|
||||||
|
onSharedPreferenceChanged(null, null);
|
||||||
runInHandlerThread(new Runnable() {
|
runInHandlerThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -131,6 +126,9 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
Log.i(TAG, "TrackDataHub not started, ignoring stop.");
|
Log.i(TAG, "TrackDataHub not started, ignoring stop.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreferencesUtils.unregister(context, this);
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
dataSourceManager.unregisterAllListeners();
|
dataSourceManager.unregisterAllListeners();
|
||||||
@@ -251,7 +249,7 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void notifyPreferenceChanged(final String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
|
||||||
runInHandlerThread(new Runnable() {
|
runInHandlerThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -261,46 +259,6 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(context);
|
|
||||||
if (key != null) {
|
|
||||||
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
|
||||||
if (trackDataListener.onMetricUnitsChanged(metricUnits)) {
|
|
||||||
loadDataForListener(trackDataListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.stats_rate_key, key)) {
|
|
||||||
reportSpeed = PreferencesUtils.isReportSpeed(context);
|
|
||||||
if (key != null) {
|
|
||||||
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
|
||||||
if (trackDataListener.onReportSpeedChanged(reportSpeed)) {
|
|
||||||
loadDataForListener(trackDataListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_gps_accuracy_key, key)) {
|
|
||||||
recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
|
||||||
if (key != null) {
|
|
||||||
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
|
||||||
if (trackDataListener.onRecordingGpsAccuracy(recordingGpsAccuracy)) {
|
|
||||||
loadDataForListener(trackDataListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
|
||||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
|
||||||
if (key != null) {
|
|
||||||
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
|
||||||
if (trackDataListener.onRecordingDistanceIntervalChanged(recordingDistanceInterval)) {
|
|
||||||
loadDataForListener(trackDataListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -314,13 +272,6 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
|
|
||||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
|
||||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
|
||||||
trackDataListener.onRecordingGpsAccuracy(recordingGpsAccuracy);
|
|
||||||
trackDataListener.onRecordingDistanceIntervalChanged(recordingDistanceInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyTracksTableUpdate(trackDataManager.getListeners(TrackDataType.TRACKS_TABLE));
|
notifyTracksTableUpdate(trackDataManager.getListeners(TrackDataType.TRACKS_TABLE));
|
||||||
|
|
||||||
for (TrackDataListener listener : trackDataManager.getListeners(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE)) {
|
for (TrackDataListener listener : trackDataManager.getListeners(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE)) {
|
||||||
@@ -341,13 +292,6 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
Set<TrackDataListener> trackDataListeners = Collections.singleton(trackDataListener);
|
Set<TrackDataListener> trackDataListeners = Collections.singleton(trackDataListener);
|
||||||
EnumSet<TrackDataType> trackDataTypes = trackDataManager.getTrackDataTypes(trackDataListener);
|
EnumSet<TrackDataType> trackDataTypes = trackDataManager.getTrackDataTypes(trackDataListener);
|
||||||
|
|
||||||
if (trackDataTypes.contains(TrackDataType.PREFERENCE)) {
|
|
||||||
trackDataListener.onMetricUnitsChanged(metricUnits);
|
|
||||||
trackDataListener.onReportSpeedChanged(reportSpeed);
|
|
||||||
trackDataListener.onRecordingGpsAccuracy(recordingGpsAccuracy);
|
|
||||||
trackDataListener.onRecordingDistanceIntervalChanged(recordingDistanceInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackDataTypes.contains(TrackDataType.TRACKS_TABLE)) {
|
if (trackDataTypes.contains(TrackDataType.TRACKS_TABLE)) {
|
||||||
notifyTracksTableUpdate(trackDataListeners);
|
notifyTracksTableUpdate(trackDataListeners);
|
||||||
}
|
}
|
||||||
@@ -448,11 +392,11 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
int samplingFrequency = -1;
|
int samplingFrequency = -1;
|
||||||
boolean includeNextPoint = false;
|
boolean includeNextPoint = false;
|
||||||
|
|
||||||
try (LocationIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, localLastSeenLocationId + 1, false, LocationFactory.DEFAULT_LOCATION_FACTORY)) {
|
try (TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, localLastSeenLocationId + 1, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY)) {
|
||||||
|
|
||||||
while (locationIterator.hasNext()) {
|
while (locationIterator.hasNext()) {
|
||||||
Location location = locationIterator.next();
|
Location location = locationIterator.next();
|
||||||
long locationId = locationIterator.getLocationId();
|
long locationId = locationIterator.getTrackPointId();
|
||||||
|
|
||||||
// Stop if past the last wanted point
|
// Stop if past the last wanted point
|
||||||
if (maxPointId != -1L && locationId > maxPointId) {
|
if (maxPointId != -1L && locationId > maxPointId) {
|
||||||
@@ -540,32 +484,4 @@ public class TrackDataHub implements DataSourceListener {
|
|||||||
public long getSelectedTrackId() {
|
public long getSelectedTrackId() {
|
||||||
return selectedTrackId;
|
return selectedTrackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the recordingGpsAccuracy.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
int getRecordingGpsAccuracy() {
|
|
||||||
return recordingGpsAccuracy;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the metricUnits.
|
|
||||||
*
|
|
||||||
* @return the metricUnits
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isMetricUnits() {
|
|
||||||
return metricUnits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the reportSpeed.
|
|
||||||
*
|
|
||||||
* @return the reportSpeed
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isReportSpeed() {
|
|
||||||
return reportSpeed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,9 @@ package de.dennisguse.opentracks.content;
|
|||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for track data changes.
|
* Listener for track data changes.
|
||||||
*
|
*
|
||||||
@@ -74,36 +77,4 @@ public interface TrackDataListener {
|
|||||||
* This gets called after every batch of calls to {@link #clearWaypoints()} and {@link #onNewWaypoint(Waypoint)}.
|
* This gets called after every batch of calls to {@link #clearWaypoints()} and {@link #onNewWaypoint(Waypoint)}.
|
||||||
*/
|
*/
|
||||||
void onNewWaypointsDone();
|
void onNewWaypointsDone();
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the metric units preference value is change.
|
|
||||||
*
|
|
||||||
* @param metricUnits true to use metric units, false to use imperial units
|
|
||||||
* @return true to reload all the data, false otherwise.
|
|
||||||
*/
|
|
||||||
boolean onMetricUnitsChanged(boolean metricUnits);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the report speed preference value is changed.
|
|
||||||
*
|
|
||||||
* @param reportSpeed true to report speed, false to report pace
|
|
||||||
* @return true to reload all the data, false otherwise.
|
|
||||||
*/
|
|
||||||
boolean onReportSpeedChanged(boolean reportSpeed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the recording gps accuracy preference value is changed.
|
|
||||||
*
|
|
||||||
* @param recordingGpsAccuracy the new value
|
|
||||||
* @return true to reload all the data, false otherwise
|
|
||||||
*/
|
|
||||||
boolean onRecordingGpsAccuracy(int recordingGpsAccuracy);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the recording distance interval preference value is changed.
|
|
||||||
*
|
|
||||||
* @param recordingDistanceInterval the new value
|
|
||||||
* @return true to reload all the data, false otherwise.
|
|
||||||
*/
|
|
||||||
boolean onRecordingDistanceIntervalChanged(int recordingDistanceInterval);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,5 +26,4 @@ public enum TrackDataType {
|
|||||||
WAYPOINTS_TABLE, // waypoints table changes
|
WAYPOINTS_TABLE, // waypoints table changes
|
||||||
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
SAMPLED_IN_TRACK_POINTS_TABLE, // sampled-in track points table changes
|
||||||
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
SAMPLED_OUT_TRACK_POINTS_TABLE, // sampled-out track points table changes
|
||||||
PREFERENCE // preference changes
|
|
||||||
}
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010 Google Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
||||||
* use this file except in compliance with the License. You may obtain a copy of
|
|
||||||
* the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations under
|
|
||||||
* the License.
|
|
||||||
*/
|
|
||||||
package de.dennisguse.opentracks.content;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A request for the service to create a waypoint at the current location.
|
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
*/
|
|
||||||
public class WaypointCreationRequest {
|
|
||||||
|
|
||||||
public final static WaypointCreationRequest DEFAULT_WAYPOINT = new WaypointCreationRequest(WaypointType.WAYPOINT, false);
|
|
||||||
|
|
||||||
@Deprecated // TODO Do not create per
|
|
||||||
public final static WaypointCreationRequest DEFAULT_START_TRACK = new WaypointCreationRequest(WaypointType.STATISTICS, true);
|
|
||||||
|
|
||||||
private WaypointType type;
|
|
||||||
|
|
||||||
// true if this marker contains the track statistics
|
|
||||||
private boolean isTrackStatistics;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String category;
|
|
||||||
private String description;
|
|
||||||
private String iconUrl;
|
|
||||||
private String photoUrl;
|
|
||||||
|
|
||||||
private WaypointCreationRequest(WaypointType type, boolean isTrackStatistics) {
|
|
||||||
this(type, isTrackStatistics, null, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WaypointCreationRequest(WaypointType type, boolean isTrackStatistics, String name, String category, String description, String iconUrl, String photoUrl) {
|
|
||||||
this.type = type;
|
|
||||||
this.isTrackStatistics = isTrackStatistics;
|
|
||||||
this.name = name;
|
|
||||||
this.category = category;
|
|
||||||
this.description = description;
|
|
||||||
this.iconUrl = iconUrl;
|
|
||||||
this.photoUrl = photoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WaypointType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTrackStatistics() {
|
|
||||||
return isTrackStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIconUrl() {
|
|
||||||
return iconUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPhotoUrl() {
|
|
||||||
return photoUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-19
@@ -14,10 +14,12 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -35,8 +37,6 @@ public class Track {
|
|||||||
private String name = "";
|
private String name = "";
|
||||||
private String description = "";
|
private String description = "";
|
||||||
private String category = "";
|
private String category = "";
|
||||||
private long startId = -1L;
|
|
||||||
private long stopId = -1L;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The number of location points (present even if the points themselves are not loaded)
|
* The number of location points (present even if the points themselves are not loaded)
|
||||||
@@ -84,22 +84,6 @@ public class Track {
|
|||||||
this.category = category;
|
this.category = category;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStartId() {
|
|
||||||
return startId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStartId(long startId) {
|
|
||||||
this.startId = startId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStopId() {
|
|
||||||
return stopId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStopId(long stopId) {
|
|
||||||
this.stopId = stopId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumberOfPoints() {
|
public int getNumberOfPoints() {
|
||||||
return numberOfPoints;
|
return numberOfPoints;
|
||||||
}
|
}
|
||||||
@@ -124,6 +108,7 @@ public class Track {
|
|||||||
this.tripStatistics = tripStatistics;
|
this.tripStatistics = tripStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
public void addLocation(Location location) {
|
public void addLocation(Location location) {
|
||||||
locations.add(location);
|
locations.add(location);
|
||||||
}
|
}
|
||||||
+4
-4
@@ -13,7 +13,7 @@
|
|||||||
* License for the specific language governing permissions and limitations under
|
* License for the specific language governing permissions and limitations under
|
||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
|
||||||
@@ -24,16 +24,16 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
|||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class SensorDataSetLocation extends Location {
|
public class TrackPoint extends Location {
|
||||||
|
|
||||||
private SensorDataSet sensorDataSet = null;
|
private SensorDataSet sensorDataSet = null;
|
||||||
|
|
||||||
public SensorDataSetLocation(Location location, SensorDataSet sensorDataSet) {
|
public TrackPoint(Location location, SensorDataSet sensorDataSet) {
|
||||||
super(location);
|
super(location);
|
||||||
this.sensorDataSet = sensorDataSet;
|
this.sensorDataSet = sensorDataSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataSetLocation(String provider) {
|
public TrackPoint(String provider) {
|
||||||
super(provider);
|
super(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
+9
-2
@@ -14,11 +14,13 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for the track points table.
|
* Constants for the track points table.
|
||||||
*
|
*
|
||||||
@@ -27,15 +29,20 @@ import android.provider.BaseColumns;
|
|||||||
public interface TrackPointsColumns extends BaseColumns {
|
public interface TrackPointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "trackpoints";
|
String TABLE_NAME = "trackpoints";
|
||||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/trackpoints");
|
Uri CONTENT_URI_BY_ID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||||
|
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
String TRACKID = "trackid";
|
String TRACKID = "trackid";
|
||||||
|
|
||||||
String LONGITUDE = "longitude";
|
String LONGITUDE = "longitude";
|
||||||
String LATITUDE = "latitude";
|
String LATITUDE = "latitude";
|
||||||
|
double PAUSE_LATITUDE = 100.0;
|
||||||
|
double RESUME_LATITUDE = 200.0;
|
||||||
|
|
||||||
String TIME = "time";
|
String TIME = "time";
|
||||||
String ALTITUDE = "elevation";
|
String ALTITUDE = "elevation";
|
||||||
String ACCURACY = "accuracy";
|
String ACCURACY = "accuracy";
|
||||||
+19
-11
@@ -14,11 +14,13 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for the tracks table.
|
* Constants for the tracks table.
|
||||||
*
|
*
|
||||||
@@ -27,7 +29,7 @@ import android.provider.BaseColumns;
|
|||||||
public interface TracksColumns extends BaseColumns {
|
public interface TracksColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "tracks";
|
String TABLE_NAME = "tracks";
|
||||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/tracks");
|
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.track";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.track";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.track";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.track";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
@@ -36,22 +38,28 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
String NAME = "name"; // track name
|
String NAME = "name"; // track name
|
||||||
String DESCRIPTION = "description"; // track description
|
String DESCRIPTION = "description"; // track description
|
||||||
String CATEGORY = "category"; // track activity type
|
String CATEGORY = "category"; // track activity type
|
||||||
String STARTID = "startid"; // first track point id
|
@Deprecated
|
||||||
String STOPID = "stopid"; // last track point id
|
String STARTID = "startid"; // first track point id // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String STOPID = "stopid"; // last track point id // TODO UNUSED
|
||||||
String STARTTIME = "starttime"; // track start time
|
String STARTTIME = "starttime"; // track start time
|
||||||
String STOPTIME = "stoptime"; // track stop time
|
String STOPTIME = "stoptime"; // track stop time
|
||||||
String NUMPOINTS = "numpoints"; // number of track points
|
String NUMPOINTS = "numpoints"; // number of track points
|
||||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||||
String TOTALTIME = "totaltime"; // total time
|
String TOTALTIME = "totaltime"; // total time
|
||||||
String MOVINGTIME = "movingtime"; // moving time
|
String MOVINGTIME = "movingtime"; // moving time
|
||||||
String MINLAT = "minlat"; // minimum latitude
|
|
||||||
String MAXLAT = "maxlat"; // maximum latitude
|
|
||||||
String MINLON = "minlon"; // minimum longitude
|
|
||||||
String MAXLON = "maxlon"; // maximum longitude
|
|
||||||
String AVGSPEED = "avgspeed"; // average speed
|
|
||||||
|
|
||||||
// average moving speed
|
@Deprecated
|
||||||
String AVGMOVINGSPEED = "avgmovingspeed";
|
String MINLAT = "minlat"; // minimum latitude // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MAXLAT = "maxlat"; // maximum latitude // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MINLON = "minlon"; // minimum longitude //TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MAXLON = "maxlon"; // maximum longitude //TODO UNUSED
|
||||||
|
|
||||||
|
String AVGSPEED = "avgspeed"; // average speed
|
||||||
|
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
||||||
String MAXSPEED = "maxspeed"; // maximum speed
|
String MAXSPEED = "maxspeed"; // maximum speed
|
||||||
String MINELEVATION = "minelevation"; // minimum elevation
|
String MINELEVATION = "minelevation"; // minimum elevation
|
||||||
String MAXELEVATION = "maxelevation"; // maximum elevation
|
String MAXELEVATION = "maxelevation"; // maximum elevation
|
||||||
+2
-53
@@ -14,13 +14,11 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A waypoint.
|
* A waypoint.
|
||||||
*
|
*
|
||||||
@@ -35,31 +33,23 @@ public final class Waypoint {
|
|||||||
private String category = "";
|
private String category = "";
|
||||||
private String icon = "";
|
private String icon = "";
|
||||||
private long trackId = -1L;
|
private long trackId = -1L;
|
||||||
private WaypointType type = WaypointType.WAYPOINT;
|
|
||||||
private double length = 0.0;
|
private double length = 0.0;
|
||||||
private long duration = 0;
|
private long duration = 0;
|
||||||
private long startId = -1L;
|
|
||||||
private long stopId = -1L;
|
|
||||||
private Location location = null;
|
private Location location = null;
|
||||||
private TripStatistics tripStatistics = null;
|
|
||||||
private String photoUrl = "";
|
private String photoUrl = "";
|
||||||
|
|
||||||
public Waypoint() {
|
public Waypoint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Waypoint(String name, String description, String category, String icon, long trackId, WaypointType type, double length, long duration, long startId, long stopId, Location location, TripStatistics tripStatistics, String photoUrl) {
|
public Waypoint(String name, String description, String category, String icon, long trackId, double length, long duration, Location location, String photoUrl) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
this.type = type;
|
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
this.startId = startId;
|
|
||||||
this.stopId = stopId;
|
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.tripStatistics = tripStatistics;
|
|
||||||
this.photoUrl = photoUrl;
|
this.photoUrl = photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +101,6 @@ public final class Waypoint {
|
|||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WaypointType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(WaypointType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLength() {
|
public double getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
@@ -135,22 +117,6 @@ public final class Waypoint {
|
|||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStartId() {
|
|
||||||
return startId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStartId(long startId) {
|
|
||||||
this.startId = startId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getStopId() {
|
|
||||||
return stopId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStopId(long stopId) {
|
|
||||||
this.stopId = stopId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
public Location getLocation() {
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
@@ -159,18 +125,6 @@ public final class Waypoint {
|
|||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTripStatistics() {
|
|
||||||
return tripStatistics != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TripStatistics getTripStatistics() {
|
|
||||||
return tripStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTripStatistics(TripStatistics tripStatistics) {
|
|
||||||
this.tripStatistics = tripStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPhotoUrl() {
|
public String getPhotoUrl() {
|
||||||
return photoUrl;
|
return photoUrl;
|
||||||
}
|
}
|
||||||
@@ -186,9 +140,4 @@ public final class Waypoint {
|
|||||||
public boolean hasPhoto() {
|
public boolean hasPhoto() {
|
||||||
return photoUrl != null && !"".equals(photoUrl);
|
return photoUrl != null && !"".equals(photoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WaypointType {
|
|
||||||
WAYPOINT,
|
|
||||||
STATISTICS //Always the first waypoint; only one can exist.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+30
-12
@@ -14,11 +14,13 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for waypoints table.
|
* Constants for waypoints table.
|
||||||
*
|
*
|
||||||
@@ -27,7 +29,7 @@ import android.provider.BaseColumns;
|
|||||||
public interface WaypointsColumns extends BaseColumns {
|
public interface WaypointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "waypoints";
|
String TABLE_NAME = "waypoints";
|
||||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/waypoints");
|
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
@@ -38,12 +40,16 @@ public interface WaypointsColumns extends BaseColumns {
|
|||||||
String CATEGORY = "category"; // waypoint category
|
String CATEGORY = "category"; // waypoint category
|
||||||
String ICON = "icon"; // waypoint icon
|
String ICON = "icon"; // waypoint icon
|
||||||
String TRACKID = "trackid"; // track id
|
String TRACKID = "trackid"; // track id
|
||||||
String TYPE = "type"; // type
|
@Deprecated
|
||||||
|
String TYPE = "type"; // type // TODO UNUSED
|
||||||
|
|
||||||
String LENGTH = "length"; // length of the track (without smoothing)
|
String LENGTH = "length"; // length of the track (without smoothing)
|
||||||
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
String DURATION = "duration"; // total duration of the track (not from last waypoint)
|
||||||
String STARTTIME = "starttime"; // start time of the trip statistics
|
String STARTTIME = "starttime"; // start time of the trip statistics
|
||||||
String STARTID = "startid"; // start track point id
|
@Deprecated
|
||||||
String STOPID = "stopid"; // stop track point id
|
String STARTID = "startid"; // start track point id // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String STOPID = "stopid"; // stop track point id // TODO UNUSED
|
||||||
|
|
||||||
String LONGITUDE = "longitude"; // longitude
|
String LONGITUDE = "longitude"; // longitude
|
||||||
String LATITUDE = "latitude"; // latitude
|
String LATITUDE = "latitude"; // latitude
|
||||||
@@ -53,17 +59,29 @@ public interface WaypointsColumns extends BaseColumns {
|
|||||||
String SPEED = "speed"; // speed
|
String SPEED = "speed"; // speed
|
||||||
String BEARING = "bearing"; // bearing
|
String BEARING = "bearing"; // bearing
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
String TOTALDISTANCE = "totaldistance"; // total distance
|
String TOTALDISTANCE = "totaldistance"; // total distance
|
||||||
|
@Deprecated
|
||||||
String TOTALTIME = "totaltime"; // total time
|
String TOTALTIME = "totaltime"; // total time
|
||||||
|
@Deprecated
|
||||||
String MOVINGTIME = "movingtime"; // moving time
|
String MOVINGTIME = "movingtime"; // moving time
|
||||||
|
@Deprecated
|
||||||
String AVGSPEED = "avgspeed"; // average speed
|
String AVGSPEED = "avgspeed"; // average speed
|
||||||
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
|
||||||
String MAXSPEED = "maxspeed"; // max speed
|
@Deprecated
|
||||||
String MINELEVATION = "minelevation"; // min elevation
|
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed // TODO UNUSED
|
||||||
String MAXELEVATION = "maxelevation"; // max elevation
|
@Deprecated
|
||||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
String MAXSPEED = "maxspeed"; // max speed // TODO UNUSED
|
||||||
String MINGRADE = "mingrade"; // min grade
|
@Deprecated
|
||||||
String MAXGRADE = "maxgrade"; // max grade
|
String MINELEVATION = "minelevation"; // min elevation // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MAXELEVATION = "maxelevation"; // max elevation // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String ELEVATIONGAIN = "elevationgain"; // elevation gain // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MINGRADE = "mingrade"; // min grade // TODO UNUSED
|
||||||
|
@Deprecated
|
||||||
|
String MAXGRADE = "maxgrade"; // max grade // TODO UNUSED
|
||||||
|
|
||||||
String PHOTOURL = "photoUrl"; // url for the photo
|
String PHOTOURL = "photoUrl"; // url for the photo
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of track points indexes.
|
||||||
|
*/
|
||||||
|
class CachedTrackPointsIndexes {
|
||||||
|
final int idIndex;
|
||||||
|
final int longitudeIndex;
|
||||||
|
final int latitudeIndex;
|
||||||
|
final int timeIndex;
|
||||||
|
final int altitudeIndex;
|
||||||
|
final int accuracyIndex;
|
||||||
|
final int speedIndex;
|
||||||
|
final int bearingIndex;
|
||||||
|
final int sensorHeartRateIndex;
|
||||||
|
final int sensorCadenceIndex;
|
||||||
|
final int sensorPowerIndex;
|
||||||
|
|
||||||
|
CachedTrackPointsIndexes(Cursor cursor) {
|
||||||
|
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
|
||||||
|
longitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
||||||
|
latitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
||||||
|
timeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
||||||
|
altitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
|
||||||
|
accuracyIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
||||||
|
speedIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
||||||
|
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||||
|
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
||||||
|
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
||||||
|
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
||||||
|
}
|
||||||
|
}
|
||||||
+157
-447
@@ -14,7 +14,7 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
@@ -22,7 +22,7 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
@@ -30,12 +30,16 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
import de.dennisguse.opentracks.BuildConfig;
|
||||||
import de.dennisguse.opentracks.android.ContentResolverWrapper;
|
import de.dennisguse.opentracks.android.ContentResolverWrapper;
|
||||||
import de.dennisguse.opentracks.android.IContentResolver;
|
import de.dennisguse.opentracks.android.IContentResolver;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
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.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
@@ -51,20 +55,21 @@ public class ContentProviderUtils {
|
|||||||
private static final String TAG = ContentProviderUtils.class.getSimpleName();
|
private static final String TAG = ContentProviderUtils.class.getSimpleName();
|
||||||
private static final int MAX_LATITUDE = 90000000;
|
private static final int MAX_LATITUDE = 90000000;
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of waypoints that will be loaded at one time.
|
|
||||||
*/
|
|
||||||
public static int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The authority (the first part of the URI) for the app's content provider.
|
* The authority (the first part of the URI) for the app's content provider.
|
||||||
*/
|
*/
|
||||||
static String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
|
static final String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base URI for the app's content provider.
|
* The base URI for the app's content provider.
|
||||||
*/
|
*/
|
||||||
static String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
public static final String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of waypoints that will be loaded at one time.
|
||||||
|
*/
|
||||||
|
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
||||||
|
public static final String ID_SEPARATOR = ",";
|
||||||
|
|
||||||
private final IContentResolver contentResolver;
|
private final IContentResolver contentResolver;
|
||||||
private int defaultCursorBatchSize = 2000;
|
private int defaultCursorBatchSize = 2000;
|
||||||
@@ -104,18 +109,12 @@ public class ContentProviderUtils {
|
|||||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||||
int startIdIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTID);
|
|
||||||
int stopIdIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPID);
|
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||||
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
||||||
int numPointsIndex = cursor.getColumnIndexOrThrow(TracksColumns.NUMPOINTS);
|
int numPointsIndex = cursor.getColumnIndexOrThrow(TracksColumns.NUMPOINTS);
|
||||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||||
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||||
int movingTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
|
int movingTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
|
||||||
int minLatIndex = cursor.getColumnIndexOrThrow(TracksColumns.MINLAT);
|
|
||||||
int maxLatIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXLAT);
|
|
||||||
int minLonIndex = cursor.getColumnIndexOrThrow(TracksColumns.MINLON);
|
|
||||||
int maxLonIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXLON);
|
|
||||||
int maxSpeedIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
|
int maxSpeedIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
|
||||||
int minElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
|
int minElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
|
||||||
int maxElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
|
int maxElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
|
||||||
@@ -138,12 +137,6 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(categoryIndex)) {
|
if (!cursor.isNull(categoryIndex)) {
|
||||||
track.setCategory(cursor.getString(categoryIndex));
|
track.setCategory(cursor.getString(categoryIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(startIdIndex)) {
|
|
||||||
track.setStartId(cursor.getLong(startIdIndex));
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(stopIdIndex)) {
|
|
||||||
track.setStopId(cursor.getLong(stopIdIndex));
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(startTimeIndex)) {
|
if (!cursor.isNull(startTimeIndex)) {
|
||||||
tripStatistics.setStartTime(cursor.getLong(startTimeIndex));
|
tripStatistics.setStartTime(cursor.getLong(startTimeIndex));
|
||||||
}
|
}
|
||||||
@@ -162,13 +155,6 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(movingTimeIndex)) {
|
if (!cursor.isNull(movingTimeIndex)) {
|
||||||
tripStatistics.setMovingTime(cursor.getLong(movingTimeIndex));
|
tripStatistics.setMovingTime(cursor.getLong(movingTimeIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(minLatIndex) && !cursor.isNull(maxLatIndex) && !cursor.isNull(minLonIndex) && !cursor.isNull(maxLonIndex)) {
|
|
||||||
int bottom = cursor.getInt(minLatIndex);
|
|
||||||
int top = cursor.getInt(maxLatIndex);
|
|
||||||
int left = cursor.getInt(minLonIndex);
|
|
||||||
int right = cursor.getInt(maxLonIndex);
|
|
||||||
tripStatistics.setBounds(left, top, right, bottom);
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(maxSpeedIndex)) {
|
if (!cursor.isNull(maxSpeedIndex)) {
|
||||||
tripStatistics.setMaxSpeed(cursor.getFloat(maxSpeedIndex));
|
tripStatistics.setMaxSpeed(cursor.getFloat(maxSpeedIndex));
|
||||||
}
|
}
|
||||||
@@ -197,13 +183,13 @@ public class ContentProviderUtils {
|
|||||||
* Deletes all tracks (including waypoints and track points).
|
* Deletes all tracks (including waypoints and track points).
|
||||||
*/
|
*/
|
||||||
public void deleteAllTracks(Context context) {
|
public void deleteAllTracks(Context context) {
|
||||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null);
|
||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||||
// Delete tracks last since it triggers a database vaccum call
|
// Delete tracks last since it triggers a database vaccum call
|
||||||
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
||||||
|
|
||||||
File dir = FileUtils.getPhotoDir();
|
File dir = FileUtils.getPhotoDir(context);
|
||||||
deleteDirectoryRecurse(context, dir);
|
deleteDirectoryRecurse(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,23 +206,19 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes track points and waypoints of a track. Assumes
|
* Deletes track points and waypoints of a track.
|
||||||
* {@link TracksColumns#STARTID}, {@link TracksColumns#STOPID}, and
|
* Assumes {@link TracksColumns#NUMPOINTS} will be updated by the caller.
|
||||||
* {@link TracksColumns#NUMPOINTS} will be updated by the caller.
|
|
||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
*/
|
*/
|
||||||
private void deleteTrackPointsAndWaypoints(Context context, long trackId) {
|
private void deleteTrackPointsAndWaypoints(Context context, long trackId) {
|
||||||
Track track = getTrack(trackId);
|
String where = TrackPointsColumns.TRACKID + "=?";
|
||||||
if (track != null) {
|
String[] selectionArgs = new String[]{Long.toString(trackId)};
|
||||||
String where = TrackPointsColumns._ID + ">=? AND " + TrackPointsColumns._ID + "<=?";
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, where, selectionArgs);
|
||||||
String[] selectionArgs = new String[]{
|
|
||||||
Long.toString(track.getStartId()), Long.toString(track.getStopId())};
|
|
||||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, where, selectionArgs);
|
|
||||||
}
|
|
||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
|
||||||
new String[]{Long.toString(trackId)});
|
new String[]{Long.toString(trackId)});
|
||||||
deleteDirectoryRecurse(context, FileUtils.getPhotoDir(trackId));
|
deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,15 +226,12 @@ public class ContentProviderUtils {
|
|||||||
*
|
*
|
||||||
* @param dir the directory
|
* @param dir the directory
|
||||||
*/
|
*/
|
||||||
private void deleteDirectoryRecurse(Context context, File dir) {
|
private void deleteDirectoryRecurse(File dir) {
|
||||||
if (FileUtils.isDirectory(dir)) {
|
if (dir != null && dir.exists() && dir.isDirectory()) {
|
||||||
for (File child : dir.listFiles()) {
|
for (File child : dir.listFiles()) {
|
||||||
deleteDirectoryRecurse(context, child);
|
deleteDirectoryRecurse(child);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (dir.exists()) {
|
|
||||||
dir.delete();
|
dir.delete();
|
||||||
FileUtils.updateMediaScanner(context, Uri.fromFile(dir));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,9 +240,10 @@ public class ContentProviderUtils {
|
|||||||
* If no track exists, an empty list is returned.
|
* If no track exists, an empty list is returned.
|
||||||
* NOTE: the returned tracks do not have any track points attached.
|
* NOTE: the returned tracks do not have any track points attached.
|
||||||
*/
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
public List<Track> getAllTracks() {
|
public List<Track> getAllTracks() {
|
||||||
ArrayList<Track> tracks = new ArrayList<>();
|
ArrayList<Track> tracks = new ArrayList<>();
|
||||||
try (Cursor cursor = getTrackCursor(null, null, null, TracksColumns._ID)) {
|
try (Cursor cursor = getTrackCursor(null, null, TracksColumns._ID)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
tracks.ensureCapacity(cursor.getCount());
|
tracks.ensureCapacity(cursor.getCount());
|
||||||
do {
|
do {
|
||||||
@@ -278,7 +258,7 @@ public class ContentProviderUtils {
|
|||||||
* Gets the last track or null.
|
* Gets the last track or null.
|
||||||
*/
|
*/
|
||||||
public Track getLastTrack() {
|
public Track getLastTrack() {
|
||||||
try (Cursor cursor = getTrackCursor(null, null, null, TracksColumns.STARTTIME + " DESC")) {
|
try (Cursor cursor = getTrackCursor(null, null, TracksColumns.STARTTIME + " DESC")) {
|
||||||
// Using the same order as shown in the track list
|
// Using the same order as shown in the track list
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
return createTrack(cursor);
|
return createTrack(cursor);
|
||||||
@@ -297,7 +277,7 @@ public class ContentProviderUtils {
|
|||||||
if (trackId < 0) {
|
if (trackId < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try (Cursor cursor = getTrackCursor(null, TracksColumns._ID + "=?", new String[]{Long.toString(trackId)}, TracksColumns._ID)) {
|
try (Cursor cursor = getTrackCursor(TracksColumns._ID + "=?", new String[]{Long.toString(trackId)}, TracksColumns._ID)) {
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
return createTrack(cursor);
|
return createTrack(cursor);
|
||||||
}
|
}
|
||||||
@@ -314,7 +294,7 @@ public class ContentProviderUtils {
|
|||||||
* @param sortOrder the sort order. Can be null
|
* @param sortOrder the sort order. Can be null
|
||||||
*/
|
*/
|
||||||
public Cursor getTrackCursor(String selection, String[] selectionArgs, String sortOrder) {
|
public Cursor getTrackCursor(String selection, String[] selectionArgs, String sortOrder) {
|
||||||
return getTrackCursor(null, selection, selectionArgs, sortOrder);
|
return contentResolver.query(TracksColumns.CONTENT_URI, null, selection, selectionArgs, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -350,18 +330,12 @@ public class ContentProviderUtils {
|
|||||||
values.put(TracksColumns.NAME, track.getName());
|
values.put(TracksColumns.NAME, track.getName());
|
||||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
values.put(TracksColumns.CATEGORY, track.getCategory());
|
||||||
values.put(TracksColumns.STARTID, track.getStartId());
|
|
||||||
values.put(TracksColumns.STOPID, track.getStopId());
|
|
||||||
values.put(TracksColumns.STARTTIME, tripStatistics.getStartTime());
|
values.put(TracksColumns.STARTTIME, tripStatistics.getStartTime());
|
||||||
values.put(TracksColumns.STOPTIME, tripStatistics.getStopTime());
|
values.put(TracksColumns.STOPTIME, tripStatistics.getStopTime());
|
||||||
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
|
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
|
||||||
values.put(TracksColumns.TOTALDISTANCE, tripStatistics.getTotalDistance());
|
values.put(TracksColumns.TOTALDISTANCE, tripStatistics.getTotalDistance());
|
||||||
values.put(TracksColumns.TOTALTIME, tripStatistics.getTotalTime());
|
values.put(TracksColumns.TOTALTIME, tripStatistics.getTotalTime());
|
||||||
values.put(TracksColumns.MOVINGTIME, tripStatistics.getMovingTime());
|
values.put(TracksColumns.MOVINGTIME, tripStatistics.getMovingTime());
|
||||||
values.put(TracksColumns.MINLAT, tripStatistics.getBottom());
|
|
||||||
values.put(TracksColumns.MAXLAT, tripStatistics.getTop());
|
|
||||||
values.put(TracksColumns.MINLON, tripStatistics.getLeft());
|
|
||||||
values.put(TracksColumns.MAXLON, tripStatistics.getRight());
|
|
||||||
values.put(TracksColumns.AVGSPEED, tripStatistics.getAverageSpeed());
|
values.put(TracksColumns.AVGSPEED, tripStatistics.getAverageSpeed());
|
||||||
values.put(TracksColumns.AVGMOVINGSPEED, tripStatistics.getAverageMovingSpeed());
|
values.put(TracksColumns.AVGMOVINGSPEED, tripStatistics.getAverageMovingSpeed());
|
||||||
values.put(TracksColumns.MAXSPEED, tripStatistics.getMaxSpeed());
|
values.put(TracksColumns.MAXSPEED, tripStatistics.getMaxSpeed());
|
||||||
@@ -375,17 +349,6 @@ public class ContentProviderUtils {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a track cursor.
|
|
||||||
*
|
|
||||||
* @param projection the projection
|
|
||||||
* @param selection the selection
|
|
||||||
* @param selectionArgs the selection arguments
|
|
||||||
* @param sortOrder the sort oder
|
|
||||||
*/
|
|
||||||
private Cursor getTrackCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
|
||||||
return contentResolver.query(TracksColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a waypoint from a cursor.
|
* Creates a waypoint from a cursor.
|
||||||
@@ -399,12 +362,8 @@ public class ContentProviderUtils {
|
|||||||
int categoryIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.CATEGORY);
|
||||||
int iconIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ICON);
|
int iconIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ICON);
|
||||||
int trackIdIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TRACKID);
|
int trackIdIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TRACKID);
|
||||||
int typeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TYPE);
|
|
||||||
int lengthIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LENGTH);
|
int lengthIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LENGTH);
|
||||||
int durationIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.DURATION);
|
int durationIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.DURATION);
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTTIME);
|
|
||||||
int startIdIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTID);
|
|
||||||
int stopIdIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.STOPID);
|
|
||||||
int longitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LONGITUDE);
|
int longitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LONGITUDE);
|
||||||
int latitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LATITUDE);
|
int latitudeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.LATITUDE);
|
||||||
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
int timeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
|
||||||
@@ -412,15 +371,6 @@ public class ContentProviderUtils {
|
|||||||
int accuracyIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ACCURACY);
|
int accuracyIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ACCURACY);
|
||||||
int speedIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED);
|
int speedIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED);
|
||||||
int bearingIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.BEARING);
|
int bearingIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.BEARING);
|
||||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALDISTANCE);
|
|
||||||
int totalTimeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALTIME);
|
|
||||||
int movingTimeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MOVINGTIME);
|
|
||||||
int maxSpeedIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXSPEED);
|
|
||||||
int minElevationIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MINELEVATION);
|
|
||||||
int maxElevationIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXELEVATION);
|
|
||||||
int elevationGainIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.ELEVATIONGAIN);
|
|
||||||
int minGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MINGRADE);
|
|
||||||
int maxGradeIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXGRADE);
|
|
||||||
int photoUrlIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.PHOTOURL);
|
int photoUrlIndex = cursor.getColumnIndexOrThrow(WaypointsColumns.PHOTOURL);
|
||||||
|
|
||||||
Waypoint waypoint = new Waypoint();
|
Waypoint waypoint = new Waypoint();
|
||||||
@@ -443,21 +393,12 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(trackIdIndex)) {
|
if (!cursor.isNull(trackIdIndex)) {
|
||||||
waypoint.setTrackId(cursor.getLong(trackIdIndex));
|
waypoint.setTrackId(cursor.getLong(trackIdIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(typeIndex)) {
|
|
||||||
waypoint.setType(WaypointType.values()[cursor.getInt(typeIndex)]);
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(lengthIndex)) {
|
if (!cursor.isNull(lengthIndex)) {
|
||||||
waypoint.setLength(cursor.getFloat(lengthIndex));
|
waypoint.setLength(cursor.getFloat(lengthIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(durationIndex)) {
|
if (!cursor.isNull(durationIndex)) {
|
||||||
waypoint.setDuration(cursor.getLong(durationIndex));
|
waypoint.setDuration(cursor.getLong(durationIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(startIdIndex)) {
|
|
||||||
waypoint.setStartId(cursor.getLong(startIdIndex));
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(stopIdIndex)) {
|
|
||||||
waypoint.setStopId(cursor.getLong(stopIdIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
Location location = new Location("");
|
Location location = new Location("");
|
||||||
if (!cursor.isNull(longitudeIndex) && !cursor.isNull(latitudeIndex)) {
|
if (!cursor.isNull(longitudeIndex) && !cursor.isNull(latitudeIndex)) {
|
||||||
@@ -481,52 +422,6 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
waypoint.setLocation(location);
|
waypoint.setLocation(location);
|
||||||
|
|
||||||
TripStatistics tripStatistics = new TripStatistics();
|
|
||||||
boolean hasTripStatistics = false;
|
|
||||||
if (!cursor.isNull(startTimeIndex)) {
|
|
||||||
tripStatistics.setStartTime(cursor.getLong(startTimeIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(totalDistanceIndex)) {
|
|
||||||
tripStatistics.setTotalDistance(cursor.getFloat(totalDistanceIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(totalTimeIndex)) {
|
|
||||||
tripStatistics.setTotalTime(cursor.getLong(totalTimeIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(movingTimeIndex)) {
|
|
||||||
tripStatistics.setMovingTime(cursor.getLong(movingTimeIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(maxSpeedIndex)) {
|
|
||||||
tripStatistics.setMaxSpeed(cursor.getFloat(maxSpeedIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(minElevationIndex)) {
|
|
||||||
tripStatistics.setMinElevation(cursor.getFloat(minElevationIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(maxElevationIndex)) {
|
|
||||||
tripStatistics.setMaxElevation(cursor.getFloat(maxElevationIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(elevationGainIndex)) {
|
|
||||||
tripStatistics.setTotalElevationGain(cursor.getFloat(elevationGainIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(minGradeIndex)) {
|
|
||||||
tripStatistics.setMinGrade(cursor.getFloat(minGradeIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (!cursor.isNull(maxGradeIndex)) {
|
|
||||||
tripStatistics.setMaxGrade(cursor.getFloat(maxGradeIndex));
|
|
||||||
hasTripStatistics = true;
|
|
||||||
}
|
|
||||||
if (hasTripStatistics) {
|
|
||||||
waypoint.setTripStatistics(tripStatistics);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cursor.isNull(photoUrlIndex)) {
|
if (!cursor.isNull(photoUrlIndex)) {
|
||||||
waypoint.setPhotoUrl(cursor.getString(photoUrlIndex));
|
waypoint.setPhotoUrl(cursor.getString(photoUrlIndex));
|
||||||
}
|
}
|
||||||
@@ -538,33 +433,17 @@ public class ContentProviderUtils {
|
|||||||
* If deleting a statistics waypoint, this will also correct the next statistics waypoint after the deleted one to reflect the deletion.
|
* If deleting a statistics waypoint, this will also correct the next statistics waypoint after the deleted one to reflect the deletion.
|
||||||
* The generator is used to update the next statistics waypoint.
|
* The generator is used to update the next statistics waypoint.
|
||||||
*
|
*
|
||||||
* @param waypointId the waypoint id
|
* @param waypointId the waypoint id
|
||||||
* @param descriptionGenerator the description generator. Can be null for waypoint marker
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void deleteWaypoint(Context context, long waypointId, DescriptionGenerator descriptionGenerator) {
|
public void deleteWaypoint(long waypointId) {
|
||||||
final Waypoint waypoint = getWaypoint(waypointId);
|
final Waypoint waypoint = getWaypoint(waypointId);
|
||||||
if (waypoint != null && waypoint.getType() == WaypointType.STATISTICS
|
|
||||||
&& descriptionGenerator != null) {
|
|
||||||
final Waypoint nextWaypoint = getNextStatisticsWaypointAfter(waypoint);
|
|
||||||
if (nextWaypoint == null) {
|
|
||||||
Log.d(TAG, "Unable to find the next statistics marker after deleting one.");
|
|
||||||
} else {
|
|
||||||
nextWaypoint.getTripStatistics().merge(waypoint.getTripStatistics());
|
|
||||||
nextWaypoint.setDescription(
|
|
||||||
descriptionGenerator.generateWaypointDescription(nextWaypoint.getTripStatistics()));
|
|
||||||
if (!updateWaypoint(nextWaypoint)) {
|
|
||||||
Log.e(TAG, "Unable to update the next statistics marker after deleting one.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (waypoint != null && waypoint.hasPhoto()) {
|
if (waypoint != null && waypoint.hasPhoto()) {
|
||||||
Uri uri = waypoint.getPhotoURI();
|
Uri uri = waypoint.getPhotoURI();
|
||||||
File file = new File(uri.getPath());
|
File file = new File(uri.getPath());
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
File parent = file.getParentFile();
|
File parent = file.getParentFile();
|
||||||
file.delete();
|
file.delete();
|
||||||
FileUtils.updateMediaScanner(context, uri);
|
|
||||||
if (parent.listFiles().length == 0) {
|
if (parent.listFiles().length == 0) {
|
||||||
parent.delete();
|
parent.delete();
|
||||||
}
|
}
|
||||||
@@ -573,65 +452,22 @@ public class ContentProviderUtils {
|
|||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId)});
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns._ID + "=?", new String[]{Long.toString(waypointId)});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first waypoint id for a track.
|
|
||||||
* The first waypoint is special as it contains the stats for the track.
|
|
||||||
* Returns -1L if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
public long getFirstWaypointId(long trackId) {
|
|
||||||
if (trackId < 0) {
|
|
||||||
return -1L;
|
|
||||||
}
|
|
||||||
try (Cursor cursor = getWaypointCursor(new String[]{WaypointsColumns._ID}, WaypointsColumns.TRACKID + "=?", new String[]{Long.toString(trackId)}, WaypointsColumns._ID, 1)) {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return cursor.getLong(cursor.getColumnIndexOrThrow(WaypointsColumns._ID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the last waypoint for a type. Returns null if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param waypointType the waypoint type
|
|
||||||
*/
|
|
||||||
public Waypoint getLastWaypoint(long trackId, WaypointType waypointType) {
|
|
||||||
if (trackId < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?";
|
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId), Integer.toString(waypointType.ordinal())};
|
|
||||||
try (Cursor cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID + " DESC", 1)) {
|
|
||||||
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return createWaypoint(cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the next waypoint number for a type.
|
* Gets the next waypoint number for a type.
|
||||||
* Returns -1 if not able to get the next waypoint number.
|
* Returns -1 if not able to get the next waypoint number.
|
||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
* @param waypointType the waypoint type
|
|
||||||
*/
|
*/
|
||||||
public int getNextWaypointNumber(long trackId, WaypointType waypointType) {
|
public int getNextWaypointNumber(long trackId) {
|
||||||
if (trackId < 0) {
|
if (trackId < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
String[] projection = {WaypointsColumns._ID};
|
String[] projection = {WaypointsColumns._ID};
|
||||||
String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?";
|
String selection = WaypointsColumns.TRACKID + "=?";
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId), Integer.toString(waypointType.ordinal())};
|
String[] selectionArgs = new String[]{Long.toString(trackId)};
|
||||||
try (Cursor cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1)) {
|
try (Cursor cursor = getWaypointCursor(projection, selection, selectionArgs, WaypointsColumns._ID, -1)) {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
int count = cursor.getCount();
|
return cursor.getCount();
|
||||||
// For statistics markers, the first marker is for the track statistics, so return the count as the next user visible number.
|
|
||||||
return waypointType == WaypointType.STATISTICS ? count : count + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@@ -695,6 +531,19 @@ public class ContentProviderUtils {
|
|||||||
return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints);
|
return getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, maxWaypoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public List<Waypoint> getWaypoints(long trackId) {
|
||||||
|
ArrayList<Waypoint> waypoints = new ArrayList<>();
|
||||||
|
try (Cursor cursor = getWaypointCursor(trackId, -1L, -1)) {
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
waypoints.add(createWaypoint(cursor));
|
||||||
|
} while (cursor.moveToNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return waypoints;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of waypoints for a track.
|
* Gets the number of waypoints for a track.
|
||||||
*
|
*
|
||||||
@@ -713,8 +562,7 @@ public class ContentProviderUtils {
|
|||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
int count = cursor.getInt(0);
|
int count = cursor.getInt(0);
|
||||||
cursor.close();
|
cursor.close();
|
||||||
// not count the first waypoint
|
return count;
|
||||||
return count > 0 ? count - 1 : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -751,11 +599,8 @@ public class ContentProviderUtils {
|
|||||||
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
|
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
|
||||||
values.put(WaypointsColumns.ICON, waypoint.getIcon());
|
values.put(WaypointsColumns.ICON, waypoint.getIcon());
|
||||||
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId());
|
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId());
|
||||||
values.put(WaypointsColumns.TYPE, waypoint.getType().ordinal());
|
|
||||||
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
|
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
|
||||||
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
|
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
|
||||||
values.put(WaypointsColumns.STARTID, waypoint.getStartId());
|
|
||||||
values.put(WaypointsColumns.STOPID, waypoint.getStopId());
|
|
||||||
|
|
||||||
Location location = waypoint.getLocation();
|
Location location = waypoint.getLocation();
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
@@ -776,38 +621,10 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TripStatistics tripStatistics = waypoint.getTripStatistics();
|
|
||||||
if (tripStatistics != null) {
|
|
||||||
values.put(WaypointsColumns.STARTTIME, tripStatistics.getStartTime());
|
|
||||||
values.put(WaypointsColumns.TOTALDISTANCE, tripStatistics.getTotalDistance());
|
|
||||||
values.put(WaypointsColumns.TOTALTIME, tripStatistics.getTotalTime());
|
|
||||||
values.put(WaypointsColumns.MOVINGTIME, tripStatistics.getMovingTime());
|
|
||||||
values.put(WaypointsColumns.AVGSPEED, tripStatistics.getAverageSpeed());
|
|
||||||
values.put(WaypointsColumns.AVGMOVINGSPEED, tripStatistics.getAverageMovingSpeed());
|
|
||||||
values.put(WaypointsColumns.MAXSPEED, tripStatistics.getMaxSpeed());
|
|
||||||
values.put(WaypointsColumns.MINELEVATION, tripStatistics.getMinElevation());
|
|
||||||
values.put(WaypointsColumns.MAXELEVATION, tripStatistics.getMaxElevation());
|
|
||||||
values.put(WaypointsColumns.ELEVATIONGAIN, tripStatistics.getTotalElevationGain());
|
|
||||||
values.put(WaypointsColumns.MINGRADE, tripStatistics.getMinGrade());
|
|
||||||
values.put(WaypointsColumns.MAXGRADE, tripStatistics.getMaxGrade());
|
|
||||||
}
|
|
||||||
|
|
||||||
values.put(WaypointsColumns.PHOTOURL, waypoint.getPhotoUrl());
|
values.put(WaypointsColumns.PHOTOURL, waypoint.getPhotoUrl());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Waypoint getNextStatisticsWaypointAfter(Waypoint waypoint) {
|
|
||||||
String selection = WaypointsColumns._ID + ">? AND " + WaypointsColumns.TRACKID + "=? AND "
|
|
||||||
+ WaypointsColumns.TYPE + "=" + WaypointType.STATISTICS.ordinal();
|
|
||||||
String[] selectionArgs = new String[]{Long.toString(waypoint.getId()), Long.toString(waypoint.getTrackId())};
|
|
||||||
try (Cursor cursor = getWaypointCursor(null, selection, selectionArgs, WaypointsColumns._ID, 1)) {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
|
||||||
return createWaypoint(cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a waypoint cursor.
|
* Gets a waypoint cursor.
|
||||||
*
|
*
|
||||||
@@ -844,7 +661,7 @@ public class ContentProviderUtils {
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
values[i] = createContentValues(locations[i], trackId);
|
values[i] = createContentValues(locations[i], trackId);
|
||||||
}
|
}
|
||||||
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI_BY_ID, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -853,7 +670,7 @@ public class ContentProviderUtils {
|
|||||||
* @param cursor the cursor pointing to the location
|
* @param cursor the cursor pointing to the location
|
||||||
*/
|
*/
|
||||||
public Location createTrackPoint(Cursor cursor) {
|
public Location createTrackPoint(Cursor cursor) {
|
||||||
Location location = new SensorDataSetLocation("");
|
Location location = new TrackPoint("");
|
||||||
fillTrackPoint(cursor, new CachedTrackPointsIndexes(cursor), location);
|
fillTrackPoint(cursor, new CachedTrackPointsIndexes(cursor), location);
|
||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
@@ -864,6 +681,7 @@ public class ContentProviderUtils {
|
|||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public long getFirstTrackPointId(long trackId) {
|
public long getFirstTrackPointId(long trackId) {
|
||||||
if (trackId < 0) {
|
if (trackId < 0) {
|
||||||
return -1L;
|
return -1L;
|
||||||
@@ -886,6 +704,7 @@ public class ContentProviderUtils {
|
|||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public long getLastTrackPointId(long trackId) {
|
public long getLastTrackPointId(long trackId) {
|
||||||
if (trackId < 0) {
|
if (trackId < 0) {
|
||||||
return -1L;
|
return -1L;
|
||||||
@@ -914,8 +733,8 @@ public class ContentProviderUtils {
|
|||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID
|
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID
|
||||||
+ ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID
|
+ ") from " + TrackPointsColumns.TABLE_NAME
|
||||||
+ "=? AND " + TrackPointsColumns.TIME + "=?)";
|
+ " WHERE " + TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns.TIME + "=?)";
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId), Long.toString(location.getTime())};
|
String[] selectionArgs = new String[]{Long.toString(trackId), Long.toString(location.getTime())};
|
||||||
try (Cursor cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection, selectionArgs, TrackPointsColumns._ID)) {
|
try (Cursor cursor = getTrackPointCursor(new String[]{TrackPointsColumns._ID}, selection, selectionArgs, TrackPointsColumns._ID)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
@@ -925,29 +744,13 @@ public class ContentProviderUtils {
|
|||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the first valid location for a track.
|
|
||||||
* Returns null if it doesn't exist.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
public Location getFirstValidTrackPoint(long trackId) {
|
|
||||||
if (trackId < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String selection = TrackPointsColumns._ID + "=(select min(" + TrackPointsColumns._ID + ") from "
|
|
||||||
+ TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=? AND "
|
|
||||||
+ TrackPointsColumns.LATITUDE + "<=" + MAX_LATITUDE + ")";
|
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId)};
|
|
||||||
return findTrackPointBy(selection, selectionArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the last valid location for a track.
|
* Gets the last valid location for a track.
|
||||||
* Returns null if it doesn't exist.
|
* Returns null if it doesn't exist.
|
||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Location getLastValidTrackPoint(long trackId) {
|
public Location getLastValidTrackPoint(long trackId) {
|
||||||
if (trackId < 0) {
|
if (trackId < 0) {
|
||||||
return null;
|
return null;
|
||||||
@@ -995,164 +798,6 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new read-only iterator over a given track's points.
|
|
||||||
* It provides a lightweight way of iterating over long tracks without failing due to the underlying cursor limitations.
|
|
||||||
* Since it's a read-only iterator, {@link Iterator#remove()} always throws {@link UnsupportedOperationException}.
|
|
||||||
* Each call to {@link LocationIterator#next()} may advance to the next DB record, and if so, the iterator calls {@link LocationFactory#createLocation()} and populates it with information retrieved from the record.
|
|
||||||
* When done with iteration, {@link LocationIterator#close()} must be called.
|
|
||||||
*
|
|
||||||
* @param trackId the track id
|
|
||||||
* @param startTrackPointId the starting track point id. -1L to ignore
|
|
||||||
* @param descending true to sort the result in descending order (latest location first)
|
|
||||||
* @param locationFactory the location factory
|
|
||||||
*/
|
|
||||||
public LocationIterator getTrackPointLocationIterator(final long trackId,
|
|
||||||
final long startTrackPointId, final boolean descending,
|
|
||||||
final LocationFactory locationFactory) {
|
|
||||||
if (locationFactory == null) {
|
|
||||||
throw new IllegalArgumentException("locationFactory is null");
|
|
||||||
}
|
|
||||||
return new LocationIterator() {
|
|
||||||
private long lastTrackPointId = -1L;
|
|
||||||
private Cursor cursor = getCursor(startTrackPointId);
|
|
||||||
private final CachedTrackPointsIndexes
|
|
||||||
indexes = cursor != null ? new CachedTrackPointsIndexes(cursor)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the track point cursor.
|
|
||||||
*
|
|
||||||
* @param trackPointId the starting track point id
|
|
||||||
*/
|
|
||||||
private Cursor getCursor(long trackPointId) {
|
|
||||||
return getTrackPointCursor(trackId, trackPointId, defaultCursorBatchSize, descending);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Advances the cursor to the next batch. Returns true if successful.
|
|
||||||
*/
|
|
||||||
private boolean advanceCursorToNextBatch() {
|
|
||||||
long trackPointId = lastTrackPointId == -1L ? -1L
|
|
||||||
: lastTrackPointId + (descending ? -1 : 1);
|
|
||||||
Log.d(TAG, "Advancing track point id: " + trackPointId);
|
|
||||||
cursor.close();
|
|
||||||
cursor = getCursor(trackPointId);
|
|
||||||
return cursor != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getLocationId() {
|
|
||||||
return lastTrackPointId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
if (cursor == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cursor.isAfterLast()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cursor.isLast()) {
|
|
||||||
if (cursor.getCount() != defaultCursorBatchSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return advanceCursorToNextBatch() && !cursor.isAfterLast();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Location next() {
|
|
||||||
if (cursor == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
if (!cursor.moveToNext()) {
|
|
||||||
if (!advanceCursorToNextBatch() || !cursor.moveToNext()) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTrackPointId = cursor.getLong(indexes.idIndex);
|
|
||||||
Location location = locationFactory.createLocation();
|
|
||||||
fillTrackPoint(cursor, indexes, location);
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
cursor = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts a track point.
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param trackId the track id
|
|
||||||
* @return the content provider URI of the inserted track point
|
|
||||||
*/
|
|
||||||
public Uri insertTrackPoint(Location location, long trackId) {
|
|
||||||
return contentResolver.insert(TrackPointsColumns.CONTENT_URI, createContentValues(location, trackId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the {@link ContentValues} for a {@link Location}.
|
|
||||||
*
|
|
||||||
* @param location the location
|
|
||||||
* @param trackId the track id
|
|
||||||
*/
|
|
||||||
private ContentValues createContentValues(Location location, long trackId) {
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(TrackPointsColumns.TRACKID, trackId);
|
|
||||||
values.put(TrackPointsColumns.LONGITUDE, (int) (location.getLongitude() * 1E6));
|
|
||||||
values.put(TrackPointsColumns.LATITUDE, (int) (location.getLatitude() * 1E6));
|
|
||||||
|
|
||||||
// Hack for Samsung phones that don't properly populate the time field
|
|
||||||
long time = location.getTime();
|
|
||||||
if (time == 0) {
|
|
||||||
time = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
values.put(TrackPointsColumns.TIME, time);
|
|
||||||
if (location.hasAltitude()) {
|
|
||||||
values.put(TrackPointsColumns.ALTITUDE, location.getAltitude());
|
|
||||||
}
|
|
||||||
if (location.hasAccuracy()) {
|
|
||||||
values.put(TrackPointsColumns.ACCURACY, location.getAccuracy());
|
|
||||||
}
|
|
||||||
if (location.hasSpeed()) {
|
|
||||||
values.put(TrackPointsColumns.SPEED, location.getSpeed());
|
|
||||||
}
|
|
||||||
if (location.hasBearing()) {
|
|
||||||
values.put(TrackPointsColumns.BEARING, location.getBearing());
|
|
||||||
}
|
|
||||||
|
|
||||||
//SensorData
|
|
||||||
if (location instanceof SensorDataSetLocation) {
|
|
||||||
SensorDataSetLocation sensorDataSetLocation = (SensorDataSetLocation) location;
|
|
||||||
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
|
||||||
if (sensorDataSet != null && sensorDataSet.hasHeartRate()) {
|
|
||||||
values.put(TrackPointsColumns.SENSOR_HEARTRATE, sensorDataSetLocation.getSensorDataSet().getHeartRate());
|
|
||||||
}
|
|
||||||
if (sensorDataSet != null && sensorDataSet.hasCadence()) {
|
|
||||||
values.put(TrackPointsColumns.SENSOR_CADENCE, sensorDataSetLocation.getSensorDataSet().getCadence());
|
|
||||||
}
|
|
||||||
if (sensorDataSet != null && sensorDataSet.hasPower()) {
|
|
||||||
values.put(TrackPointsColumns.SENSOR_POWER, sensorDataSetLocation.getSensorDataSet().getPower());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills a track point from a cursor.
|
* Fills a track point from a cursor.
|
||||||
*
|
*
|
||||||
@@ -1160,7 +805,7 @@ public class ContentProviderUtils {
|
|||||||
* @param indexes the cached track points indexes
|
* @param indexes the cached track points indexes
|
||||||
* @param location the track point
|
* @param location the track point
|
||||||
*/
|
*/
|
||||||
private void fillTrackPoint(Cursor cursor, CachedTrackPointsIndexes indexes, Location location) {
|
static void fillTrackPoint(Cursor cursor, CachedTrackPointsIndexes indexes, Location location) {
|
||||||
location.reset();
|
location.reset();
|
||||||
|
|
||||||
if (!cursor.isNull(indexes.longitudeIndex)) {
|
if (!cursor.isNull(indexes.longitudeIndex)) {
|
||||||
@@ -1184,8 +829,8 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(indexes.bearingIndex)) {
|
if (!cursor.isNull(indexes.bearingIndex)) {
|
||||||
location.setBearing(cursor.getFloat(indexes.bearingIndex));
|
location.setBearing(cursor.getFloat(indexes.bearingIndex));
|
||||||
}
|
}
|
||||||
if (location instanceof SensorDataSetLocation) {
|
if (location instanceof TrackPoint) {
|
||||||
SensorDataSetLocation sensorDataSetLocation = (SensorDataSetLocation) location;
|
TrackPoint sensorDataSetLocation = (TrackPoint) location;
|
||||||
|
|
||||||
float heartRate = cursor.isNull(indexes.sensorHeartRateIndex) ? SensorDataSet.DATA_UNAVAILABLE : cursor.getFloat(indexes.sensorHeartRateIndex);
|
float heartRate = cursor.isNull(indexes.sensorHeartRateIndex) ? SensorDataSet.DATA_UNAVAILABLE : cursor.getFloat(indexes.sensorHeartRateIndex);
|
||||||
float cadence = cursor.isNull(indexes.sensorCadenceIndex) ? SensorDataSet.DATA_UNAVAILABLE : cursor.getFloat(indexes.sensorCadenceIndex);
|
float cadence = cursor.isNull(indexes.sensorCadenceIndex) ? SensorDataSet.DATA_UNAVAILABLE : cursor.getFloat(indexes.sensorCadenceIndex);
|
||||||
@@ -1195,6 +840,76 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a track point.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param trackId the track id
|
||||||
|
* @return the content provider URI of the inserted track point
|
||||||
|
*/
|
||||||
|
public Uri insertTrackPoint(Location location, long trackId) {
|
||||||
|
return contentResolver.insert(TrackPointsColumns.CONTENT_URI_BY_ID, createContentValues(location, trackId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link ContentValues} for a {@link Location}.
|
||||||
|
*
|
||||||
|
* @param location the location
|
||||||
|
* @param trackId the track id
|
||||||
|
*/
|
||||||
|
private ContentValues createContentValues(Location location, long trackId) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(TrackPointsColumns.TRACKID, trackId);
|
||||||
|
values.put(TrackPointsColumns.LONGITUDE, (int) (location.getLongitude() * 1E6));
|
||||||
|
values.put(TrackPointsColumns.LATITUDE, (int) (location.getLatitude() * 1E6));
|
||||||
|
|
||||||
|
values.put(TrackPointsColumns.TIME, location.getTime());
|
||||||
|
if (location.hasAltitude()) {
|
||||||
|
values.put(TrackPointsColumns.ALTITUDE, location.getAltitude());
|
||||||
|
}
|
||||||
|
if (location.hasAccuracy()) {
|
||||||
|
values.put(TrackPointsColumns.ACCURACY, location.getAccuracy());
|
||||||
|
}
|
||||||
|
if (location.hasSpeed()) {
|
||||||
|
values.put(TrackPointsColumns.SPEED, location.getSpeed());
|
||||||
|
}
|
||||||
|
if (location.hasBearing()) {
|
||||||
|
values.put(TrackPointsColumns.BEARING, location.getBearing());
|
||||||
|
}
|
||||||
|
|
||||||
|
//SensorData
|
||||||
|
if (location instanceof TrackPoint) {
|
||||||
|
TrackPoint sensorDataSetLocation = (TrackPoint) location;
|
||||||
|
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
||||||
|
if (sensorDataSet != null && sensorDataSet.hasHeartRate()) {
|
||||||
|
values.put(TrackPointsColumns.SENSOR_HEARTRATE, sensorDataSetLocation.getSensorDataSet().getHeartRate());
|
||||||
|
}
|
||||||
|
if (sensorDataSet != null && sensorDataSet.hasCadence()) {
|
||||||
|
values.put(TrackPointsColumns.SENSOR_CADENCE, sensorDataSetLocation.getSensorDataSet().getCadence());
|
||||||
|
}
|
||||||
|
if (sensorDataSet != null && sensorDataSet.hasPower()) {
|
||||||
|
values.put(TrackPointsColumns.SENSOR_POWER, sensorDataSetLocation.getSensorDataSet().getPower());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new read-only iterator over a given track's points.
|
||||||
|
* It provides a lightweight way of iterating over long tracks without failing due to the underlying cursor limitations.
|
||||||
|
* Since it's a read-only iterator, {@link Iterator#remove()} always throws {@link UnsupportedOperationException}.
|
||||||
|
* Each call to {@link TrackPointIterator#next()} may advance to the next DB record, and if so, the iterator calls {@link TrackPointFactory#createLocation()} and populates it with information retrieved from the record.
|
||||||
|
* When done with iteration, {@link TrackPointIterator#close()} must be called.
|
||||||
|
*
|
||||||
|
* @param trackId the track id
|
||||||
|
* @param startTrackPointId the starting track point id. -1L to ignore
|
||||||
|
* @param descending true to sort the result in descending order (latest location first)
|
||||||
|
* @param trackPointFactory the location factory
|
||||||
|
*/
|
||||||
|
public TrackPointIterator getTrackPointLocationIterator(final long trackId, final long startTrackPointId, final boolean descending, final TrackPointFactory trackPointFactory) {
|
||||||
|
return new TrackPointIterator(this, trackId, startTrackPointId, descending, trackPointFactory);
|
||||||
|
}
|
||||||
|
|
||||||
private Location findTrackPointBy(String selection, String[] selectionArgs) {
|
private Location findTrackPointBy(String selection, String[] selectionArgs) {
|
||||||
try (Cursor cursor = getTrackPointCursor(null, selection, selectionArgs, TrackPointsColumns._ID)) {
|
try (Cursor cursor = getTrackPointCursor(null, selection, selectionArgs, TrackPointsColumns._ID)) {
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
@@ -1213,7 +928,11 @@ public class ContentProviderUtils {
|
|||||||
* @param sortOrder the sort order
|
* @param sortOrder the sort order
|
||||||
*/
|
*/
|
||||||
private Cursor getTrackPointCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
private Cursor getTrackPointCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
return contentResolver.query(TrackPointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
|
return contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, projection, selection, selectionArgs, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDefaultCursorBatchSize() {
|
||||||
|
return defaultCursorBatchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1227,33 +946,24 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of track points indexes.
|
* Formats an array of IDs as comma separated string value
|
||||||
|
*
|
||||||
|
* @param ids array with IDs
|
||||||
|
* @return comma separated list of ids
|
||||||
*/
|
*/
|
||||||
private static class CachedTrackPointsIndexes {
|
public static String formatIdListForUri(long[] ids) {
|
||||||
final int idIndex;
|
StringBuilder idsPathSegment = new StringBuilder();
|
||||||
final int longitudeIndex;
|
for (long id : ids) {
|
||||||
final int latitudeIndex;
|
if (idsPathSegment.length() > 0) {
|
||||||
final int timeIndex;
|
idsPathSegment.append(ID_SEPARATOR);
|
||||||
final int altitudeIndex;
|
}
|
||||||
final int accuracyIndex;
|
idsPathSegment.append(id);
|
||||||
final int speedIndex;
|
|
||||||
final int bearingIndex;
|
|
||||||
final int sensorHeartRateIndex;
|
|
||||||
final int sensorCadenceIndex;
|
|
||||||
final int sensorPowerIndex;
|
|
||||||
|
|
||||||
CachedTrackPointsIndexes(Cursor cursor) {
|
|
||||||
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
|
|
||||||
longitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
|
|
||||||
latitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
|
|
||||||
timeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
|
|
||||||
altitudeIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
|
|
||||||
accuracyIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
|
|
||||||
speedIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
|
|
||||||
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
|
||||||
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
|
||||||
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
|
||||||
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
|
||||||
}
|
}
|
||||||
|
return idsPathSegment.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String[] parseTrackIdsFromUri(Uri url) {
|
||||||
|
return TextUtils.split(url.getLastPathSegment(), ID_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+55
-62
@@ -14,7 +14,7 @@
|
|||||||
* the License.
|
* the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentUris;
|
import android.content.ContentUris;
|
||||||
@@ -25,7 +25,6 @@ import android.database.Cursor;
|
|||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.database.sqlite.SQLiteQueryBuilder;
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -34,6 +33,10 @@ import android.util.Log;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ContentProvider} that handles access to track points, tracks, and waypoints tables.
|
* A {@link ContentProvider} that handles access to track points, tracks, and waypoints tables.
|
||||||
*
|
*
|
||||||
@@ -41,26 +44,25 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
*/
|
*/
|
||||||
public abstract class CustomContentProvider extends ContentProvider {
|
public abstract class CustomContentProvider extends ContentProvider {
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int DATABASE_VERSION = 23;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String DATABASE_NAME = "database.db";
|
|
||||||
|
|
||||||
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String SQL_LIST_DELIMITER = ",";
|
||||||
|
|
||||||
private final UriMatcher uriMatcher;
|
private final UriMatcher uriMatcher;
|
||||||
|
|
||||||
private SQLiteDatabase db;
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
public CustomContentProvider() {
|
public CustomContentProvider() {
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath(), UrlType.TRACKPOINTS.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_ID.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath() + "/#", UrlType.TRACKPOINTS_BY_ID.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME, UrlType.TRACKS.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.TRACKPOINTS_BY_TRACKID.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/#", UrlType.TRACKS_ID.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.TABLE_NAME, UrlType.WAYPOINTS.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_ID.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal());
|
||||||
|
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath(), UrlType.WAYPOINTS.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath() + "/#", UrlType.WAYPOINTS_BY_ID.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -76,7 +78,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean onCreate(Context context) {
|
boolean onCreate(Context context) {
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
CustomSQLiteOpenHelper databaseHelper = new CustomSQLiteOpenHelper(context);
|
||||||
try {
|
try {
|
||||||
db = databaseHelper.getWritableDatabase();
|
db = databaseHelper.getWritableDatabase();
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
@@ -128,15 +130,16 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS:
|
||||||
return TrackPointsColumns.CONTENT_TYPE;
|
return TrackPointsColumns.CONTENT_TYPE;
|
||||||
case TRACKPOINTS_ID:
|
case TRACKPOINTS_BY_ID:
|
||||||
|
case TRACKPOINTS_BY_TRACKID:
|
||||||
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||||
case TRACKS:
|
case TRACKS:
|
||||||
return TracksColumns.CONTENT_TYPE;
|
return TracksColumns.CONTENT_TYPE;
|
||||||
case TRACKS_ID:
|
case TRACKS_BY_ID:
|
||||||
return TracksColumns.CONTENT_ITEMTYPE;
|
return TracksColumns.CONTENT_ITEMTYPE;
|
||||||
case WAYPOINTS:
|
case WAYPOINTS:
|
||||||
return WaypointsColumns.CONTENT_TYPE;
|
return WaypointsColumns.CONTENT_TYPE;
|
||||||
case WAYPOINTS_ID:
|
case WAYPOINTS_BY_ID:
|
||||||
return WaypointsColumns.CONTENT_ITEMTYPE;
|
return WaypointsColumns.CONTENT_ITEMTYPE;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
@@ -192,25 +195,29 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
break;
|
||||||
case TRACKPOINTS_ID:
|
case TRACKPOINTS_BY_ID:
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
|
break;
|
||||||
|
case TRACKPOINTS_BY_TRACKID:
|
||||||
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
|
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
break;
|
||||||
case TRACKS:
|
case TRACKS:
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
break;
|
||||||
case TRACKS_ID:
|
case TRACKS_BY_ID:
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
break;
|
||||||
case WAYPOINTS:
|
case WAYPOINTS:
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : WaypointsColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : WaypointsColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
break;
|
||||||
case WAYPOINTS_ID:
|
case WAYPOINTS_BY_ID:
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere("_id=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(WaypointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
throw new IllegalArgumentException("Unknown url " + url);
|
||||||
@@ -222,6 +229,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int update(@NonNull Uri url, ContentValues values, String where, String[] selectionArgs) {
|
public int update(@NonNull Uri url, ContentValues values, String where, String[] selectionArgs) {
|
||||||
|
// TODO Use SQLiteQueryBuilder
|
||||||
String table;
|
String table;
|
||||||
String whereClause;
|
String whereClause;
|
||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
@@ -229,9 +237,9 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
break;
|
||||||
case TRACKPOINTS_ID:
|
case TRACKPOINTS_BY_ID:
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = TrackPointsColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -240,9 +248,9 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
break;
|
||||||
case TRACKS_ID:
|
case TRACKS_BY_ID:
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = TracksColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -251,9 +259,9 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
table = WaypointsColumns.TABLE_NAME;
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
break;
|
||||||
case WAYPOINTS_ID:
|
case WAYPOINTS_BY_ID:
|
||||||
table = WaypointsColumns.TABLE_NAME;
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
whereClause = WaypointsColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = WaypointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -278,8 +286,15 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*
|
*
|
||||||
* @param url the url
|
* @param url the url
|
||||||
*/
|
*/
|
||||||
|
@NonNull
|
||||||
private UrlType getUrlType(Uri url) {
|
private UrlType getUrlType(Uri url) {
|
||||||
return UrlType.values()[uriMatcher.match(url)];
|
UrlType[] urlTypes = UrlType.values();
|
||||||
|
int matchIndex = uriMatcher.match(url);
|
||||||
|
if (0 <= matchIndex && matchIndex < urlTypes.length) {
|
||||||
|
return urlTypes[matchIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,7 +332,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
||||||
if (rowId >= 0) {
|
if (rowId >= 0) {
|
||||||
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_ID.buildUpon(), rowId).build();
|
||||||
}
|
}
|
||||||
throw new SQLiteException("Failed to insert a track point " + url);
|
throw new SQLiteException("Failed to insert a track point " + url);
|
||||||
}
|
}
|
||||||
@@ -330,8 +345,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*/
|
*/
|
||||||
private Uri insertTrack(Uri url, ContentValues contentValues) {
|
private Uri insertTrack(Uri url, ContentValues contentValues) {
|
||||||
boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME);
|
boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME);
|
||||||
boolean hasStartId = contentValues.containsKey(TracksColumns.STARTID);
|
if (!hasStartTime) {
|
||||||
if (!hasStartTime || !hasStartId) {
|
|
||||||
throw new IllegalArgumentException("Both start time and start id values are required.");
|
throw new IllegalArgumentException("Both start time and start id values are required.");
|
||||||
}
|
}
|
||||||
long rowId = db.insert(TracksColumns.TABLE_NAME, TracksColumns._ID, contentValues);
|
long rowId = db.insert(TracksColumns.TABLE_NAME, TracksColumns._ID, contentValues);
|
||||||
@@ -362,33 +376,12 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
enum UrlType {
|
enum UrlType {
|
||||||
TRACKPOINTS, TRACKPOINTS_ID, TRACKS, TRACKS_ID, WAYPOINTS, WAYPOINTS_ID
|
TRACKPOINTS,
|
||||||
}
|
TRACKPOINTS_BY_ID,
|
||||||
|
TRACKPOINTS_BY_TRACKID,
|
||||||
/**
|
TRACKS,
|
||||||
* Database helper for creating and upgrading the database.
|
TRACKS_BY_ID,
|
||||||
*/
|
WAYPOINTS,
|
||||||
@VisibleForTesting
|
WAYPOINTS_BY_ID
|
||||||
static class DatabaseHelper extends SQLiteOpenHelper {
|
|
||||||
|
|
||||||
public DatabaseHelper(Context context) {
|
|
||||||
this(context, DATABASE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public DatabaseHelper(Context context, String databaseName) {
|
|
||||||
super(context, databaseName, null, DATABASE_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(SQLiteDatabase db) {
|
|
||||||
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
|
||||||
db.execSQL(TracksColumns.CREATE_TABLE);
|
|
||||||
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database helper for creating and upgrading the database.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final int DATABASE_VERSION = 23;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String DATABASE_NAME = "database.db";
|
||||||
|
|
||||||
|
public CustomSQLiteOpenHelper(Context context) {
|
||||||
|
this(context, DATABASE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public CustomSQLiteOpenHelper(Context context, String databaseName) {
|
||||||
|
super(context, databaseName, null, DATABASE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
||||||
|
db.execSQL(TracksColumns.CREATE_TABLE);
|
||||||
|
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-2
@@ -1,4 +1,4 @@
|
|||||||
package de.dennisguse.opentracks.content;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.UriMatcher;
|
import android.content.UriMatcher;
|
||||||
@@ -21,6 +21,8 @@ import java.io.IOException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.android.IContentResolver;
|
import de.dennisguse.opentracks.android.IContentResolver;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
|
|||||||
}
|
}
|
||||||
builder.deleteCharAt(builder.lastIndexOf(TRACKID_DELIMITER));
|
builder.deleteCharAt(builder.lastIndexOf(TRACKID_DELIMITER));
|
||||||
|
|
||||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TracksColumns.TABLE_NAME + "/" + trackFileFormat.getName() + "/" + builder + "." + trackFileFormat.getExtension());
|
Uri uri = Uri.parse(TracksColumns.CONTENT_URI + "/" + trackFileFormat.getName() + "/" + builder + "." + trackFileFormat.getExtension());
|
||||||
String mime = getTypeMime(uri);
|
String mime = getTypeMime(uri);
|
||||||
|
|
||||||
Log.d(TAG, "Created uri " + uri.toString() + " with MIME " + mime);
|
Log.d(TAG, "Created uri " + uri.toString() + " with MIME " + mime);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.location.LocationManager;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link TrackPoint}.
|
||||||
|
* An implementation can create new instances or reuse existing instances for optimization.
|
||||||
|
*/
|
||||||
|
public class TrackPointFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default {@link TrackPointFactory} which creates a location each time.
|
||||||
|
*/
|
||||||
|
public static final TrackPointFactory DEFAULT_LOCATION_FACTORY = new TrackPointFactory();
|
||||||
|
|
||||||
|
public TrackPoint createLocation() {
|
||||||
|
return new TrackPoint(LocationManager.GPS_PROVIDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A lightweight wrapper around the original {@link Cursor} with a method to clean up.
|
||||||
|
*/
|
||||||
|
public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||||
|
|
||||||
|
private static final String TAG = TrackPointIterator.class.getSimpleName();
|
||||||
|
|
||||||
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
private final long trackId;
|
||||||
|
private final boolean descending;
|
||||||
|
private final TrackPointFactory trackPointFactory;
|
||||||
|
private final CachedTrackPointsIndexes indexes;
|
||||||
|
private long lastTrackPointId = -1L;
|
||||||
|
private Cursor cursor;
|
||||||
|
|
||||||
|
|
||||||
|
public TrackPointIterator(ContentProviderUtils contentProviderUtils, long trackId, long startTrackPointId, boolean descending, TrackPointFactory trackPointFactory) {
|
||||||
|
if (trackPointFactory == null) {
|
||||||
|
throw new IllegalArgumentException("trackPointFactory is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
|
this.trackId = trackId;
|
||||||
|
this.descending = descending;
|
||||||
|
this.trackPointFactory = trackPointFactory;
|
||||||
|
|
||||||
|
cursor = getCursor(startTrackPointId);
|
||||||
|
indexes = cursor != null ? new CachedTrackPointsIndexes(cursor)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the track point cursor.
|
||||||
|
*
|
||||||
|
* @param trackPointId the starting track point id
|
||||||
|
*/
|
||||||
|
private Cursor getCursor(long trackPointId) {
|
||||||
|
return contentProviderUtils.getTrackPointCursor(trackId, trackPointId, contentProviderUtils.getDefaultCursorBatchSize(), descending);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances the cursor to the next batch. Returns true if successful.
|
||||||
|
*/
|
||||||
|
private boolean advanceCursorToNextBatch() {
|
||||||
|
long trackPointId = lastTrackPointId == -1L ? -1L : lastTrackPointId + (descending ? -1 : 1);
|
||||||
|
Log.d(TAG, "Advancing track point id: " + trackPointId);
|
||||||
|
cursor.close();
|
||||||
|
cursor = getCursor(trackPointId);
|
||||||
|
return cursor != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTrackPointId() {
|
||||||
|
return lastTrackPointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (cursor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.isAfterLast()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (cursor.isLast()) {
|
||||||
|
if (cursor.getCount() != contentProviderUtils.getDefaultCursorBatchSize()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return advanceCursorToNextBatch() && !cursor.isAfterLast();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TrackPoint next() {
|
||||||
|
if (cursor == null) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
if (!cursor.moveToNext()) {
|
||||||
|
if (!advanceCursorToNextBatch() || !cursor.moveToNext()) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTrackPointId = cursor.getLong(indexes.idIndex);
|
||||||
|
TrackPoint trackPoint = trackPointFactory.createLocation();
|
||||||
|
ContentProviderUtils.fillTrackPoint(cursor, indexes, trackPoint);
|
||||||
|
return trackPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
cursor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@ public enum SensorState {
|
|||||||
case CONNECTED:
|
case CONNECTED:
|
||||||
return c.getString(R.string.sensor_state_connected);
|
return c.getString(R.string.sensor_state_connected);
|
||||||
case DISCONNECTING:
|
case DISCONNECTING:
|
||||||
return c.getString(R.string.sensor_state_disconnected);
|
|
||||||
case DISCONNECTED:
|
case DISCONNECTED:
|
||||||
return c.getString(R.string.sensor_state_disconnected);
|
return c.getString(R.string.sensor_state_disconnected);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.fragments;
|
|
||||||
|
|
||||||
public class ChartDistanceFragment extends ChartFragment {
|
|
||||||
|
|
||||||
public static final String CHART_FRAGMENT_TAG = ChartDistanceFragment.class.getSimpleName();
|
|
||||||
|
|
||||||
public ChartDistanceFragment() {
|
|
||||||
super(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.fragments;
|
package de.dennisguse.opentracks.fragments;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -24,7 +25,6 @@ import android.view.ViewGroup;
|
|||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
@@ -36,12 +36,12 @@ import java.util.List;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||||
import de.dennisguse.opentracks.chart.ChartView;
|
import de.dennisguse.opentracks.chart.ChartView;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
|
||||||
import de.dennisguse.opentracks.content.Track;
|
|
||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||||
import de.dennisguse.opentracks.content.TrackDataType;
|
import de.dennisguse.opentracks.content.TrackDataType;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
||||||
@@ -55,9 +55,18 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public abstract class ChartFragment extends Fragment implements TrackDataListener {
|
public class ChartFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
private static final String STATE_CHART_VIEW_BY_DISTANCE_KEY = "chartViewByDistance";
|
private static final String KEY_CHART_VIEW_BY_DISTANCE_KEY = "chartViewByDistance";
|
||||||
|
|
||||||
|
public static Fragment newInstance(boolean chartByDistance) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(KEY_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
||||||
|
|
||||||
|
ChartFragment chartFragment = new ChartFragment();
|
||||||
|
chartFragment.setArguments(bundle);
|
||||||
|
return chartFragment;
|
||||||
|
}
|
||||||
|
|
||||||
private final List<double[]> pendingPoints = new ArrayList<>();
|
private final List<double[]> pendingPoints = new ArrayList<>();
|
||||||
|
|
||||||
@@ -73,11 +82,56 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
|
|
||||||
// Modes of operation
|
// Modes of operation
|
||||||
private boolean chartByDistance;
|
private boolean chartByDistance;
|
||||||
private boolean[] chartShow = new boolean[]{true, true, true, true, true, true};
|
private final boolean[] chartShow = new boolean[]{true, true, true, true, true, true};
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
private ChartView chartView;
|
private ChartView chartView;
|
||||||
|
|
||||||
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
|
if (!isResumed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||||
|
boolean metric = PreferencesUtils.isMetricUnits(getContext());
|
||||||
|
if (metricUnits != metric) {
|
||||||
|
metricUnits = metric;
|
||||||
|
chartView.setMetricUnits(metricUnits);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||||
|
boolean speed = PreferencesUtils.isReportSpeed(getContext());
|
||||||
|
if (reportSpeed != speed) {
|
||||||
|
reportSpeed = speed;
|
||||||
|
chartView.setReportSpeed(reportSpeed);
|
||||||
|
setSeriesEnabled(ChartView.SPEED_SERIES, reportSpeed);
|
||||||
|
setSeriesEnabled(ChartView.PACE_SERIES, !reportSpeed);
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
chartView.requestLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PreferencesUtils.isKey(getContext(), R.string.recording_distance_interval_key, key)) {
|
||||||
|
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(getContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable that will set the orange pointer as appropriate and redraw.
|
* A runnable that will set the orange pointer as appropriate and redraw.
|
||||||
*/
|
*/
|
||||||
@@ -93,14 +147,13 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public ChartFragment(boolean chartByDistance) {
|
|
||||||
this.chartByDistance = chartByDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
chartByDistance = getArguments().getBoolean(KEY_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
||||||
|
|
||||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceIntervalDefault(getContext());
|
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceIntervalDefault(getContext());
|
||||||
|
|
||||||
// Create a chartView here to store data thus won't need to reload all the data on every onStart or onResume.
|
// Create a chartView here to store data thus won't need to reload all the data on every onStart or onResume.
|
||||||
@@ -124,6 +177,8 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
resumeTrackDataHub();
|
resumeTrackDataHub();
|
||||||
|
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
||||||
|
|
||||||
checkChartSettings();
|
checkChartSettings();
|
||||||
getActivity().runOnUiThread(updateChart);
|
getActivity().runOnUiThread(updateChart);
|
||||||
}
|
}
|
||||||
@@ -132,6 +187,7 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
pauseTrackDataHub();
|
pauseTrackDataHub();
|
||||||
|
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -141,20 +197,6 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
layout.removeView(chartView);
|
layout.removeView(chartView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putBoolean(STATE_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewStateRestored(savedInstanceState);
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
chartByDistance = savedInstanceState.getBoolean(STATE_CHART_VIEW_BY_DISTANCE_KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrackUpdated(Track track) {
|
public void onTrackUpdated(Track track) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
@@ -229,68 +271,6 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMetricUnitsChanged(boolean metric) {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (metricUnits == metric) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
metricUnits = metric;
|
|
||||||
chartView.setMetricUnits(metricUnits);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
chartView.requestLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onReportSpeedChanged(boolean speed) {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (reportSpeed == speed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
reportSpeed = speed;
|
|
||||||
chartView.setReportSpeed(reportSpeed);
|
|
||||||
setSeriesEnabled(ChartView.SPEED_SERIES, reportSpeed);
|
|
||||||
setSeriesEnabled(ChartView.PACE_SERIES, !reportSpeed);
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
chartView.requestLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingGpsAccuracy(int minRequiredAccuracy) {
|
|
||||||
// We don't care.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingDistanceIntervalChanged(int value) {
|
|
||||||
if (isResumed()) {
|
|
||||||
if (recordingDistanceInterval == value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
recordingDistanceInterval = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the chart settings.
|
* Checks the chart settings.
|
||||||
*/
|
*/
|
||||||
@@ -334,7 +314,7 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||||
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
TrackDataType.WAYPOINTS_TABLE, TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE,
|
||||||
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE, TrackDataType.PREFERENCE));
|
TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -424,8 +404,8 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
double heartRate = Double.NaN;
|
double heartRate = Double.NaN;
|
||||||
double cadence = Double.NaN;
|
double cadence = Double.NaN;
|
||||||
double power = Double.NaN;
|
double power = Double.NaN;
|
||||||
if (location instanceof SensorDataSetLocation && ((SensorDataSetLocation) location).getSensorDataSet() != null) {
|
if (location instanceof TrackPoint && ((TrackPoint) location).getSensorDataSet() != null) {
|
||||||
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
SensorDataSet sensorDataSet = ((TrackPoint) location).getSensorDataSet();
|
||||||
if (sensorDataSet.hasHeartRate()) {
|
if (sensorDataSet.hasHeartRate()) {
|
||||||
heartRate = sensorDataSet.getHeartRate();
|
heartRate = sensorDataSet.getHeartRate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.fragments;
|
|
||||||
|
|
||||||
public class ChartTimeFragment extends ChartFragment {
|
|
||||||
|
|
||||||
public static final String CHART_FRAGMENT_TAG = ChartTimeFragment.class.getSimpleName();
|
|
||||||
|
|
||||||
public ChartTimeFragment() {
|
|
||||||
super(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-2
@@ -39,7 +39,7 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
|||||||
return TrackIconUtils.getAllIconValues().indexOf(iconValue);
|
return TrackIconUtils.getAllIconValues().indexOf(iconValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String preselectedCategory;
|
private final String preselectedCategory;
|
||||||
|
|
||||||
private ChooseActivityTypeCaller chooseActivityTypeCaller;
|
private ChooseActivityTypeCaller chooseActivityTypeCaller;
|
||||||
|
|
||||||
@@ -71,7 +71,6 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
|||||||
int position = getPosition(getContext(), preselectedCategory);
|
int position = getPosition(getContext(), preselectedCategory);
|
||||||
if (position != -1) {
|
if (position != -1) {
|
||||||
imageAdapter.setSelected(position);
|
imageAdapter.setSelected(position);
|
||||||
imageAdapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gridView.setOnItemClickListener(this);
|
gridView.setOnItemClickListener(this);
|
||||||
|
|||||||
+1
-4
@@ -56,12 +56,9 @@ public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSelected() {
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSelected(int position) {
|
public void setSelected(int position) {
|
||||||
selected = position;
|
selected = position;
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,12 +36,9 @@ import de.dennisguse.opentracks.util.DialogUtils;
|
|||||||
public class ConfirmDeleteDialogFragment extends DialogFragment {
|
public class ConfirmDeleteDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
private static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
||||||
private ConfirmDeleteCaller caller;
|
private static final String KEY_TRACK_IDS = "trackIds";
|
||||||
private long[] trackIds;
|
|
||||||
|
|
||||||
public ConfirmDeleteDialogFragment(long[] trackIds) {
|
private ConfirmDeleteCaller caller;
|
||||||
this.trackIds = trackIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
@@ -49,7 +46,12 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
|||||||
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
|
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
|
||||||
*/
|
*/
|
||||||
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
|
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
|
||||||
new ConfirmDeleteDialogFragment(trackIds).show(fragmentManager, CONFIRM_DELETE_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
||||||
|
|
||||||
|
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
||||||
|
deleteTrackDialogFragment.setArguments(bundle);
|
||||||
|
deleteTrackDialogFragment.show(fragmentManager, CONFIRM_DELETE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,6 +67,8 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||||
|
|
||||||
int titleId;
|
int titleId;
|
||||||
int messageId;
|
int messageId;
|
||||||
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
|
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.DescriptionGeneratorImpl;
|
|
||||||
import de.dennisguse.opentracks.util.DialogUtils;
|
import de.dennisguse.opentracks.util.DialogUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,16 +38,17 @@ import de.dennisguse.opentracks.util.DialogUtils;
|
|||||||
public class DeleteMarkerDialogFragment extends DialogFragment {
|
public class DeleteMarkerDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
private static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
||||||
private long[] markerIds;
|
private static final String KEY_MARKER_IDS = "markerIds";
|
||||||
|
|
||||||
private DeleteMarkerCaller caller;
|
private DeleteMarkerCaller caller;
|
||||||
|
|
||||||
public DeleteMarkerDialogFragment(long[] markerIds) {
|
|
||||||
this.markerIds = markerIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
|
public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
|
||||||
new DeleteMarkerDialogFragment(markerIds).show(fragmentManager, DELETE_MARKER_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
|
||||||
|
|
||||||
|
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
|
||||||
|
deleteMarkerDialogFragment.setArguments(bundle);
|
||||||
|
deleteMarkerDialogFragment.show(fragmentManager, DELETE_MARKER_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,6 +64,8 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
|
||||||
|
|
||||||
final FragmentActivity fragmentActivity = getActivity();
|
final FragmentActivity fragmentActivity = getActivity();
|
||||||
int titleId;
|
int titleId;
|
||||||
int messageId;
|
int messageId;
|
||||||
@@ -83,8 +85,7 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
|
|||||||
public void run() {
|
public void run() {
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
|
||||||
for (long markerId : markerIds) {
|
for (long markerId : markerIds) {
|
||||||
contentProviderUtils.deleteWaypoint(fragmentActivity,
|
contentProviderUtils.deleteWaypoint(markerId);
|
||||||
markerId, new DescriptionGeneratorImpl(fragmentActivity));
|
|
||||||
}
|
}
|
||||||
caller.onDeleteMarkerDone();
|
caller.onDeleteMarkerDone();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import java.util.Locale;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DialogFragment to select a file type, gpx, kml, etc.
|
* A DialogFragment to select a file type, gpx, kml, etc.
|
||||||
@@ -41,18 +40,19 @@ import de.dennisguse.opentracks.util.FileUtils;
|
|||||||
public class FileTypeDialogFragment extends DialogFragment {
|
public class FileTypeDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
private static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||||
private int titleId;
|
private static final String KEY_TITLE_ID = "titleId";
|
||||||
|
private static final String KEY_OPTION_ID = "optionId";
|
||||||
|
|
||||||
private FileTypeCaller caller;
|
private FileTypeCaller caller;
|
||||||
private int optionId;
|
|
||||||
|
|
||||||
public FileTypeDialogFragment(int titleId, int optionId) {
|
|
||||||
this.titleId = titleId;
|
|
||||||
this.optionId = optionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
|
public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
|
||||||
new FileTypeDialogFragment(titleId, optionId).show(fragmentManager, FILE_TYPE_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||||
|
bundle.putInt(KEY_OPTION_ID, optionId);
|
||||||
|
|
||||||
|
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
|
||||||
|
fileTypeDialogFragment.setArguments(bundle);
|
||||||
|
fileTypeDialogFragment.show(fragmentManager, FILE_TYPE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -68,12 +68,17 @@ public class FileTypeDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
|
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
||||||
|
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||||
|
|
||||||
|
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
|
||||||
String[] choices = new String[trackFileFormats.length];
|
String[] choices = new String[trackFileFormats.length];
|
||||||
for (int i = 0; i < choices.length; i++) {
|
for (int i = 0; i < choices.length; i++) {
|
||||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||||
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
|
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
|
||||||
choices[i] = getString(optionId, trackFileFormatUpperCase, FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
|
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
|
||||||
|
String trackFileFormatDisplayName = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
|
||||||
|
choices[i] = getString(optionId, trackFileFormatDisplayName);
|
||||||
}
|
}
|
||||||
return new AlertDialog.Builder(getActivity())
|
return new AlertDialog.Builder(getActivity())
|
||||||
.setNegativeButton(R.string.generic_cancel, new OnClickListener() {
|
.setNegativeButton(R.string.generic_cancel, new OnClickListener() {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.MarkerEditActivity;
|
import de.dennisguse.opentracks.MarkerEditActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||||
@@ -57,14 +57,16 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
|
|
||||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||||
private static final String KEY_MARKER_ID = "markerId";
|
private static final String KEY_MARKER_ID = "markerId";
|
||||||
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND;
|
|
||||||
|
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND_MS;
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private ImageView photoView;
|
private ImageView photoView;
|
||||||
private ImageView textGradient;
|
private ImageView textGradient;
|
||||||
private LinearLayout waypointInfo;
|
private LinearLayout waypointInfo;
|
||||||
private Waypoint waypoint;
|
private Waypoint waypoint;
|
||||||
private Runnable hideText = new Runnable() {
|
private final Runnable hideText = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
|
Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.fadeout);
|
||||||
@@ -219,7 +221,7 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
photoView.setImageResource(MarkerUtils.ICON_ID);
|
photoView.setImageResource(MarkerUtils.ICON_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(R.id.marker_detail_waypoint_name, hasPhoto);
|
setName(hasPhoto);
|
||||||
|
|
||||||
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
TextView category = getView().findViewById(R.id.marker_detail_waypoint_category);
|
||||||
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
ListItemUtils.setTextView(getActivity(), category, StringUtils.getCategory(waypoint.getCategory()), hasPhoto);
|
||||||
@@ -227,16 +229,16 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
TextView description = getView().findViewById(R.id.marker_detail_waypoint_description);
|
||||||
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
ListItemUtils.setTextView(getActivity(), description, waypoint.getDescription(), hasPhoto);
|
||||||
|
|
||||||
setLocation(R.id.marker_detail_waypoint_location, hasPhoto);
|
setLocation(hasPhoto);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setName(int resId, boolean addShadow) {
|
private void setName(boolean addShadow) {
|
||||||
TextView textView = getView().findViewById(resId);
|
TextView textView = getView().findViewById(R.id.marker_detail_waypoint_name);
|
||||||
ListItemUtils.setTextView(getActivity(), textView, waypoint.getName(), addShadow);
|
ListItemUtils.setTextView(getActivity(), textView, waypoint.getName(), addShadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setLocation(int resId, boolean addShadow) {
|
private void setLocation(boolean addShadow) {
|
||||||
TextView textView = getView().findViewById(resId);
|
TextView textView = getView().findViewById(R.id.marker_detail_waypoint_location);
|
||||||
Location location = waypoint.getLocation();
|
Location location = waypoint.getLocation();
|
||||||
String value;
|
String value;
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.fragments;
|
package de.dennisguse.opentracks.fragments;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -37,14 +38,14 @@ import java.util.EnumSet;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
|
||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||||
import de.dennisguse.opentracks.content.TrackDataType;
|
import de.dennisguse.opentracks.content.TrackDataType;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
||||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
@@ -61,9 +62,9 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
*/
|
*/
|
||||||
public class StatsFragment extends Fragment implements TrackDataListener {
|
public class StatsFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
public static final String STATS_FRAGMENT_TAG = StatsFragment.class.getSimpleName();
|
private static final String STATS_FRAGMENT_TAG = StatsFragment.class.getSimpleName();
|
||||||
|
|
||||||
private static final long UI_UPDATE_INTERVAL = UnitConversions.ONE_SECOND;
|
private static final long UI_UPDATE_INTERVAL = UnitConversions.ONE_SECOND_MS;
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
private Handler handlerUpdateUI;
|
private Handler handlerUpdateUI;
|
||||||
@@ -75,8 +76,30 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
private TripStatistics lastTripStatistics = null;
|
private TripStatistics lastTripStatistics = null;
|
||||||
|
|
||||||
private String category = "";
|
private String category = "";
|
||||||
|
@Deprecated //TODO This should be handled somewhere else; not in the UI.
|
||||||
private int recordingGpsAccuracy;
|
private int recordingGpsAccuracy;
|
||||||
|
|
||||||
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||||
|
if (isResumed()) {
|
||||||
|
getActivity().runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (isResumed()) {
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (PreferencesUtils.isKey(getContext(), R.string.recording_track_id_key, key)) {
|
||||||
|
recordingGpsAccuracy = PreferencesUtils.getRecordingGPSAccuracy(getContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.stats, container, false);
|
return inflater.inflate(R.layout.stats, container, false);
|
||||||
@@ -221,6 +244,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
resumeTrackDataHub();
|
resumeTrackDataHub();
|
||||||
|
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(getContext(), null);
|
||||||
trackRecordingServiceConnection.startConnection(getContext());
|
trackRecordingServiceConnection.startConnection(getContext());
|
||||||
@@ -232,6 +256,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
pauseTrackDataHub();
|
pauseTrackDataHub();
|
||||||
|
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
|
||||||
|
|
||||||
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
handlerUpdateUI.removeCallbacks(updateUIeachSecond);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +265,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
if (trackRecordingServiceConnection != null) {
|
if (trackRecordingServiceConnection != null) {
|
||||||
trackRecordingServiceConnection.unbind();
|
trackRecordingServiceConnection.unbind(getContext());
|
||||||
}
|
}
|
||||||
trackRecordingServiceConnection = null;
|
trackRecordingServiceConnection = null;
|
||||||
}
|
}
|
||||||
@@ -370,48 +396,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
// We don't care.
|
// We don't care.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMetricUnitsChanged(final boolean metric) {
|
|
||||||
if (isResumed()) {
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onReportSpeedChanged(final boolean speed) {
|
|
||||||
if (isResumed()) {
|
|
||||||
getActivity().runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingGpsAccuracy(int newValue) {
|
|
||||||
recordingGpsAccuracy = newValue;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRecordingDistanceIntervalChanged(int minRecordingDistance) {
|
|
||||||
// We don't care.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resumes the trackDataHub.
|
* Resumes the trackDataHub.
|
||||||
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
||||||
@@ -419,8 +403,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
private synchronized void resumeTrackDataHub() {
|
private synchronized void resumeTrackDataHub() {
|
||||||
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
trackDataHub = ((TrackDetailActivity) getActivity()).getTrackDataHub();
|
||||||
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
trackDataHub.registerTrackDataListener(this, EnumSet.of(TrackDataType.TRACKS_TABLE,
|
||||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE,
|
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
|
||||||
TrackDataType.PREFERENCE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -458,7 +441,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
|||||||
* Tries to fetch most recent {@link SensorDataSet} {@link de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager}.
|
* Tries to fetch most recent {@link SensorDataSet} {@link de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager}.
|
||||||
*/
|
*/
|
||||||
private void updateSensorDataUI() {
|
private void updateSensorDataUI() {
|
||||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
|
||||||
SensorDataSet sensorDataSet = null;
|
SensorDataSet sensorDataSet = null;
|
||||||
if (trackRecordingService == null) {
|
if (trackRecordingService == null) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import android.content.Context;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter;
|
import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
|
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
|
||||||
@@ -67,7 +67,7 @@ public enum TrackFileFormat {
|
|||||||
},
|
},
|
||||||
KMZ_ONLY_TRACK {
|
KMZ_ONLY_TRACK {
|
||||||
|
|
||||||
private final static boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||||
@@ -86,10 +86,15 @@ public enum TrackFileFormat {
|
|||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includesPhotos() {
|
||||||
|
return exportPhotos;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
KMZ_WITH_TRACKDETAIL {
|
KMZ_WITH_TRACKDETAIL {
|
||||||
|
|
||||||
private final static boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||||
@@ -108,10 +113,16 @@ public enum TrackFileFormat {
|
|||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includesPhotos() {
|
||||||
|
return exportPhotos;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
KMZ_WITH_TRACKDETAIL_AND_SENSORDATA {
|
KMZ_WITH_TRACKDETAIL_AND_SENSORDATA {
|
||||||
|
|
||||||
private final static boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||||
@@ -130,10 +141,17 @@ public enum TrackFileFormat {
|
|||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includesPhotos() {
|
||||||
|
return exportPhotos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES {
|
KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES {
|
||||||
|
|
||||||
private final static boolean exportPhotos = true;
|
private static final boolean exportPhotos = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||||
@@ -152,6 +170,12 @@ public enum TrackFileFormat {
|
|||||||
public String getExtension() {
|
public String getExtension() {
|
||||||
return "kmz";
|
return "kmz";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean includesPhotos() {
|
||||||
|
return exportPhotos;
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
GPX {
|
GPX {
|
||||||
@Override
|
@Override
|
||||||
@@ -205,6 +229,13 @@ public enum TrackFileFormat {
|
|||||||
*/
|
*/
|
||||||
public abstract String getExtension();
|
public abstract String getExtension();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the format supports photos.
|
||||||
|
*/
|
||||||
|
public boolean includesPhotos() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of for each format.
|
* Returns the name of for each format.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,21 +16,20 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
@@ -39,7 +38,6 @@ import de.dennisguse.opentracks.util.FileUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity for saving tracks to the external storage.
|
* An activity for saving tracks to the external storage.
|
||||||
* If saving a specific track, option to save it to a temp directory and play the track afterward.
|
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
@@ -53,34 +51,34 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
|
|||||||
private String directoryDisplayName;
|
private String directoryDisplayName;
|
||||||
|
|
||||||
private ExportAsyncTask exportAsyncTask;
|
private ExportAsyncTask exportAsyncTask;
|
||||||
|
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
private int processedTrackCount;
|
private int processedTrackCount;
|
||||||
private int totalTrackCount;
|
private int totalTrackCount;
|
||||||
|
|
||||||
|
private Uri directoryUri;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
|
||||||
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
startActivityForResult(intent, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
|
||||||
} else {
|
|
||||||
fileTypeDialogStart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, resultData);
|
||||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
directoryUri = resultData.getData();
|
||||||
} else {
|
|
||||||
fileTypeDialogStart();
|
fileTypeDialogStart();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fileTypeDialogStart() {
|
private void fileTypeDialogStart() {
|
||||||
@@ -89,21 +87,14 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
||||||
if (!FileUtils.isExternalStorageWriteable()) {
|
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
|
||||||
finish();
|
if (exportDirectory == null) {
|
||||||
return;
|
exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
File directory = new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
directoryDisplayName = FileUtils.getPath(exportDirectory);
|
||||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, exportDirectory);
|
||||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
|
||||||
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, directory);
|
|
||||||
exportAsyncTask.execute();
|
exportAsyncTask.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,8 +132,7 @@ public class ExportActivity extends FragmentActivity implements FileTypeDialogFr
|
|||||||
} else {
|
} else {
|
||||||
iconId = R.drawable.ic_dialog_error_24dp;
|
iconId = R.drawable.ic_dialog_error_24dp;
|
||||||
titleId = R.string.generic_error_title;
|
titleId = R.string.generic_error_title;
|
||||||
message = getString(R.string.export_external_storage_error, processedTrackCount, totalTracks,
|
message = getString(R.string.export_external_storage_error, processedTrackCount, totalTracks, directoryDisplayName);
|
||||||
directoryDisplayName);
|
|
||||||
}
|
}
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
|
AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
|
||||||
.setIcon(iconId).setMessage(message)
|
.setIcon(iconId).setMessage(message)
|
||||||
|
|||||||
@@ -22,17 +22,16 @@ import android.os.AsyncTask;
|
|||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import java.io.FileNotFoundException;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import java.io.IOException;
|
||||||
import de.dennisguse.opentracks.content.TracksColumns;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,16 +44,16 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
|
|
||||||
private static final String TAG = ExportAsyncTask.class.getSimpleName();
|
private static final String TAG = ExportAsyncTask.class.getSimpleName();
|
||||||
private final TrackFileFormat trackFileFormat;
|
private final TrackFileFormat trackFileFormat;
|
||||||
private final File directory;
|
private final DocumentFile directory;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private ExportActivity exportActivity;
|
private ExportActivity exportActivity;
|
||||||
private WakeLock wakeLock;
|
private WakeLock wakeLock;
|
||||||
|
|
||||||
private boolean completed;
|
private boolean completed = false;
|
||||||
|
|
||||||
private int processedTrackCount;
|
private int processedTrackCount = 0;
|
||||||
private int totalTrackCount;
|
private int totalTrackCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an AsyncTask.
|
* Creates an AsyncTask.
|
||||||
@@ -63,16 +62,12 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
* @param trackFileFormat the track file format
|
* @param trackFileFormat the track file format
|
||||||
* @param directory the directory to write the file
|
* @param directory the directory to write the file
|
||||||
*/
|
*/
|
||||||
public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat, File directory) {
|
public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat, DocumentFile directory) {
|
||||||
this.exportActivity = exportActivity;
|
this.exportActivity = exportActivity;
|
||||||
this.trackFileFormat = trackFileFormat;
|
this.trackFileFormat = trackFileFormat;
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
context = exportActivity.getApplicationContext();
|
context = exportActivity.getApplicationContext();
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
completed = false;
|
|
||||||
processedTrackCount = 0;
|
|
||||||
totalTrackCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,17 +93,12 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
try {
|
try {
|
||||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||||
boolean isRecording = PreferencesUtils.isRecording(exportActivity);
|
|
||||||
boolean isPaused = PreferencesUtils.isRecordingTrackPaused(exportActivity);
|
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
||||||
// Get the wake lock if not recording or paused
|
|
||||||
if (!isRecording || isPaused) {
|
return exportAllTracks();
|
||||||
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
|
||||||
}
|
|
||||||
return saveAllTracks();
|
|
||||||
} finally {
|
} finally {
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,15 +126,11 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves tracks to one file (uses first track to determine the filename).
|
* Export the track to one file.
|
||||||
*
|
*
|
||||||
* @param tracks the tracks
|
* @param track the track
|
||||||
*/
|
*/
|
||||||
private Boolean saveTracks(Track[] tracks) {
|
private Boolean exportTrack(Track track) {
|
||||||
if (tracks.length == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackExporterListener trackExporterListener = new TrackExporterListener() {
|
TrackExporterListener trackExporterListener = new TrackExporterListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onProgressUpdate(int number, int max) {
|
public void onProgressUpdate(int number, int max) {
|
||||||
@@ -155,14 +141,18 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, tracks, trackExporterListener);
|
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track}, trackExporterListener);
|
||||||
|
|
||||||
Track track = tracks[0];
|
String fileName = track.getId() + "." + trackFileFormat.getExtension();
|
||||||
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), trackFileFormat.getExtension());
|
|
||||||
File file = new File(directory, fileName);
|
|
||||||
|
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
// Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists.
|
||||||
if (trackExporter.writeTrack(context, fileOutputStream)) {
|
DocumentFile file = directory.findFile(fileName);
|
||||||
|
if (file == null) {
|
||||||
|
file = directory.createFile(trackFileFormat.getMimeType(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (OutputStream outputStream = context.getContentResolver().openOutputStream(file.getUri())) {
|
||||||
|
if (trackExporter.writeTrack(context, outputStream)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (!file.delete()) {
|
if (!file.delete()) {
|
||||||
@@ -183,7 +173,7 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
/**
|
/**
|
||||||
* Saves all the tracks.
|
* Saves all the tracks.
|
||||||
*/
|
*/
|
||||||
private Boolean saveAllTracks() {
|
private Boolean exportAllTracks() {
|
||||||
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -195,7 +185,7 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
cursor.moveToPosition(i);
|
cursor.moveToPosition(i);
|
||||||
Track track = contentProviderUtils.createTrack(cursor);
|
Track track = contentProviderUtils.createTrack(cursor);
|
||||||
if (track != null && saveTracks(new Track[]{track})) {
|
if (track != null && exportTrack(track)) {
|
||||||
processedTrackCount++;
|
processedTrackCount++;
|
||||||
}
|
}
|
||||||
publishProgress(i + 1, totalTrackCount);
|
publishProgress(i + 1, totalTrackCount);
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.LocationFactory;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.LocationIterator;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +99,6 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
boolean hasWaypoints = false;
|
boolean hasWaypoints = false;
|
||||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), -1L, ContentProviderUtils.MAX_LOADED_WAYPOINTS_POINTS)) {
|
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), -1L, ContentProviderUtils.MAX_LOADED_WAYPOINTS_POINTS)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
// Intentionally skip first waypoint (contains statistics).
|
|
||||||
for (int i = 0; i < cursor.getCount(); i++) {
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
throw new InterruptedException();
|
throw new InterruptedException();
|
||||||
@@ -109,9 +108,7 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
hasWaypoints = true;
|
hasWaypoints = true;
|
||||||
}
|
}
|
||||||
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
Waypoint waypoint = contentProviderUtils.createWaypoint(cursor);
|
||||||
if (!waypoint.isTripStatistics()) {
|
trackWriter.writeWaypoint(waypoint);
|
||||||
trackWriter.writeWaypoint(waypoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.moveToNext();
|
cursor.moveToNext();
|
||||||
}
|
}
|
||||||
@@ -129,10 +126,10 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
boolean wroteTrack = false;
|
boolean wroteTrack = false;
|
||||||
boolean wroteSegment = false;
|
boolean wroteSegment = false;
|
||||||
boolean isLastLocationValid = false;
|
boolean isLastLocationValid = false;
|
||||||
TrackWriterLocationFactory locationFactory = new TrackWriterLocationFactory();
|
TrackWriterTrackPointFactory trackPointFactory = new TrackWriterTrackPointFactory();
|
||||||
int locationNumber = 0;
|
int locationNumber = 0;
|
||||||
|
|
||||||
try (LocationIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, locationFactory)) {
|
try (TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, trackPointFactory)) {
|
||||||
|
|
||||||
while (locationIterator.hasNext()) {
|
while (locationIterator.hasNext()) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
@@ -147,7 +144,7 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
boolean isSegmentValid = isLocationValid && isLastLocationValid;
|
boolean isSegmentValid = isLocationValid && isLastLocationValid;
|
||||||
if (!wroteTrack && isSegmentValid) {
|
if (!wroteTrack && isSegmentValid) {
|
||||||
// Found the first two consecutive locations that are valid
|
// Found the first two consecutive locations that are valid
|
||||||
trackWriter.writeBeginTrack(track, locationFactory.lastLocation);
|
trackWriter.writeBeginTrack(track, trackPointFactory.lastLocation);
|
||||||
wroteTrack = true;
|
wroteTrack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +155,7 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
|
|
||||||
// Write the previous location, which we had previously skipped
|
// Write the previous location, which we had previously skipped
|
||||||
trackWriter.writeLocation(locationFactory.lastLocation);
|
trackWriter.writeLocation(trackPointFactory.lastLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the current location
|
// Write the current location
|
||||||
@@ -172,7 +169,7 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
locationFactory.swapLocations();
|
trackPointFactory.swapLocations();
|
||||||
isLastLocationValid = isLocationValid;
|
isLastLocationValid = isLocationValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,20 +207,20 @@ public class FileTrackExporter implements TrackExporter {
|
|||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
private class TrackWriterLocationFactory extends LocationFactory {
|
private class TrackWriterTrackPointFactory extends TrackPointFactory {
|
||||||
Location currentLocation;
|
TrackPoint currentLocation;
|
||||||
Location lastLocation;
|
TrackPoint lastLocation;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Location createLocation() {
|
public TrackPoint createLocation() {
|
||||||
if (currentLocation == null) {
|
if (currentLocation == null) {
|
||||||
currentLocation = new SensorDataSetLocation("");
|
currentLocation = new TrackPoint("");
|
||||||
}
|
}
|
||||||
return currentLocation;
|
return currentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void swapLocations() {
|
void swapLocations() {
|
||||||
Location tempLocation = lastLocation;
|
TrackPoint tempLocation = lastLocation;
|
||||||
lastLocation = currentLocation;
|
lastLocation = currentLocation;
|
||||||
currentLocation = tempLocation;
|
currentLocation = tempLocation;
|
||||||
if (currentLocation != null) {
|
if (currentLocation != null) {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import java.io.PrintWriter;
|
|||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,12 +119,9 @@ public class GpxTrackWriter implements TrackWriter {
|
|||||||
if (location.hasAltitude()) {
|
if (location.hasAltitude()) {
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ELEVATION_FORMAT.format(location.getAltitude()) + "</ele>");
|
||||||
}
|
}
|
||||||
printWriter.println(
|
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
||||||
"<time>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</time>");
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(waypoint.getName()) + "</name>");
|
||||||
printWriter.println("<cmt>" + StringUtils.formatCData(waypoint.getType().name()) + "</cmt>");
|
printWriter.println("<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
||||||
printWriter.println(
|
|
||||||
"<desc>" + StringUtils.formatCData(waypoint.getDescription()) + "</desc>");
|
|
||||||
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
|
printWriter.println("<type>" + StringUtils.formatCData(waypoint.getCategory()) + "</type>");
|
||||||
printWriter.println("</wpt>");
|
printWriter.println("</wpt>");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.content.DescriptionGenerator;
|
import de.dennisguse.opentracks.content.DescriptionGenerator;
|
||||||
import de.dennisguse.opentracks.content.DescriptionGeneratorImpl;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
@@ -43,19 +41,17 @@ import de.dennisguse.opentracks.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public class KmlTrackWriter implements TrackWriter {
|
public class KmlTrackWriter implements TrackWriter {
|
||||||
|
|
||||||
private static final String WAYPOINT_STYLE = "waypoint";
|
public static final String WAYPOINT_STYLE = "waypoint";
|
||||||
private static final String STATISTICS_STYLE = "statistics";
|
|
||||||
private static final String START_STYLE = "start";
|
private static final String START_STYLE = "start";
|
||||||
private static final String END_STYLE = "end";
|
private static final String END_STYLE = "end";
|
||||||
private static final String TRACK_STYLE = "track";
|
private static final String TRACK_STYLE = "track";
|
||||||
private static final String SCHEMA_ID = "schema";
|
private static final String SCHEMA_ID = "schema";
|
||||||
|
|
||||||
private static final String SENSOR_TYPE_CADENCE = "cadence";
|
public static final String SENSOR_TYPE_CADENCE = "cadence";
|
||||||
private static final String SENSOR_TYPE_HEART_RATE = "heart_rate";
|
public static final String SENSOR_TYPE_HEART_RATE = "heart_rate";
|
||||||
private static final String SENSOR_TYPE_POWER = "power";
|
public static final String SENSOR_TYPE_POWER = "power";
|
||||||
|
|
||||||
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||||
private static final String STATISTICS_ICON = "http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";
|
|
||||||
private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
|
||||||
private static final String END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
private static final String END_ICON = "http://maps.google.com/mapfiles/kml/paddle/red-circle.png";
|
||||||
private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||||
@@ -69,9 +65,9 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
private List<Float> powerList = new ArrayList<>();
|
private final List<Float> powerList = new ArrayList<>();
|
||||||
private List<Float> cadenceList = new ArrayList<>();
|
private final List<Float> cadenceList = new ArrayList<>();
|
||||||
private List<Float> heartRateList = new ArrayList<>();
|
private final List<Float> heartRateList = new ArrayList<>();
|
||||||
|
|
||||||
private Location startLocation;
|
private Location startLocation;
|
||||||
|
|
||||||
@@ -88,7 +84,7 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
this.exportTrackDetail = exportTrackDetail;
|
this.exportTrackDetail = exportTrackDetail;
|
||||||
this.exportSensorData = exportSensorData;
|
this.exportSensorData = exportSensorData;
|
||||||
this.exportPhotos = exportPhotos;
|
this.exportPhotos = exportPhotos;
|
||||||
this.descriptionGenerator = new DescriptionGeneratorImpl(context);
|
this.descriptionGenerator = new DescriptionGenerator(context);
|
||||||
this.contentProviderUtils = new ContentProviderUtils(context);
|
this.contentProviderUtils = new ContentProviderUtils(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +121,6 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
writeTrackStyle();
|
writeTrackStyle();
|
||||||
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
||||||
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
||||||
writePlacemarkerStyle(STATISTICS_STYLE, STATISTICS_ICON, 20, 2);
|
|
||||||
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
||||||
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
||||||
|
|
||||||
@@ -167,13 +162,11 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void writeWaypoint(Waypoint waypoint) {
|
public void writeWaypoint(Waypoint waypoint) {
|
||||||
if (printWriter != null && exportTrackDetail) {
|
if (printWriter != null && exportTrackDetail) {
|
||||||
String styleName = waypoint.getType() == WaypointType.STATISTICS ? STATISTICS_STYLE : WAYPOINT_STYLE;
|
|
||||||
|
|
||||||
if (waypoint.hasPhoto() && exportPhotos) {
|
if (waypoint.hasPhoto() && exportPhotos) {
|
||||||
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation());
|
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation());
|
||||||
writePhotoOverlay(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation(), waypoint.getPhotoUrl(), heading);
|
writePhotoOverlay(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), waypoint.getLocation(), waypoint.getPhotoUrl(), heading);
|
||||||
} else {
|
} else {
|
||||||
writePlacemark(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), styleName, waypoint.getLocation());
|
writePlacemark(waypoint.getName(), waypoint.getCategory(), waypoint.getDescription(), WAYPOINT_STYLE, waypoint.getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,6 +198,7 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
if (exportTrackDetail) {
|
if (exportTrackDetail) {
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
printWriter.println("<description>" + StringUtils.formatCData(track.getDescription()) + "</description>");
|
||||||
|
printWriter.println("<icon>" + StringUtils.formatCData(track.getIcon()) + "</icon>");
|
||||||
}
|
}
|
||||||
|
|
||||||
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||||
@@ -270,8 +264,8 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
|
|
||||||
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
|
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
|
||||||
|
|
||||||
if (exportSensorData && location instanceof SensorDataSetLocation) {
|
if (exportSensorData && location instanceof TrackPoint) {
|
||||||
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
SensorDataSet sensorDataSet = ((TrackPoint) location).getSensorDataSet();
|
||||||
if (sensorDataSet != null) {
|
if (sensorDataSet != null) {
|
||||||
if (sensorDataSet.hasHeartRate()) {
|
if (sensorDataSet.hasHeartRate()) {
|
||||||
heartRateList.add(sensorDataSet.getHeartRate());
|
heartRateList.add(sensorDataSet.getHeartRate());
|
||||||
@@ -327,16 +321,14 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a photo overlay.
|
* Writes a photo overlay.
|
||||||
*
|
* @param name the name
|
||||||
* @param name the name
|
|
||||||
* @param category the category
|
* @param category the category
|
||||||
* @param description the description
|
* @param description the description
|
||||||
* @param styleName the style name
|
|
||||||
* @param location the location
|
* @param location the location
|
||||||
* @param photoUrl the photo url
|
* @param photoUrl the photo url
|
||||||
* @param heading the heading
|
* @param heading the heading
|
||||||
*/
|
*/
|
||||||
private void writePhotoOverlay(String name, String category, String description, String styleName, Location location, String photoUrl, float heading) {
|
private void writePhotoOverlay(String name, String category, String description, Location location, String photoUrl, float heading) {
|
||||||
if (location != null && exportTrackDetail) {
|
if (location != null && exportTrackDetail) {
|
||||||
printWriter.println("<PhotoOverlay>");
|
printWriter.println("<PhotoOverlay>");
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||||
@@ -349,7 +341,7 @@ public class KmlTrackWriter implements TrackWriter {
|
|||||||
printWriter.print("<tilt>90</tilt>");
|
printWriter.print("<tilt>90</tilt>");
|
||||||
printWriter.println("</Camera>");
|
printWriter.println("</Camera>");
|
||||||
printWriter.println("<TimeStamp><when>" + getTime(location) + "</when></TimeStamp>");
|
printWriter.println("<TimeStamp><when>" + getTime(location) + "</when></TimeStamp>");
|
||||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + KmlTrackWriter.WAYPOINT_STYLE + "</styleUrl>");
|
||||||
writeCategory(category);
|
writeCategory(category);
|
||||||
|
|
||||||
if (exportPhotos) {
|
if (exportPhotos) {
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ import java.io.OutputStream;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +52,7 @@ public class KmzTrackExporter implements TrackExporter {
|
|||||||
private final TrackExporter fileTrackExporter;
|
private final TrackExporter fileTrackExporter;
|
||||||
private final Track[] tracks;
|
private final Track[] tracks;
|
||||||
|
|
||||||
private boolean exportPhotos;
|
private final boolean exportPhotos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import android.location.Location;
|
|||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for writing tracks to a file. The expected sequence of calls is:
|
* Interface for writing tracks to a file. The expected sequence of calls is:
|
||||||
|
|||||||
+33
-77
@@ -38,15 +38,12 @@ import javax.xml.parsers.SAXParser;
|
|||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.DescriptionGeneratorImpl;
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.LocationFactory;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.LocationIterator;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
@@ -71,12 +68,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final int recordingDistanceInterval;
|
private final int recordingDistanceInterval;
|
||||||
|
|
||||||
private final List<Long> trackIds;
|
private final List<Long> trackIds = new ArrayList<>();
|
||||||
private final List<Waypoint> waypoints;
|
private final List<Waypoint> waypoints = new ArrayList<>();
|
||||||
|
|
||||||
// The current element content
|
// The current element content
|
||||||
//TODO Should be made private and getter be used by child classes.
|
//TODO Should be made private and getter be used by child classes.
|
||||||
protected String content;
|
protected String content;
|
||||||
|
protected String icon;
|
||||||
protected String name;
|
protected String name;
|
||||||
protected String description;
|
protected String description;
|
||||||
protected String category;
|
protected String category;
|
||||||
@@ -104,8 +102,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
this.importTrackId = importTrackId;
|
this.importTrackId = importTrackId;
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
||||||
trackIds = new ArrayList<>();
|
|
||||||
waypoints = new ArrayList<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -164,9 +160,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
Waypoint waypoint = null;
|
Waypoint waypoint = null;
|
||||||
Location location = null;
|
Location location = null;
|
||||||
TripStatisticsUpdater trackTripStatisticstrackUpdater = new TripStatisticsUpdater(track.getTripStatistics().getStartTime());
|
TripStatisticsUpdater trackTripStatisticstrackUpdater = new TripStatisticsUpdater(track.getTripStatistics().getStartTime());
|
||||||
TripStatisticsUpdater markerTripStatisticsUpdater = new TripStatisticsUpdater(track.getTripStatistics().getStartTime());
|
@Deprecated // TODO Should not be necessary anymore?
|
||||||
|
TripStatisticsUpdater markerTripStatisticsUpdater = new TripStatisticsUpdater(track.getTripStatistics().getStartTime());
|
||||||
|
|
||||||
try (LocationIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, LocationFactory.DEFAULT_LOCATION_FACTORY)) {
|
try (TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY)) {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (waypoint == null) {
|
if (waypoint == null) {
|
||||||
@@ -177,6 +174,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
if (!locationIterator.hasNext()) {
|
if (!locationIterator.hasNext()) {
|
||||||
// No more track points. Ignore the rest of the waypoints.
|
// No more track points. Ignore the rest of the waypoints.
|
||||||
@@ -186,9 +184,11 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
trackTripStatisticstrackUpdater.addLocation(location, recordingDistanceInterval);
|
trackTripStatisticstrackUpdater.addLocation(location, recordingDistanceInterval);
|
||||||
markerTripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
markerTripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (waypoint.getLocation().getTime() > location.getTime()) {
|
if (waypoint.getLocation().getTime() > location.getTime()) {
|
||||||
location = null;
|
location = null;
|
||||||
} else if (waypoint.getLocation().getTime() < location.getTime()) {
|
} else if (waypoint.getLocation().getTime() < location.getTime()) {
|
||||||
|
Log.w(TAG, "Ignoring waypoint: current location was after waypoint.");
|
||||||
waypoint = null;
|
waypoint = null;
|
||||||
} else {
|
} else {
|
||||||
// The waypoint location time matches the track point time
|
// The waypoint location time matches the track point time
|
||||||
@@ -200,28 +200,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
// Valid location
|
// Valid location
|
||||||
if (location.getLatitude() == waypoint.getLocation().getLatitude() && location.getLongitude() == waypoint.getLocation().getLongitude()) {
|
if (location.getLatitude() == waypoint.getLocation().getLatitude() && location.getLongitude() == waypoint.getLocation().getLongitude()) {
|
||||||
|
String waypointDescription = waypoint.getDescription();
|
||||||
// Get tripStatistics, description, and icon
|
String icon = context.getString(R.string.marker_waypoint_icon_url);
|
||||||
TripStatistics tripStatistics;
|
|
||||||
String waypointDescription;
|
|
||||||
String icon;
|
|
||||||
if (waypoint.getType() == WaypointType.STATISTICS) {
|
|
||||||
tripStatistics = markerTripStatisticsUpdater.getTripStatistics();
|
|
||||||
markerTripStatisticsUpdater = new TripStatisticsUpdater(location.getTime());
|
|
||||||
waypointDescription = new DescriptionGeneratorImpl(context).generateWaypointDescription(tripStatistics);
|
|
||||||
icon = context.getString(R.string.marker_statistics_icon_url);
|
|
||||||
} else {
|
|
||||||
tripStatistics = null;
|
|
||||||
waypointDescription = waypoint.getDescription();
|
|
||||||
icon = context.getString(R.string.marker_waypoint_icon_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get length and duration
|
|
||||||
double length = trackTripStatisticstrackUpdater.getTripStatistics().getTotalDistance();
|
double length = trackTripStatisticstrackUpdater.getTripStatistics().getTotalDistance();
|
||||||
long duration = trackTripStatisticstrackUpdater.getTripStatistics().getTotalTime();
|
long duration = trackTripStatisticstrackUpdater.getTripStatistics().getTotalTime();
|
||||||
|
|
||||||
// Insert waypoint
|
// Insert waypoint
|
||||||
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), waypoint.getType(), length, duration, -1L, -1L, location, tripStatistics, waypoint.getPhotoUrl());
|
Waypoint newWaypoint = new Waypoint(waypoint.getName(), waypointDescription, waypoint.getCategory(), icon, track.getId(), length, duration, location, waypoint.getPhotoUrl());
|
||||||
contentProviderUtils.insertWaypoint(newWaypoint);
|
contentProviderUtils.insertWaypoint(newWaypoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +250,12 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
}
|
}
|
||||||
if (category != null) {
|
if (category != null) {
|
||||||
trackData.track.setCategory(category);
|
trackData.track.setCategory(category);
|
||||||
trackData.track.setIcon(TrackIconUtils.getIconValue(context, category));
|
//TODO remove when GPX and KML support reading this property.
|
||||||
|
if (icon == null)
|
||||||
|
trackData.track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||||
|
}
|
||||||
|
if (icon != null) {
|
||||||
|
trackData.track.setIcon(icon);
|
||||||
}
|
}
|
||||||
if (trackData.tripStatisticsUpdater == null) {
|
if (trackData.tripStatisticsUpdater == null) {
|
||||||
trackData.tripStatisticsUpdater = new TripStatisticsUpdater(trackData.importTime);
|
trackData.tripStatisticsUpdater = new TripStatisticsUpdater(trackData.importTime);
|
||||||
@@ -274,7 +264,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
trackData.track.setTripStatistics(trackData.tripStatisticsUpdater.getTripStatistics());
|
trackData.track.setTripStatistics(trackData.tripStatisticsUpdater.getTripStatistics());
|
||||||
trackData.track.setNumberOfPoints(trackData.numberOfLocations);
|
trackData.track.setNumberOfPoints(trackData.numberOfLocations);
|
||||||
contentProviderUtils.updateTrack(trackData.track);
|
contentProviderUtils.updateTrack(trackData.track);
|
||||||
insertFirstWaypoint(trackData.track);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,17 +274,15 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
//If not the first segment, add a pause separator if there is at least one location in the last segment.
|
//If not the first segment, add a pause separator if there is at least one location in the last segment.
|
||||||
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment != null) {
|
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment != null) {
|
||||||
insertLocation(createLocation(TrackRecordingService.PAUSE_LATITUDE, 0.0, 0.0, trackData.lastLocationInCurrentSegment.getTime()));
|
insertLocation(createLocation(TrackPointsColumns.PAUSE_LATITUDE, 0.0, 0.0, trackData.lastLocationInCurrentSegment.getTime()));
|
||||||
}
|
}
|
||||||
trackData.lastLocationInCurrentSegment = null;
|
trackData.lastLocationInCurrentSegment = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a waypoint.
|
* Adds a waypoint.
|
||||||
*
|
|
||||||
* @param type the waypoint type
|
|
||||||
*/
|
*/
|
||||||
protected void addWaypoint(WaypointType type) throws SAXException {
|
protected void addWaypoint() throws SAXException {
|
||||||
// Waypoint must have a time, else cannot match to the track points
|
// Waypoint must have a time, else cannot match to the track points
|
||||||
if (time == null) {
|
if (time == null) {
|
||||||
return;
|
return;
|
||||||
@@ -318,7 +305,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
if (category != null) {
|
if (category != null) {
|
||||||
waypoint.setCategory(category);
|
waypoint.setCategory(category);
|
||||||
}
|
}
|
||||||
waypoint.setType(type);
|
|
||||||
|
|
||||||
if (photoUrl != null) {
|
if (photoUrl != null) {
|
||||||
waypoint.setPhotoUrl(photoUrl);
|
waypoint.setPhotoUrl(photoUrl);
|
||||||
@@ -358,8 +344,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment == null) {
|
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment == null) {
|
||||||
// If not the first segment, add a resume separator before adding the first location.
|
// If not the first segment, add a resume separator before adding the first location.
|
||||||
insertLocation(
|
insertLocation(createLocation(TrackPointsColumns.RESUME_LATITUDE, 0.0, 0.0, location.getTime()));
|
||||||
createLocation(TrackRecordingService.RESUME_LATITUDE, 0.0, 0.0, location.getTime()));
|
|
||||||
}
|
}
|
||||||
trackData.lastLocationInCurrentSegment = location;
|
trackData.lastLocationInCurrentSegment = location;
|
||||||
return location;
|
return location;
|
||||||
@@ -372,11 +357,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
*/
|
*/
|
||||||
protected void insertTrackPoint(Location location) {
|
protected void insertTrackPoint(Location location) {
|
||||||
insertLocation(location);
|
insertLocation(location);
|
||||||
|
|
||||||
if (trackData.track.getStartId() == -1L) {
|
|
||||||
// Flush the location to set the track start id and the track end id
|
|
||||||
flushLocations(trackData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,7 +377,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
if (importTrackId == -1L) {
|
if (importTrackId == -1L) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
File dir = FileUtils.getPhotoDir(importTrackId);
|
File dir = FileUtils.getPhotoDir(context, importTrackId);
|
||||||
File file = new File(dir, fileName);
|
File file = new File(dir, fileName);
|
||||||
return Uri.fromFile(file).toString();
|
return Uri.fromFile(file).toString();
|
||||||
}
|
}
|
||||||
@@ -431,7 +411,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
timeValue = trackData.importTime;
|
timeValue = trackData.importTime;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
timeValue = StringUtils.getTime(time);
|
timeValue = StringUtils.parseTime(time);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
|
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
|
||||||
}
|
}
|
||||||
@@ -494,31 +474,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
}
|
}
|
||||||
contentProviderUtils.bulkInsertTrackPoint(data.bufferedLocations, data.numBufferedLocations, data.track.getId());
|
contentProviderUtils.bulkInsertTrackPoint(data.bufferedLocations, data.numBufferedLocations, data.track.getId());
|
||||||
data.numBufferedLocations = 0;
|
data.numBufferedLocations = 0;
|
||||||
if (data.track.getStartId() == -1L) {
|
|
||||||
data.track.setStartId(contentProviderUtils.getFirstTrackPointId(data.track.getId()));
|
|
||||||
}
|
|
||||||
data.track.setStopId(contentProviderUtils.getLastTrackPointId(data.track.getId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the first waypoint, the track statistics waypoint.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
*/
|
|
||||||
private void insertFirstWaypoint(Track track) {
|
|
||||||
String waypointName = context.getString(R.string.marker_split_name_format, 0);
|
|
||||||
String waypointCategory = "";
|
|
||||||
TripStatisticsUpdater updater = new TripStatisticsUpdater(track.getTripStatistics().getStartTime());
|
|
||||||
TripStatistics tripStatistics = updater.getTripStatistics();
|
|
||||||
String waypointDescription = new DescriptionGeneratorImpl(context).generateWaypointDescription(tripStatistics);
|
|
||||||
String icon = context.getString(R.string.marker_statistics_icon_url);
|
|
||||||
double length = 0.0;
|
|
||||||
long duration = 0L;
|
|
||||||
Location waypointLocation = new Location("");
|
|
||||||
waypointLocation.setLatitude(100);
|
|
||||||
waypointLocation.setLongitude(180);
|
|
||||||
Waypoint waypoint = new Waypoint(waypointName, waypointDescription, waypointCategory, icon, track.getId(), WaypointType.STATISTICS, length, duration, -1L, -1L, waypointLocation, tripStatistics, "");
|
|
||||||
contentProviderUtils.insertWaypoint(waypoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -535,9 +490,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
|
// TODO Why private inner class?
|
||||||
private class TrackData {
|
private class TrackData {
|
||||||
// The current track
|
// The current track
|
||||||
Track track = new Track();
|
final Track track = new Track();
|
||||||
|
|
||||||
// The number of segments processed for the current track
|
// The number of segments processed for the current track
|
||||||
int numberOfSegments = 0;
|
int numberOfSegments = 0;
|
||||||
@@ -552,10 +508,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
TripStatisticsUpdater tripStatisticsUpdater;
|
TripStatisticsUpdater tripStatisticsUpdater;
|
||||||
|
|
||||||
// The import time of the track.
|
// The import time of the track.
|
||||||
long importTime = System.currentTimeMillis();
|
final long importTime = System.currentTimeMillis();
|
||||||
|
|
||||||
// The buffered locations
|
// The buffered locations
|
||||||
Location[] bufferedLocations = new Location[MAX_BUFFERED_LOCATIONS];
|
final Location[] bufferedLocations = new Location[MAX_BUFFERED_LOCATIONS];
|
||||||
|
|
||||||
// The number of buffered locations
|
// The number of buffered locations
|
||||||
int numBufferedLocations = 0;
|
int numBufferedLocations = 0;
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import org.xml.sax.Attributes;
|
import org.xml.sax.Attributes;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a GPX file.
|
* Imports a GPX file.
|
||||||
@@ -184,6 +183,6 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
* On waypoint end.
|
* On waypoint end.
|
||||||
*/
|
*/
|
||||||
private void onWaypointEnd() throws SAXException {
|
private void onWaypointEnd() throws SAXException {
|
||||||
addWaypoint(WaypointType.STATISTICS.name().equals(waypointType) ? WaypointType.STATISTICS : WaypointType.WAYPOINT);
|
addWaypoint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,21 +16,20 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
@@ -38,8 +37,7 @@ import de.dennisguse.opentracks.util.DialogUtils;
|
|||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity to import files from the external storage. Optionally to import
|
* An activity to import files from the external storage.
|
||||||
* one specific file.
|
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
@@ -50,70 +48,53 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
|
|||||||
private static final int DIALOG_PROGRESS_ID = 0;
|
private static final int DIALOG_PROGRESS_ID = 0;
|
||||||
private static final int DIALOG_RESULT_ID = 1;
|
private static final int DIALOG_RESULT_ID = 1;
|
||||||
|
|
||||||
|
private String directoryDisplayName;
|
||||||
|
|
||||||
private ImportAsyncTask importAsyncTask;
|
private ImportAsyncTask importAsyncTask;
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
// the path on the external storage to import
|
|
||||||
private String directoryDisplayName;
|
|
||||||
|
|
||||||
private int importedTrackCount;
|
private int importedTrackCount;
|
||||||
|
|
||||||
private int totalTrackCount;
|
private int totalTrackCount;
|
||||||
|
|
||||||
|
private Uri directoryUri;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
} else {
|
|
||||||
fileTypeDialogStart();
|
startActivityForResult(intent, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, resultData);
|
||||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
Toast.makeText(this, R.string.external_storage_not_readable, Toast.LENGTH_LONG).show();
|
directoryUri = resultData.getData();
|
||||||
} else {
|
|
||||||
fileTypeDialogStart();
|
fileTypeDialogStart();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void fileTypeDialogStart() {
|
private void fileTypeDialogStart() {
|
||||||
FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.import_selection_title, R.string.import_selection_option);
|
FileTypeDialogFragment.showDialog(getSupportFragmentManager(), R.string.import_selection_title, R.string.import_selection_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
||||||
if (!FileUtils.isExternalStorageAvailable()) {
|
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
|
||||||
Toast.makeText(this, R.string.external_storage_not_available, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
directoryDisplayName = FileUtils.getPath(pickedDirectory);
|
||||||
String directoryPath = FileUtils.getPath(trackFileFormat.getExtension());
|
|
||||||
if (!FileUtils.isDirectory(new File(directoryPath))) {
|
|
||||||
Toast.makeText(this, getString(R.string.import_no_directory, directoryDisplayName), Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO (still needed?): getLastNonConfiguration instance returned ExportAsyncTask before
|
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, pickedDirectory);
|
||||||
// Object retained = getLastNonConfigurationInstance();
|
|
||||||
// if (retained instanceof ImportAsyncTask) {
|
|
||||||
// importAsyncTask = (ImportAsyncTask) retained;
|
|
||||||
// importAsyncTask.setActivity(this);
|
|
||||||
// } else {
|
|
||||||
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, directoryPath);
|
|
||||||
importAsyncTask.execute();
|
importAsyncTask.execute();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -154,8 +135,7 @@ public class ImportActivity extends FragmentActivity implements FileTypeDialogFr
|
|||||||
} else {
|
} else {
|
||||||
iconId = R.drawable.ic_dialog_error_24dp;
|
iconId = R.drawable.ic_dialog_error_24dp;
|
||||||
titleId = R.string.generic_error_title;
|
titleId = R.string.generic_error_title;
|
||||||
message = getString(
|
message = getString(R.string.import_error, importedTrackCount, totalFiles, directoryDisplayName);
|
||||||
R.string.import_error, importedTrackCount, totalFiles, directoryDisplayName);
|
|
||||||
}
|
}
|
||||||
return new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
return new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
|
||||||
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
|
||||||
|
|||||||
@@ -22,14 +22,15 @@ import android.os.AsyncTask;
|
|||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.File;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
@@ -44,7 +45,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
|
|
||||||
private static final String TAG = ImportAsyncTask.class.getSimpleName();
|
private static final String TAG = ImportAsyncTask.class.getSimpleName();
|
||||||
private final TrackFileFormat trackFileFormat;
|
private final TrackFileFormat trackFileFormat;
|
||||||
private final String path;
|
private final DocumentFile directory;
|
||||||
// TODO Can be removed, or?
|
// TODO Can be removed, or?
|
||||||
private Context context;
|
private Context context;
|
||||||
// TODO Use weak reference
|
// TODO Use weak reference
|
||||||
@@ -66,12 +67,12 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
*
|
*
|
||||||
* @param importActivity the activity currently associated with this AsyncTask
|
* @param importActivity the activity currently associated with this AsyncTask
|
||||||
* @param trackFileFormat the track file format
|
* @param trackFileFormat the track file format
|
||||||
* @param path path to import GPX files
|
* @param directory path to import GPX files
|
||||||
*/
|
*/
|
||||||
public ImportAsyncTask(ImportActivity importActivity, TrackFileFormat trackFileFormat, String path) {
|
public ImportAsyncTask(ImportActivity importActivity, TrackFileFormat trackFileFormat, DocumentFile directory) {
|
||||||
this.importActivity = importActivity;
|
this.importActivity = importActivity;
|
||||||
this.trackFileFormat = trackFileFormat;
|
this.trackFileFormat = trackFileFormat;
|
||||||
this.path = path;
|
this.directory = directory;
|
||||||
context = importActivity.getApplicationContext();
|
context = importActivity.getApplicationContext();
|
||||||
|
|
||||||
completed = false;
|
completed = false;
|
||||||
@@ -99,39 +100,26 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
protected Boolean doInBackground(Void... params) {
|
* Gets a list of files.
|
||||||
try {
|
* If importAll is true, returns a list of the files under the path directory.
|
||||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); // TODO Should be set to previous level at the end of this method.
|
* If importAll is false, returns a list containing just the path file.
|
||||||
// Get the wake lock if not recording or paused
|
*/
|
||||||
boolean isRecording = PreferencesUtils.isRecording(importActivity);
|
private static List<DocumentFile> getFiles(DocumentFile file, TrackFileFormat trackFileFormat) {
|
||||||
boolean isPaused = PreferencesUtils.isRecordingTrackPaused(importActivity);
|
List<DocumentFile> files = new ArrayList<>();
|
||||||
if (!isRecording || isPaused) {
|
|
||||||
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<File> files = getFiles();
|
for (DocumentFile candidate : file.listFiles()) {
|
||||||
totalTrackCount = files.size();
|
if (!candidate.isDirectory()) {
|
||||||
if (totalTrackCount == 0) {
|
String extension = FileUtils.getExtension(candidate.getName());
|
||||||
return true;
|
if (trackFileFormat.getExtension().equals(extension)) {
|
||||||
}
|
files.add(candidate);
|
||||||
|
|
||||||
for (int i = 0; i < totalTrackCount; i++) {
|
|
||||||
if (isCancelled()) {
|
|
||||||
// If cancelled, return true to show the number of files imported
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (importFile(files.get(i))) {
|
} else {
|
||||||
importTrackCount++;
|
files.addAll(getFiles(candidate, trackFileFormat));
|
||||||
}
|
|
||||||
publishProgress(i + 1, totalTrackCount);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} finally {
|
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
|
||||||
wakeLock.release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -159,16 +147,49 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); // TODO Should be set to previous level at the end of this method.
|
||||||
|
// Get the wake lock if not recording or paused
|
||||||
|
boolean isRecording = PreferencesUtils.isRecording(importActivity);
|
||||||
|
boolean isPaused = PreferencesUtils.isRecordingTrackPaused(importActivity);
|
||||||
|
if (!isRecording || isPaused) {
|
||||||
|
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DocumentFile> files = getFiles(directory, trackFileFormat);
|
||||||
|
totalTrackCount = files.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < totalTrackCount; i++) {
|
||||||
|
if (isCancelled()) {
|
||||||
|
// If cancelled, return true to show the number of files imported
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (importFile(files.get(i))) {
|
||||||
|
importTrackCount++;
|
||||||
|
}
|
||||||
|
publishProgress(i + 1, totalTrackCount);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
if (wakeLock != null && wakeLock.isHeld()) {
|
||||||
|
wakeLock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a file.
|
* Imports a file.
|
||||||
*
|
*
|
||||||
* @param file the file
|
* @param file the file
|
||||||
*/
|
*/
|
||||||
private boolean importFile(final File file) {
|
private boolean importFile(final DocumentFile file) {
|
||||||
TrackImporter trackImporter;
|
TrackImporter trackImporter;
|
||||||
if (trackFileFormat == TrackFileFormat.GPX) {
|
if (trackFileFormat == TrackFileFormat.GPX) {
|
||||||
trackImporter = new GpxFileTrackImporter(importActivity);
|
trackImporter = new GpxFileTrackImporter(importActivity);
|
||||||
} else { //KML or KMZ
|
} else {
|
||||||
|
//KML or KMZ
|
||||||
String extension = FileUtils.getExtension(file.getName());
|
String extension = FileUtils.getExtension(file.getName());
|
||||||
if (TrackFileFormat.KML_ONLY_TRACK.getExtension().equals(extension)) {
|
if (TrackFileFormat.KML_ONLY_TRACK.getExtension().equals(extension)) {
|
||||||
trackImporter = new KmlFileTrackImporter(importActivity, -1L);
|
trackImporter = new KmlFileTrackImporter(importActivity, -1L);
|
||||||
@@ -181,37 +202,12 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
try (InputStream inputStream = context.getContentResolver().openInputStream(file.getUri())) {
|
||||||
trackId = trackImporter.importFile(fileInputStream);
|
trackId = trackImporter.importFile(inputStream);
|
||||||
return trackId != -1L;
|
return trackId != -1L;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Unable to import file", e);
|
Log.e(TAG, "Unable to import file", e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a list of files.
|
|
||||||
* If importAll is true, returns a list of the files under the path directory.
|
|
||||||
* If importAll is false, returns a list containing just the path file.
|
|
||||||
*/
|
|
||||||
private List<File> getFiles() {
|
|
||||||
List<File> files = new ArrayList<>();
|
|
||||||
File file = new File(path);
|
|
||||||
|
|
||||||
File[] candidates = file.listFiles();
|
|
||||||
if (candidates == null) {
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
for (File candidate : candidates) {
|
|
||||||
if (!FileUtils.isDirectory(candidate)) {
|
|
||||||
String extension = FileUtils.getExtension(candidate.getName());
|
|
||||||
if (trackFileFormat.getExtension().equals(extension)) {
|
|
||||||
files.add(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ import org.xml.sax.SAXException;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
|
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a KML file.
|
* Imports a KML file.
|
||||||
@@ -42,15 +42,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
|
|
||||||
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
||||||
|
|
||||||
private static final String CADENCE = "cadence";
|
private static final String WAYPOINT_STYLE = "#" + KmlTrackWriter.WAYPOINT_STYLE;
|
||||||
private static final String HEART_RATE = "heart_rate";
|
|
||||||
private static final String POWER = "power";
|
|
||||||
|
|
||||||
private static final String STATISTICS_STYLE = "#statistics";
|
|
||||||
private static final String WAYPOINT_STYLE = "#waypoint";
|
|
||||||
|
|
||||||
private static final String TAG_COORDINATES = "coordinates";
|
private static final String TAG_COORDINATES = "coordinates";
|
||||||
private static final String TAG_DESCRIPTION = "description";
|
private static final String TAG_DESCRIPTION = "description";
|
||||||
|
private static final String TAG_ICON = "icon";
|
||||||
private static final String TAG_GX_COORD = "gx:coord";
|
private static final String TAG_GX_COORD = "gx:coord";
|
||||||
private static final String TAG_GX_MULTI_TRACK = "gx:MultiTrack";
|
private static final String TAG_GX_MULTI_TRACK = "gx:MultiTrack";
|
||||||
private static final String TAG_GX_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
private static final String TAG_GX_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
||||||
@@ -140,6 +136,10 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.trim();
|
description = content.trim();
|
||||||
}
|
}
|
||||||
|
} else if (localName.equals(TAG_ICON)) {
|
||||||
|
if (content != null) {
|
||||||
|
icon = content.trim();
|
||||||
|
}
|
||||||
} else if (localName.equals(TAG_VALUE)) {
|
} else if (localName.equals(TAG_VALUE)) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
category = content.trim();
|
||||||
@@ -168,6 +168,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
private void onWaypointStart() {
|
private void onWaypointStart() {
|
||||||
// Reset all Placemark variables
|
// Reset all Placemark variables
|
||||||
name = null;
|
name = null;
|
||||||
|
icon = null;
|
||||||
description = null;
|
description = null;
|
||||||
category = null;
|
category = null;
|
||||||
photoUrl = null;
|
photoUrl = null;
|
||||||
@@ -182,29 +183,16 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
* On waypoint end.
|
* On waypoint end.
|
||||||
*/
|
*/
|
||||||
private void onWaypointEnd() throws SAXException {
|
private void onWaypointEnd() throws SAXException {
|
||||||
if (waypointType == null) {
|
if (!WAYPOINT_STYLE.equals(waypointType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a waypoint if the waypointType matches
|
|
||||||
WaypointType type;
|
|
||||||
switch (waypointType) {
|
|
||||||
case WAYPOINT_STYLE:
|
|
||||||
type = WaypointType.WAYPOINT;
|
|
||||||
break;
|
|
||||||
case STATISTICS_STYLE:
|
|
||||||
type = WaypointType.STATISTICS;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (photoUrl != null) {
|
if (photoUrl != null) {
|
||||||
Uri uri = Uri.parse(photoUrl);
|
Uri uri = Uri.parse(photoUrl);
|
||||||
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
photoUrl = getPhotoUrl(uri.getLastPathSegment());
|
||||||
}
|
}
|
||||||
|
|
||||||
addWaypoint(type);
|
addWaypoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,7 +248,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
if (!hasSensorData) {
|
if (!hasSensorData) {
|
||||||
insertTrackPoint(location);
|
insertTrackPoint(location);
|
||||||
} else {
|
} else {
|
||||||
SensorDataSetLocation sensorDataSetLocation = new SensorDataSetLocation(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime()));
|
TrackPoint sensorDataSetLocation = new TrackPoint(location, new SensorDataSet(heartrate, cadence, power, SensorDataSet.DATA_UNAVAILABLE, location.getTime()));
|
||||||
insertTrackPoint(sensorDataSetLocation);
|
insertTrackPoint(sensorDataSetLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,13 +303,13 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
|
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
|
||||||
}
|
}
|
||||||
switch (sensorType) {
|
switch (sensorType) {
|
||||||
case POWER:
|
case KmlTrackWriter.SENSOR_TYPE_POWER:
|
||||||
powerList.add(value);
|
powerList.add(value);
|
||||||
break;
|
break;
|
||||||
case HEART_RATE:
|
case KmlTrackWriter.SENSOR_TYPE_HEART_RATE:
|
||||||
heartRateList.add(value);
|
heartRateList.add(value);
|
||||||
break;
|
break;
|
||||||
case CADENCE:
|
case KmlTrackWriter.SENSOR_TYPE_CADENCE:
|
||||||
cadenceList.add(value);
|
cadenceList.add(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -29,7 +28,7 @@ import java.io.InputStream;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
@@ -68,7 +67,7 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
Log.d(TAG, "Thread interrupted");
|
Log.d(TAG, "Thread interrupted");
|
||||||
cleanImport(trackId);
|
cleanImport(context, trackId);
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
String fileName = zipEntry.getName();
|
String fileName = zipEntry.getName();
|
||||||
@@ -76,7 +75,7 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
trackId = parseKml(zipInputStream);
|
trackId = parseKml(zipInputStream);
|
||||||
if (trackId == -1L) {
|
if (trackId == -1L) {
|
||||||
Log.d(TAG, "Unable to parse kml in kmz");
|
Log.d(TAG, "Unable to parse kml in kmz");
|
||||||
cleanImport(trackId);
|
cleanImport(context, trackId);
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -90,7 +89,7 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
return trackId;
|
return trackId;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Unable to import file", e);
|
Log.e(TAG, "Unable to import file", e);
|
||||||
cleanImport(trackId);
|
cleanImport(context, trackId);
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,20 +99,19 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
*
|
*
|
||||||
* @param trackId the trackId
|
* @param trackId the trackId
|
||||||
*/
|
*/
|
||||||
private void cleanImport(long trackId) {
|
private void cleanImport(Context context, long trackId) {
|
||||||
if (PreferencesUtils.isRecording(trackId)) {
|
if (PreferencesUtils.isRecording(trackId)) {
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importTrackId != -1L) {
|
if (importTrackId != -1L) {
|
||||||
File dir = FileUtils.getPhotoDir(importTrackId);
|
File dir = FileUtils.getPhotoDir(context, importTrackId);
|
||||||
if (FileUtils.isDirectory(dir)) {
|
if (dir.exists() && dir.isDirectory()) {
|
||||||
for (File file : dir.listFiles()) {
|
for (File file : dir.listFiles()) {
|
||||||
file.delete();
|
file.delete();
|
||||||
}
|
}
|
||||||
dir.delete();
|
dir.delete();
|
||||||
FileUtils.updateMediaScanner(context, Uri.fromFile(dir));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,8 +157,7 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File dir = FileUtils.getPhotoDir(importTrackId);
|
File dir = FileUtils.getPhotoDir(context, importTrackId);
|
||||||
FileUtils.ensureDirectoryExists(dir);
|
|
||||||
File file = new File(dir, fileName);
|
File file = new File(dir, fileName);
|
||||||
|
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
||||||
@@ -169,8 +166,6 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
while ((count = zipInputStream.read(buffer)) != -1) {
|
while ((count = zipInputStream.read(buffer)) != -1) {
|
||||||
fileOutputStream.write(buffer, 0, count);
|
fileOutputStream.write(buffer, 0, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileUtils.updateMediaScanner(context, Uri.fromFile(file));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +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.services;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.location.LocationListener;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles connection to {@link LocationManager}.
|
|
||||||
*
|
|
||||||
* @author Jimmy Shih
|
|
||||||
*/
|
|
||||||
public class LocationManagerConnector {
|
|
||||||
|
|
||||||
private final Handler handler;
|
|
||||||
private final LocationManager locationManager;
|
|
||||||
|
|
||||||
public LocationManagerConnector(Context context, Looper looper) {
|
|
||||||
this.handler = new Handler(looper);
|
|
||||||
|
|
||||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requests location updates.
|
|
||||||
*
|
|
||||||
* @param minTime the minimal time
|
|
||||||
* @param minDistance the minimal distance
|
|
||||||
* @param locationListener the location listener
|
|
||||||
*/
|
|
||||||
public void requestLocationUpdates(final long minTime, final float minDistance, final LocationListener locationListener) {
|
|
||||||
handler.post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
public void run() {
|
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, locationListener);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes location updates.
|
|
||||||
*
|
|
||||||
* @param locationListener the location listener
|
|
||||||
*/
|
|
||||||
public void removeLocationUpdates(final LocationListener locationListener) {
|
|
||||||
locationManager.removeUpdates(locationListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.services;
|
|||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -28,12 +29,10 @@ import android.location.LocationListener;
|
|||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.core.app.TaskStackBuilder;
|
import androidx.core.app.TaskStackBuilder;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -42,14 +41,14 @@ import java.util.concurrent.Executors;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||||
import de.dennisguse.opentracks.TrackListActivity;
|
import de.dennisguse.opentracks.TrackListActivity;
|
||||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.LocationFactory;
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.LocationIterator;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.Track;
|
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||||
import de.dennisguse.opentracks.content.Waypoint;
|
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
|
||||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||||
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
||||||
@@ -72,35 +71,26 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
*/
|
*/
|
||||||
public class TrackRecordingService extends Service {
|
public class TrackRecordingService extends Service {
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 123;
|
|
||||||
|
|
||||||
public static final double PAUSE_LATITUDE = 100.0;
|
|
||||||
public static final double RESUME_LATITUDE = 200.0;
|
|
||||||
|
|
||||||
// Anything faster than that (in meters per second) will be considered moving.
|
// Anything faster than that (in meters per second) will be considered moving.
|
||||||
public static final double MAX_NO_MOVEMENT_SPEED = 0.224;
|
|
||||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||||
// 1 minute in milliseconds
|
|
||||||
private static final long ONE_MINUTE = (long) (UnitConversions.MIN_TO_S * UnitConversions.S_TO_MS);
|
public static final double MAX_NO_MOVEMENT_SPEED = 0.224;
|
||||||
|
|
||||||
// The following variables are set in onCreate:
|
// The following variables are set in onCreate:
|
||||||
private ExecutorService executorService;
|
private ExecutorService executorService;
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private Handler handler;
|
private LocationManager locationManager;
|
||||||
private LocationManagerConnector locationManagerConnector;
|
|
||||||
private PeriodicTaskExecutor voiceExecutor;
|
private PeriodicTaskExecutor voiceExecutor;
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
private TrackRecordingServiceNotificationManager notificationManager;
|
private TrackRecordingServiceNotificationManager notificationManager;
|
||||||
|
private LocationListenerPolicy locationListenerPolicy;
|
||||||
|
|
||||||
private long recordingTrackId;
|
private long recordingTrackId;
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
private LocationListenerPolicy locationListenerPolicy;
|
|
||||||
private int recordingDistanceInterval;
|
private int recordingDistanceInterval;
|
||||||
private int maxRecordingDistance;
|
private int maxRecordingDistance;
|
||||||
private int recordingGpsAccuracy;
|
private int recordingGpsAccuracy;
|
||||||
private long currentRecordingInterval;
|
private long currentRecordingInterval;
|
||||||
|
|
||||||
// The following variables are set when recording:
|
|
||||||
private TripStatisticsUpdater trackTripStatisticsUpdater;
|
|
||||||
// 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
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||||
@@ -125,12 +115,12 @@ public class TrackRecordingService extends Service {
|
|||||||
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
|
int minRecordingInterval = PreferencesUtils.getMinRecordingInterval(context);
|
||||||
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
|
if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptBatteryLife(context)) {
|
||||||
// Choose battery life over moving time accuracy.
|
// Choose battery life over moving time accuracy.
|
||||||
locationListenerPolicy = new AdaptiveLocationListenerPolicy(30 * UnitConversions.ONE_SECOND, 5 * ONE_MINUTE, 5);
|
locationListenerPolicy = new AdaptiveLocationListenerPolicy(30 * UnitConversions.ONE_SECOND_MS, 5 * UnitConversions.ONE_MINUTE_MS, 5);
|
||||||
} else if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptAccuracy(context)) {
|
} else if (minRecordingInterval == PreferencesUtils.getMinRecordingIntervalAdaptAccuracy(context)) {
|
||||||
// Get all the updates.
|
// Get all the updates.
|
||||||
locationListenerPolicy = new AdaptiveLocationListenerPolicy(UnitConversions.ONE_SECOND, 30 * UnitConversions.ONE_SECOND, 0);
|
locationListenerPolicy = new AdaptiveLocationListenerPolicy(UnitConversions.ONE_SECOND_MS, 30 * UnitConversions.ONE_SECOND_MS, 0);
|
||||||
} else {
|
} else {
|
||||||
locationListenerPolicy = new AbsoluteLocationListenerPolicy(minRecordingInterval * UnitConversions.ONE_SECOND);
|
locationListenerPolicy = new AbsoluteLocationListenerPolicy(minRecordingInterval * UnitConversions.ONE_SECOND_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||||
@@ -145,20 +135,21 @@ public class TrackRecordingService extends Service {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The following variables are set when recording:
|
||||||
private WakeLock wakeLock;
|
private WakeLock wakeLock;
|
||||||
private BluetoothRemoteSensorManager remoteSensorManager;
|
private BluetoothRemoteSensorManager remoteSensorManager;
|
||||||
|
|
||||||
|
private TripStatisticsUpdater trackTripStatisticsUpdater;
|
||||||
private Location lastLocation;
|
private Location lastLocation;
|
||||||
private boolean currentSegmentHasLocation;
|
private boolean currentSegmentHasLocation;
|
||||||
private boolean isIdle; // true if idle
|
private boolean isIdle;
|
||||||
private ServiceBinder binder = new ServiceBinder(this);
|
|
||||||
private LocationListener locationListener = new LocationListener() {
|
private TrackRecordingServiceBinder binder = new TrackRecordingServiceBinder(this);
|
||||||
|
private final LocationListener locationListener = new LocationListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLocationChanged(final Location location) {
|
public void onLocationChanged(final Location location) {
|
||||||
if (locationManagerConnector == null
|
if (executorService == null || executorService.isShutdown() || executorService.isTerminated()) {
|
||||||
|| executorService == null
|
|
||||||
|| executorService.isShutdown()
|
|
||||||
|| executorService.isTerminated()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
executorService.submit(new Runnable() {
|
executorService.submit(new Runnable() {
|
||||||
@@ -185,35 +176,21 @@ public class TrackRecordingService extends Service {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Runnable registerLocationRunnable = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isRecording() && !isPaused()) {
|
|
||||||
registerLocationListener();
|
|
||||||
}
|
|
||||||
handler.postDelayed(this, ONE_MINUTE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
executorService = Executors.newSingleThreadExecutor();
|
executorService = Executors.newSingleThreadExecutor();
|
||||||
contentProviderUtils = new ContentProviderUtils(this);
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
handler = new Handler();
|
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||||
locationManagerConnector = new LocationManagerConnector(this, handler.getLooper());
|
|
||||||
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
|
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||||
|
|
||||||
// onSharedPreferenceChanged might not set recordingTrackId.
|
// onSharedPreferenceChanged might not set recordingTrackId.
|
||||||
recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||||
|
|
||||||
// Require voiceExecutor and splitExecutor to be created.
|
PreferencesUtils.register(this, sharedPreferenceChangeListener);
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||||
|
|
||||||
handler.post(registerLocationRunnable);
|
|
||||||
|
|
||||||
// Try to restart the previous recording track in case the service has been restarted by the system, which can sometimes happen.
|
// Try to restart the previous recording track in case the service has been restarted by the system, which can sometimes happen.
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
||||||
@@ -248,11 +225,10 @@ public class TrackRecordingService extends Service {
|
|||||||
// Reverse order from onCreate
|
// Reverse order from onCreate
|
||||||
showNotification(false); //TODO Why?
|
showNotification(false); //TODO Why?
|
||||||
|
|
||||||
handler.removeCallbacks(registerLocationRunnable);
|
|
||||||
unregisterLocationListener();
|
unregisterLocationListener();
|
||||||
|
locationManager = null;
|
||||||
|
|
||||||
// unregister sharedPreferences before shutting down splitExecutor and voiceExecutor
|
PreferencesUtils.unregister(this, sharedPreferenceChangeListener);
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
voiceExecutor.shutdown();
|
voiceExecutor.shutdown();
|
||||||
@@ -260,14 +236,13 @@ public class TrackRecordingService extends Service {
|
|||||||
voiceExecutor = null;
|
voiceExecutor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
locationManagerConnector = null;
|
|
||||||
contentProviderUtils = null;
|
contentProviderUtils = null;
|
||||||
|
|
||||||
binder.detachFromService();
|
binder.detachFromService();
|
||||||
binder = null;
|
binder = null;
|
||||||
|
|
||||||
// This should be the next to last operation
|
// This should be the next to last operation
|
||||||
releaseWakeLock();
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
|
|
||||||
// Shutdown the executorService last to avoid sending events to a dead executor.
|
// Shutdown the executorService last to avoid sending events to a dead executor.
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
@@ -282,6 +257,10 @@ public class TrackRecordingService extends Service {
|
|||||||
return recordingTrackPaused;
|
return recordingTrackPaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getRecordingTrackId() {
|
||||||
|
return recordingTrackId;
|
||||||
|
}
|
||||||
|
|
||||||
public TripStatistics getTripStatistics() {
|
public TripStatistics getTripStatistics() {
|
||||||
if (trackTripStatisticsUpdater == null) {
|
if (trackTripStatisticsUpdater == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -289,55 +268,53 @@ public class TrackRecordingService extends Service {
|
|||||||
return trackTripStatisticsUpdater.getTripStatistics();
|
return trackTripStatisticsUpdater.getTripStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getTotalTime() {
|
||||||
|
if (trackTripStatisticsUpdater == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!isPaused()) {
|
||||||
|
trackTripStatisticsUpdater.updateTime(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
return trackTripStatisticsUpdater.getTripStatistics().getTotalTime();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a waypoint.
|
* Inserts a waypoint.
|
||||||
*
|
*
|
||||||
* @param waypointCreationRequest the waypoint creation request
|
|
||||||
* @return the waypoint id
|
* @return the waypoint id
|
||||||
*/
|
*/
|
||||||
public long insertWaypoint(WaypointCreationRequest waypointCreationRequest) {
|
public long insertWaypoint(String name, String category, String description, String photoUrl) {
|
||||||
if (!isRecording() || isPaused()) {
|
if (!isRecording() || isPaused()) {
|
||||||
return -1L;
|
return -1L;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = waypointCreationRequest.getName();
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
int nextWaypointNumber = contentProviderUtils.getNextWaypointNumber(recordingTrackId, waypointCreationRequest.getType());
|
int nextWaypointNumber = contentProviderUtils.getNextWaypointNumber(recordingTrackId);
|
||||||
if (nextWaypointNumber == -1) {
|
if (nextWaypointNumber == -1) {
|
||||||
nextWaypointNumber = 0;
|
nextWaypointNumber = 1;
|
||||||
}
|
}
|
||||||
name = getString(R.string.marker_name_format, nextWaypointNumber);
|
name = getString(R.string.marker_name_format, nextWaypointNumber + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
String category = waypointCreationRequest.getCategory() != null ? waypointCreationRequest.getCategory() : "";
|
|
||||||
String description = waypointCreationRequest.getDescription() != null ? waypointCreationRequest.getDescription() : "";
|
|
||||||
String icon = getString(R.string.marker_waypoint_icon_url);
|
|
||||||
|
|
||||||
double length;
|
|
||||||
long duration;
|
|
||||||
Location location = getLastValidTrackPointInCurrentSegment(recordingTrackId);
|
Location location = getLastValidTrackPointInCurrentSegment(recordingTrackId);
|
||||||
if (location != null && trackTripStatisticsUpdater != null) {
|
if (location == null) {
|
||||||
TripStatistics stats = trackTripStatisticsUpdater.getTripStatistics();
|
Log.i(TAG, "Could not create a waypoint as location is unknown.");
|
||||||
length = stats.getTotalDistance();
|
return -1L;
|
||||||
duration = stats.getTotalTime();
|
|
||||||
} else {
|
|
||||||
if (!waypointCreationRequest.isTrackStatistics()) {
|
|
||||||
return -1L;
|
|
||||||
}
|
|
||||||
// For track statistics, make it an impossible location
|
|
||||||
location = new Location("");
|
|
||||||
location.setLatitude(100);
|
|
||||||
location.setLongitude(180);
|
|
||||||
length = 0.0;
|
|
||||||
duration = 0L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String photoUrl = waypointCreationRequest.getPhotoUrl() != null ? waypointCreationRequest.getPhotoUrl() : "";
|
category = category != null ? category : "";
|
||||||
|
description = description != null ? description : "";
|
||||||
|
String icon = getString(R.string.marker_waypoint_icon_url);
|
||||||
|
photoUrl = photoUrl != null ? photoUrl : "";
|
||||||
|
|
||||||
|
TripStatistics stats = trackTripStatisticsUpdater.getTripStatistics();
|
||||||
|
double length = stats.getTotalDistance();
|
||||||
|
long duration = stats.getTotalTime();
|
||||||
|
|
||||||
// Insert waypoint
|
// Insert waypoint
|
||||||
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, waypointCreationRequest.getType(), length, duration, -1L, -1L, location, null, photoUrl);
|
Waypoint waypoint = new Waypoint(name, description, category, icon, recordingTrackId, length, duration, location, photoUrl);
|
||||||
Uri uri = contentProviderUtils.insertWaypoint(waypoint);
|
Uri uri = contentProviderUtils.insertWaypoint(waypoint);
|
||||||
return Long.parseLong(uri.getLastPathSegment());
|
return ContentUris.parseId(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -345,7 +322,7 @@ public class TrackRecordingService extends Service {
|
|||||||
*
|
*
|
||||||
* @return the track id
|
* @return the track id
|
||||||
*/
|
*/
|
||||||
private long startNewTrack() {
|
long startNewTrack() {
|
||||||
if (isRecording()) {
|
if (isRecording()) {
|
||||||
Log.d(TAG, "Ignore startNewTrack. Already recording.");
|
Log.d(TAG, "Ignore startNewTrack. Already recording.");
|
||||||
return -1L;
|
return -1L;
|
||||||
@@ -356,7 +333,7 @@ public class TrackRecordingService extends Service {
|
|||||||
// Insert a track
|
// Insert a track
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
Uri uri = contentProviderUtils.insertTrack(track);
|
Uri uri = contentProviderUtils.insertTrack(track);
|
||||||
long trackId = Long.parseLong(uri.getLastPathSegment());
|
long trackId = ContentUris.parseId(uri);
|
||||||
|
|
||||||
// Update shared preferences
|
// Update shared preferences
|
||||||
updateRecordingState(trackId, false);
|
updateRecordingState(trackId, false);
|
||||||
@@ -371,40 +348,25 @@ public class TrackRecordingService extends Service {
|
|||||||
track.setTripStatistics(trackTripStatisticsUpdater.getTripStatistics());
|
track.setTripStatistics(trackTripStatisticsUpdater.getTripStatistics());
|
||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
|
|
||||||
//TODO Do not use insertWaypoint
|
startRecording();
|
||||||
insertWaypoint(WaypointCreationRequest.DEFAULT_START_TRACK);
|
|
||||||
|
|
||||||
startRecording(true);
|
|
||||||
return trackId;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Restart a track.
|
|
||||||
*
|
|
||||||
* @param track the track
|
|
||||||
*/
|
|
||||||
private void restartTrack(Track track) {
|
private void restartTrack(Track track) {
|
||||||
Log.d(TAG, "Restarting track: " + track.getId());
|
Log.d(TAG, "Restarting track: " + track.getId());
|
||||||
|
|
||||||
TripStatistics tripStatistics = track.getTripStatistics();
|
TripStatistics tripStatistics = track.getTripStatistics();
|
||||||
trackTripStatisticsUpdater = new TripStatisticsUpdater(tripStatistics.getStartTime());
|
trackTripStatisticsUpdater = new TripStatisticsUpdater(tripStatistics.getStartTime());
|
||||||
|
|
||||||
try (LocationIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, LocationFactory.DEFAULT_LOCATION_FACTORY)) {
|
try (TrackPointIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, TrackPointFactory.DEFAULT_LOCATION_FACTORY)) {
|
||||||
|
trackTripStatisticsUpdater.addLocation(locationIterator, recordingDistanceInterval);
|
||||||
while (locationIterator.hasNext()) {
|
|
||||||
Location location = locationIterator.next();
|
|
||||||
trackTripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
|
||||||
}
|
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "RuntimeException", e);
|
Log.e(TAG, "RuntimeException", e);
|
||||||
}
|
}
|
||||||
startRecording(true);
|
startRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void resumeCurrentTrack() {
|
||||||
* Resumes current track.
|
|
||||||
*/
|
|
||||||
private void resumeCurrentTrack() {
|
|
||||||
if (!isRecording() || !isPaused()) {
|
if (!isRecording() || !isPaused()) {
|
||||||
Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
|
Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
|
||||||
return;
|
return;
|
||||||
@@ -419,20 +381,18 @@ public class TrackRecordingService extends Service {
|
|||||||
if (track != null) {
|
if (track != null) {
|
||||||
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
||||||
resume.setLongitude(0);
|
resume.setLongitude(0);
|
||||||
resume.setLatitude(RESUME_LATITUDE);
|
resume.setLatitude(TrackPointsColumns.RESUME_LATITUDE);
|
||||||
resume.setTime(System.currentTimeMillis());
|
resume.setTime(System.currentTimeMillis());
|
||||||
insertLocation(track, resume, null);
|
insertLocation(track, resume, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
startRecording(false);
|
startRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common code for starting a new track, resuming a track, or restarting after phone reboot.
|
* Common code for starting a new track, resuming a track, or restarting after phone reboot.
|
||||||
*
|
|
||||||
* @param trackStarted true if track is started, false if track is resumed
|
|
||||||
*/
|
*/
|
||||||
private void startRecording(boolean trackStarted) {
|
private void startRecording() {
|
||||||
// Update instance variables
|
// Update instance variables
|
||||||
remoteSensorManager = new BluetoothRemoteSensorManager(this);
|
remoteSensorManager = new BluetoothRemoteSensorManager(this);
|
||||||
remoteSensorManager.start();
|
remoteSensorManager.start();
|
||||||
@@ -446,19 +406,19 @@ public class TrackRecordingService extends Service {
|
|||||||
voiceExecutor.restore();
|
voiceExecutor.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void tryStartGps() {
|
||||||
* Starts gps.
|
if (isRecording()) return;
|
||||||
*/
|
|
||||||
|
startGps();
|
||||||
|
}
|
||||||
|
|
||||||
private void startGps() {
|
private void startGps() {
|
||||||
wakeLock = SystemUtils.acquireWakeLock(this, wakeLock);
|
wakeLock = SystemUtils.acquireWakeLock(this, wakeLock);
|
||||||
registerLocationListener();
|
registerLocationListener();
|
||||||
showNotification(true);
|
showNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void endCurrentTrack() {
|
||||||
* Ends the current track.
|
|
||||||
*/
|
|
||||||
private void endCurrentTrack() {
|
|
||||||
if (!isRecording()) {
|
if (!isRecording()) {
|
||||||
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
|
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
|
||||||
return;
|
return;
|
||||||
@@ -479,7 +439,7 @@ public class TrackRecordingService extends Service {
|
|||||||
insertLocation(track, lastLocation, getLastValidTrackPointInCurrentSegment(trackId));
|
insertLocation(track, lastLocation, getLastValidTrackPointInCurrentSegment(trackId));
|
||||||
|
|
||||||
// Update the recording track time
|
// Update the recording track time
|
||||||
updateRecordingTrack(track, contentProviderUtils.getLastTrackPointId(trackId), false);
|
updateRecordingTrack(track, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
String trackName = TrackNameUtils.getTrackName(this, trackId, track.getTripStatistics().getStartTime());
|
String trackName = TrackNameUtils.getTrackName(this, trackId, track.getTripStatistics().getStartTime());
|
||||||
@@ -491,10 +451,7 @@ public class TrackRecordingService extends Service {
|
|||||||
endRecording(true);
|
endRecording(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void pauseCurrentTrack() {
|
||||||
* Pauses the current track.
|
|
||||||
*/
|
|
||||||
private void pauseCurrentTrack() {
|
|
||||||
if (!isRecording() || isPaused()) {
|
if (!isRecording() || isPaused()) {
|
||||||
Log.d(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
|
Log.d(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
|
||||||
return;
|
return;
|
||||||
@@ -511,7 +468,7 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||||
pause.setLongitude(0);
|
pause.setLongitude(0);
|
||||||
pause.setLatitude(PAUSE_LATITUDE);
|
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
|
||||||
pause.setTime(System.currentTimeMillis());
|
pause.setTime(System.currentTimeMillis());
|
||||||
insertLocation(track, pause, null);
|
insertLocation(track, pause, null);
|
||||||
}
|
}
|
||||||
@@ -543,13 +500,15 @@ public class TrackRecordingService extends Service {
|
|||||||
/**
|
/**
|
||||||
* Stops gps.
|
* Stops gps.
|
||||||
*
|
*
|
||||||
* @param stop true to stop self
|
* @param shutdown true to shutdown self
|
||||||
*/
|
*/
|
||||||
private void stopGps(boolean stop) {
|
void stopGps(boolean shutdown) {
|
||||||
|
if (!isRecording()) return;
|
||||||
|
|
||||||
unregisterLocationListener();
|
unregisterLocationListener();
|
||||||
showNotification(false);
|
showNotification(false);
|
||||||
releaseWakeLock();
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
if (stop) {
|
if (shutdown) {
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -580,107 +539,94 @@ public class TrackRecordingService extends Service {
|
|||||||
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
|
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void onLocationChangedAsync(Location location) {
|
||||||
* Called when location changed.
|
if (!isRecording() || isPaused()) {
|
||||||
*
|
Log.w(TAG, "Ignore onLocationChangedAsync. Not recording or paused.");
|
||||||
* @param location the location
|
return;
|
||||||
*/
|
|
||||||
private void onLocationChangedAsync(Location location) {
|
|
||||||
try {
|
|
||||||
if (!isRecording() || isPaused()) {
|
|
||||||
Log.w(TAG, "Ignore onLocationChangedAsync. Not recording or paused.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
|
||||||
if (track == null) {
|
|
||||||
Log.w(TAG, "Ignore onLocationChangedAsync. No track.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LocationUtils.isValidLocation(location)) {
|
|
||||||
Log.w(TAG, "Ignore onLocationChangedAsync. location is invalid.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.updateLocation(this, location, recordingGpsAccuracy);
|
|
||||||
|
|
||||||
if (!location.hasAccuracy() || location.getAccuracy() >= recordingGpsAccuracy) {
|
|
||||||
Log.d(TAG, "Ignore onLocationChangedAsync. Poor accuracy.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO Necessary?
|
|
||||||
// Fix for phones that do not set the time field
|
|
||||||
if (location.getTime() == 0L) {
|
|
||||||
location.setTime(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
Location lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
|
|
||||||
long idleTime = 0L;
|
|
||||||
if (lastValidTrackPoint != null && location.getTime() > lastValidTrackPoint.getTime()) {
|
|
||||||
idleTime = location.getTime() - lastValidTrackPoint.getTime();
|
|
||||||
}
|
|
||||||
locationListenerPolicy.updateIdleTime(idleTime);
|
|
||||||
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
|
|
||||||
registerLocationListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
SensorDataSet sensorDataSet = getSensorDataSet();
|
|
||||||
if (sensorDataSet != null) {
|
|
||||||
location = new SensorDataSetLocation(location, sensorDataSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always insert the first segment location
|
|
||||||
if (!currentSegmentHasLocation) {
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
currentSegmentHasLocation = true;
|
|
||||||
lastLocation = location;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!LocationUtils.isValidLocation(lastValidTrackPoint)) {
|
|
||||||
// Should not happen. The current segment should have a location. Just insert the current location.
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
lastLocation = location;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double distanceToLastTrackLocation = location.distanceTo(lastValidTrackPoint);
|
|
||||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
|
||||||
insertLocation(track, lastLocation, lastValidTrackPoint);
|
|
||||||
|
|
||||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
|
||||||
pause.setLongitude(0);
|
|
||||||
pause.setLatitude(PAUSE_LATITUDE);
|
|
||||||
pause.setTime(lastLocation.getTime());
|
|
||||||
insertLocation(track, pause, null);
|
|
||||||
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
isIdle = false;
|
|
||||||
} else if (sensorDataSet != null || distanceToLastTrackLocation >= recordingDistanceInterval) {
|
|
||||||
insertLocation(track, lastLocation, lastValidTrackPoint);
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
isIdle = false;
|
|
||||||
} else if (!isIdle && location.hasSpeed() && location.getSpeed() < MAX_NO_MOVEMENT_SPEED) {
|
|
||||||
insertLocation(track, lastLocation, lastValidTrackPoint);
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
isIdle = true;
|
|
||||||
} else if (isIdle && location.hasSpeed() && location.getSpeed() >= MAX_NO_MOVEMENT_SPEED) {
|
|
||||||
insertLocation(track, lastLocation, lastValidTrackPoint);
|
|
||||||
insertLocation(track, location, null);
|
|
||||||
isIdle = false;
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Not recording location, idle");
|
|
||||||
}
|
|
||||||
lastLocation = location;
|
|
||||||
} catch (Error e) {
|
|
||||||
Log.e(TAG, "Error in onLocationChangedAsync", e);
|
|
||||||
throw e;
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Log.e(TAG, "RuntimeException in onLocationChangedAsync", e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
||||||
|
if (track == null) {
|
||||||
|
Log.w(TAG, "Ignore onLocationChangedAsync. No track.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LocationUtils.isValidLocation(location)) {
|
||||||
|
Log.w(TAG, "Ignore onLocationChangedAsync. location is invalid.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationManager.updateLocation(this, location, recordingGpsAccuracy);
|
||||||
|
|
||||||
|
if (!location.hasAccuracy() || location.getAccuracy() >= recordingGpsAccuracy) {
|
||||||
|
Log.d(TAG, "Ignore onLocationChangedAsync. Poor accuracy.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Necessary?
|
||||||
|
// Fix for phones that do not set the time field
|
||||||
|
if (location.getTime() == 0L) {
|
||||||
|
location.setTime(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
Location lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
|
||||||
|
long idleTime = 0L;
|
||||||
|
if (lastValidTrackPoint != null && location.getTime() > lastValidTrackPoint.getTime()) {
|
||||||
|
idleTime = location.getTime() - lastValidTrackPoint.getTime();
|
||||||
|
}
|
||||||
|
locationListenerPolicy.updateIdleTime(idleTime);
|
||||||
|
if (currentRecordingInterval != locationListenerPolicy.getDesiredPollingInterval()) {
|
||||||
|
registerLocationListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
SensorDataSet sensorDataSet = getSensorDataSet();
|
||||||
|
if (sensorDataSet != null) {
|
||||||
|
location = new TrackPoint(location, sensorDataSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always insert the first segment location
|
||||||
|
if (!currentSegmentHasLocation) {
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
currentSegmentHasLocation = true;
|
||||||
|
lastLocation = location;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LocationUtils.isValidLocation(lastValidTrackPoint)) {
|
||||||
|
// Should not happen. The current segment should have a location. Just insert the current location.
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
lastLocation = location;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double distanceToLastTrackLocation = location.distanceTo(lastValidTrackPoint);
|
||||||
|
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
||||||
|
insertLocation(track, lastLocation, lastValidTrackPoint);
|
||||||
|
|
||||||
|
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||||
|
pause.setLongitude(0);
|
||||||
|
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
|
||||||
|
pause.setTime(lastLocation.getTime());
|
||||||
|
insertLocation(track, pause, null);
|
||||||
|
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
isIdle = false;
|
||||||
|
} else if (sensorDataSet != null || distanceToLastTrackLocation >= recordingDistanceInterval) {
|
||||||
|
insertLocation(track, lastLocation, lastValidTrackPoint);
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
isIdle = false;
|
||||||
|
} else if (!isIdle && location.hasSpeed() && location.getSpeed() < MAX_NO_MOVEMENT_SPEED) {
|
||||||
|
insertLocation(track, lastLocation, lastValidTrackPoint);
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
isIdle = true;
|
||||||
|
} else if (isIdle && location.hasSpeed() && location.getSpeed() >= MAX_NO_MOVEMENT_SPEED) {
|
||||||
|
insertLocation(track, lastLocation, lastValidTrackPoint);
|
||||||
|
insertLocation(track, location, null);
|
||||||
|
isIdle = false;
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Not recording location, idle");
|
||||||
|
}
|
||||||
|
lastLocation = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -702,10 +648,9 @@ public class TrackRecordingService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Uri uri = contentProviderUtils.insertTrackPoint(location, track.getId());
|
contentProviderUtils.insertTrackPoint(location, track.getId());
|
||||||
long trackPointId = Long.parseLong(uri.getLastPathSegment());
|
|
||||||
trackTripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
trackTripStatisticsUpdater.addLocation(location, recordingDistanceInterval);
|
||||||
updateRecordingTrack(track, trackPointId, LocationUtils.isValidLocation(location));
|
updateRecordingTrack(track, LocationUtils.isValidLocation(location));
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
/*
|
/*
|
||||||
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
|
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
|
||||||
@@ -717,20 +662,13 @@ public class TrackRecordingService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the recording track time as well as the startId and the stopId.
|
* Updates the recording track time.
|
||||||
* Increase the number of points if it is a new and valid track point.
|
* Increase the number of points if it is a new and valid track point.
|
||||||
*
|
*
|
||||||
* @param track the track
|
* @param track the track
|
||||||
* @param lastTrackPointId the last track point id
|
|
||||||
* @param increaseNumberOfPoints true to increase the number of points
|
* @param increaseNumberOfPoints true to increase the number of points
|
||||||
*/
|
*/
|
||||||
private void updateRecordingTrack(Track track, long lastTrackPointId, boolean increaseNumberOfPoints) {
|
private void updateRecordingTrack(Track track, boolean increaseNumberOfPoints) {
|
||||||
if (lastTrackPointId >= 0) {
|
|
||||||
if (track.getStartId() < 0) {
|
|
||||||
track.setStartId(lastTrackPointId);
|
|
||||||
}
|
|
||||||
track.setStopId(lastTrackPointId);
|
|
||||||
}
|
|
||||||
if (increaseNumberOfPoints) {
|
if (increaseNumberOfPoints) {
|
||||||
track.setNumberOfPoints(track.getNumberOfPoints() + 1);
|
track.setNumberOfPoints(track.getNumberOfPoints() + 1);
|
||||||
}
|
}
|
||||||
@@ -740,57 +678,37 @@ public class TrackRecordingService extends Service {
|
|||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SensorDataSet getSensorDataSet() {
|
SensorDataSet getSensorDataSet() {
|
||||||
if (remoteSensorManager == null || !remoteSensorManager.isEnabled() || !remoteSensorManager.isSensorDataSetValid()) {
|
if (remoteSensorManager == null || !remoteSensorManager.isEnabled() || !remoteSensorManager.isSensorDataSetValid()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return remoteSensorManager.getSensorDataSet();
|
return remoteSensorManager.getSensorDataSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the location listener.
|
|
||||||
*/
|
|
||||||
private void registerLocationListener() {
|
private void registerLocationListener() {
|
||||||
if (locationManagerConnector == null) {
|
if (locationManager == null) {
|
||||||
Log.e(TAG, "locationManager is null.");
|
Log.e(TAG, "locationManager is null.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
long interval = locationListenerPolicy.getDesiredPollingInterval();
|
long interval = locationListenerPolicy.getDesiredPollingInterval();
|
||||||
locationManagerConnector.requestLocationUpdates(interval, locationListenerPolicy.getMinDistance_m(), locationListener);
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, locationListenerPolicy.getMinDistance_m(), locationListener);
|
||||||
currentRecordingInterval = interval;
|
currentRecordingInterval = interval;
|
||||||
} catch (RuntimeException e) {
|
} catch (SecurityException e) {
|
||||||
Log.e(TAG, "Could not register location listener.", e);
|
Log.e(TAG, "Could not register location listener; permissions not granted.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Unregisters the location manager.
|
|
||||||
*/
|
|
||||||
private void unregisterLocationListener() {
|
private void unregisterLocationListener() {
|
||||||
if (locationManagerConnector == null) {
|
if (locationManager == null) {
|
||||||
Log.e(TAG, "locationManager is null.");
|
Log.e(TAG, "locationManager is null.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
locationManagerConnector.removeLocationUpdates(locationListener);
|
locationManager.removeUpdates(locationListener);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the wake lock.
|
|
||||||
*/
|
|
||||||
private void releaseWakeLock() {
|
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
|
||||||
wakeLock.release();
|
|
||||||
wakeLock = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNotification(boolean isGpsStarted) {
|
private void showNotification(boolean isGpsStarted) {
|
||||||
if ((isRecording() && isPaused()) || (!isRecording() && !isGpsStarted)) {
|
if (isRecording()) {
|
||||||
stopForeground(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isRecording() && !isPaused()) {
|
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
||||||
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
|
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
|
||||||
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
||||||
@@ -800,7 +718,7 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
notificationManager.updatePendingIntent(pendingIntent);
|
notificationManager.updatePendingIntent(pendingIntent);
|
||||||
notificationManager.updateContent(getString(R.string.gps_starting));
|
notificationManager.updateContent(getString(R.string.gps_starting));
|
||||||
startForeground(NOTIFICATION_ID, notificationManager.getNotification());
|
startForeground(TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.getNotification());
|
||||||
}
|
}
|
||||||
if (!isRecording() && isGpsStarted) {
|
if (!isRecording() && isGpsStarted) {
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
||||||
@@ -810,109 +728,11 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
notificationManager.updatePendingIntent(pendingIntent);
|
notificationManager.updatePendingIntent(pendingIntent);
|
||||||
notificationManager.updateContent(getString(R.string.gps_starting));
|
notificationManager.updateContent(getString(R.string.gps_starting));
|
||||||
|
startForeground(TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.getNotification());
|
||||||
startForeground(NOTIFICATION_ID, notificationManager.getNotification());
|
|
||||||
}
|
}
|
||||||
}
|
if (!isRecording() && !isGpsStarted) {
|
||||||
|
stopForeground(true);
|
||||||
/**
|
notificationManager.cancelNotification();
|
||||||
* TODO: There is a bug in Android that leaks Binder instances. This bug is
|
|
||||||
* especially visible if we have a non-static class, as there is no way to
|
|
||||||
* nullify reference to the outer class (the service). A workaround is to use
|
|
||||||
* a static class and explicitly clear service and detach it from the
|
|
||||||
* underlying Binder. With this approach, we minimize the leak to 24 bytes per
|
|
||||||
* each service instance. For more details, see the following bug:
|
|
||||||
* http://code.google.com/p/android/issues/detail?id=6426.
|
|
||||||
*/
|
|
||||||
private static class ServiceBinder extends android.os.Binder implements ITrackRecordingService {
|
|
||||||
private TrackRecordingService trackRecordingService;
|
|
||||||
|
|
||||||
public ServiceBinder(TrackRecordingService trackRecordingService) {
|
|
||||||
this.trackRecordingService = trackRecordingService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startGps() {
|
|
||||||
if (!trackRecordingService.isRecording()) {
|
|
||||||
trackRecordingService.startGps();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopGps() {
|
|
||||||
if (!trackRecordingService.isRecording()) {
|
|
||||||
trackRecordingService.stopGps(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long startNewTrack() {
|
|
||||||
return trackRecordingService.startNewTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pauseCurrentTrack() {
|
|
||||||
trackRecordingService.pauseCurrentTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void resumeCurrentTrack() {
|
|
||||||
trackRecordingService.resumeCurrentTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void endCurrentTrack() {
|
|
||||||
trackRecordingService.endCurrentTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRecording() {
|
|
||||||
return trackRecordingService.isRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPaused() {
|
|
||||||
return trackRecordingService.isPaused();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getRecordingTrackId() {
|
|
||||||
return trackRecordingService.recordingTrackId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getTotalTime() {
|
|
||||||
TripStatisticsUpdater updater = trackRecordingService.trackTripStatisticsUpdater;
|
|
||||||
if (updater == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!trackRecordingService.isPaused()) {
|
|
||||||
updater.updateTime(System.currentTimeMillis());
|
|
||||||
}
|
|
||||||
return updater.getTripStatistics().getTotalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long insertWaypoint(WaypointCreationRequest waypointCreationRequest) {
|
|
||||||
return trackRecordingService.insertWaypoint(waypointCreationRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
@Override
|
|
||||||
public void insertTrackPoint(Location location) {
|
|
||||||
trackRecordingService.onLocationChangedAsync(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SensorDataSet getSensorData() {
|
|
||||||
return trackRecordingService.getSensorDataSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Detaches from the track recording service. Clears the reference to the
|
|
||||||
* outer class to minimize the leak.
|
|
||||||
*/
|
|
||||||
private void detachFromService() {
|
|
||||||
trackRecordingService = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package de.dennisguse.opentracks.services;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: There is a bug in Android that leaks Binder instances. This bug is
|
||||||
|
* especially visible if we have a non-static class, as there is no way to
|
||||||
|
* nullify reference to the outer class (the service). A workaround is to use
|
||||||
|
* a static class and explicitly clear service and detach it from the
|
||||||
|
* underlying Binder. With this approach, we minimize the leak to 24 bytes per
|
||||||
|
* each service instance. For more details, see the following bug:
|
||||||
|
* http://code.google.com/p/android/issues/detail?id=6426.
|
||||||
|
*/
|
||||||
|
class TrackRecordingServiceBinder extends android.os.Binder implements TrackRecordingServiceInterface {
|
||||||
|
private TrackRecordingService trackRecordingService;
|
||||||
|
|
||||||
|
TrackRecordingServiceBinder(TrackRecordingService trackRecordingService) {
|
||||||
|
this.trackRecordingService = trackRecordingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startGps() {
|
||||||
|
trackRecordingService.tryStartGps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopGps() {
|
||||||
|
trackRecordingService.stopGps(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long startNewTrack() {
|
||||||
|
return trackRecordingService.startNewTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pauseCurrentTrack() {
|
||||||
|
trackRecordingService.pauseCurrentTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resumeCurrentTrack() {
|
||||||
|
trackRecordingService.resumeCurrentTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endCurrentTrack() {
|
||||||
|
trackRecordingService.endCurrentTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecording() {
|
||||||
|
return trackRecordingService.isRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPaused() {
|
||||||
|
return trackRecordingService.isPaused();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRecordingTrackId() {
|
||||||
|
return trackRecordingService.getRecordingTrackId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTotalTime() {
|
||||||
|
return trackRecordingService.getTotalTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long insertWaypoint(String name, String category, String description, String photoUrl) {
|
||||||
|
return trackRecordingService.insertWaypoint(name, category, description, photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
@Override
|
||||||
|
public void insertTrackPoint(Location location) {
|
||||||
|
trackRecordingService.onLocationChangedAsync(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SensorDataSet getSensorData() {
|
||||||
|
return trackRecordingService.getSensorDataSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches from the track recording service. Clears the reference to the
|
||||||
|
* outer class to minimize the leak.
|
||||||
|
*/
|
||||||
|
void detachFromService() {
|
||||||
|
trackRecordingService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user