mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-23 10:45:18 +02:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c0069394f | |||
| 379913fb3e | |||
| 542d9ccb77 | |||
| c8ae3b83b5 | |||
| 0a046fc100 | |||
| c551a38a8b | |||
| 72b1ca82ed | |||
| 1ef81c45db | |||
| 08e6ae76f2 | |||
| 6e410e049d | |||
| 22b7962940 | |||
| 5788f99b20 | |||
| 8685e43dc2 | |||
| b8bea27155 | |||
| c9226d3b79 | |||
| 9701feff04 | |||
| b8b40a88ef | |||
| adcf7b388b | |||
| 3a7562d96c | |||
| 2753cf7d0b | |||
| 5841415a0b | |||
| d79fa07834 | |||
| ff6caf706e | |||
| c953fbc67a | |||
| 55870cca85 | |||
| 0123242be6 | |||
| 81a6d7b53a | |||
| f423b07cee | |||
| 33231bdbb1 | |||
| 677b691ef1 | |||
| 5e74e80a9a | |||
| 70b5d799a2 | |||
| 16f18fe16f | |||
| 8538c79dd8 | |||
| f8e3b28e51 | |||
| 9f665e07fd | |||
| b7c010c086 | |||
| 8296c0194d | |||
| f537cc7321 | |||
| 4fa2401b27 | |||
| f30e3cd5cd | |||
| b73353120f | |||
| 7fa9df1f99 | |||
| ea3de8a987 | |||
| c159e1eba4 | |||
| 420d538a73 | |||
| 36672d6857 | |||
| 51dc6f8e8d | |||
| 9a6c9eeace | |||
| bfb7f781ec | |||
| 8ca24b877c | |||
| 01ef5cfa6f | |||
| 08825fcf04 | |||
| 4f5e87f87d | |||
| 45b1fafdd5 | |||
| e89258c173 | |||
| d47d37afd2 | |||
| d9effc80a7 | |||
| 03d2b05ffd | |||
| e2cac6ee60 | |||
| f749ddb5ae | |||
| 6bb409fed0 | |||
| 6da2d9d104 | |||
| cd267123e7 | |||
| 617ee719c8 | |||
| 4edad610ee | |||
| fda95bbec9 | |||
| 1028e09888 | |||
| 4abd07694c | |||
| a53b3288f7 | |||
| 7ba771c417 | |||
| 2a145b4791 | |||
| cf90462772 | |||
| d9a6701260 | |||
| 1dc9558541 | |||
| 972de5f92e | |||
| 28270b202e |
Generated
+116
@@ -0,0 +1,116 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
language: android
|
||||
dist: trusty
|
||||
|
||||
android:
|
||||
components:
|
||||
- build-tools-28.0.3
|
||||
- android-29
|
||||
|
||||
script:
|
||||
- ./gradlew build
|
||||
#TODO: When all tests are passing, CI should also execute them.
|
||||
#- ./gradlew connectedCheck
|
||||
@@ -0,0 +1,12 @@
|
||||
# OpenTrack: Developer information
|
||||
|
||||
## Create releases for FDroid
|
||||
|
||||
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
|
||||
2. Decide on _version name_ (semantic versioning)
|
||||
3. Manually update _version code_ and _version name_ in `AndroidManifest.xml` (used by FDroid)
|
||||
4. Create changelog (`_version code_.txt`)
|
||||
5. Create commit with all changes
|
||||
6. Push commits
|
||||
7. Tag newly create commit with _version name_
|
||||
8. Push the tagged to public repository
|
||||
@@ -16,7 +16,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
* __No advertising__
|
||||
* __Privacy:__
|
||||
* _contains in-app analytics_
|
||||
* _does not Google Play Services_
|
||||
* _does not have Google Play Services_
|
||||
* _has no cloud integration_
|
||||
|
||||
__Only required permissions:__
|
||||
@@ -28,7 +28,7 @@ To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://pl
|
||||
|
||||
## Project history
|
||||
|
||||
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/).
|
||||
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/)).
|
||||
Initially, _My Tracks_ was initially released by Google in 2010 as [open-source software](http://google-latlong.blogspot.fr/2010/05/code-for-my-tracks-is-now-yours.html).
|
||||
In 2016, [Google decided to discontinue](https://support.google.com/maps/answer/6333516) _My Tracks_ and stopped distributing it via the Google Play store in April 2016.
|
||||
The [Plonk42](https://github.com/plonk42) conducted some maintenance work until 2016, so _My Tracks_ could still be used (based upon version _Google's MyTracks_ version 2.0.6).
|
||||
|
||||
+25
-12
@@ -6,7 +6,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ def getVersionCode = { ->
|
||||
return Integer.parseInt(stdout.toString().trim())
|
||||
}
|
||||
catch (ignored) {
|
||||
return -1;
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,21 +41,22 @@ def getVersionName = { ->
|
||||
return stdout.toString().trim()
|
||||
}
|
||||
catch (ignored) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion 29
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
//Useful for debugging.
|
||||
//versionCode getVersionCode()
|
||||
//versionName getVersionName()
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
targetSdkVersion 29
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -67,21 +68,33 @@ android {
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled true
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
}
|
||||
|
||||
//TODO: Should be removed (when LINT does not throw any errors).
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||
androidTestImplementation 'androidx.test:core:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
}
|
||||
androidTestImplementation 'androidx.test:rules:1.2.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:3.0.0'
|
||||
|
||||
testImplementation 'androidx.test:core:1.2.0'
|
||||
testImplementation 'org.mockito:mockito-core:3.0.0'
|
||||
|
||||
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-2
@@ -1,6 +1,6 @@
|
||||
#Wed Jun 19 22:47:37 CEST 2019
|
||||
#Mon Aug 26 18:59:08 CEST 2019
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
@@ -6,42 +6,6 @@
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
@@ -60,6 +24,46 @@ cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -85,7 +89,7 @@ location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
@@ -150,11 +154,19 @@ if $cygwin ; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
Vendored
+4
-10
@@ -8,14 +8,14 @@
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
@@ -46,10 +46,9 @@ echo location of your Java installation.
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
@@ -60,11 +59,6 @@ set _SKIP=2
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.0.0: OpenTracks
|
||||
|
||||
First release of OpenTracks (i.e., hard fork from MyTracks)
|
||||
|
||||
Changes:
|
||||
- Rebranded from MyTracks to OpenTracks
|
||||
- Added Bluetooth LE heart rate sensor
|
||||
- Removed all features requiring Internet access
|
||||
- Removed Google Maps integration
|
||||
- Removed use of Google Play services
|
||||
- Removed ANT+ sensors
|
||||
- Removed Bluetooth classic sensors
|
||||
- Removed Protobuf
|
||||
- Removed calorie estimation and activity estimation
|
||||
- Removed MyTracks Service API (i.e., no integration with other apps)
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.0.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Share markers
|
||||
- Restructured settings
|
||||
- Removed actionbar back button
|
||||
- Export: removed photo size settings option
|
||||
- Export: KML as format is now selectable
|
||||
|
||||
Bugfixes:
|
||||
- export directory is now /OpenTracks/
|
||||
- landscape mode is not crashing
|
||||
- fixed KML/KMZ export
|
||||
|
||||
Developer:
|
||||
- Test are executable (but many failing)
|
||||
- Enabled continuous integration with TravisCI (no tests)
|
||||
@@ -0,0 +1,12 @@
|
||||
(tbd): OpenTracks
|
||||
|
||||
Changes:
|
||||
- Added show track in map apps (tested with OsmAnd and Maps.ME)
|
||||
- Removed landscape mode for track detail view (was buggy)
|
||||
- Removed search suggestions history
|
||||
|
||||
Bugfix:
|
||||
-
|
||||
|
||||
Developer:
|
||||
-
|
||||
@@ -1 +1 @@
|
||||
A sport tracker that completely respects your privacy
|
||||
A sport tracker that completely respects your privacy.
|
||||
@@ -24,14 +24,14 @@ import android.content.Intent;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
/**
|
||||
* Tests for the BootReceiver.
|
||||
*
|
||||
@@ -41,22 +41,24 @@ import java.util.List;
|
||||
public class BootReceiverTest {
|
||||
private static final String SERVICE_NAME = "de.dennisguse.opentracks.services.TrackRecordingService";
|
||||
|
||||
private Context context = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
|
||||
/**
|
||||
* Tests the behavior when receive notification which is the phone boot.
|
||||
*/
|
||||
@Test
|
||||
public void testOnReceive_startService() {
|
||||
// Make sure no TrackRecordingService
|
||||
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
||||
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||
Intent stopIntent = new Intent(context, TrackRecordingService.class);
|
||||
context.stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
|
||||
BootReceiver bootReceiver = new BootReceiver();
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_BOOT_COMPLETED);
|
||||
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
||||
bootReceiver.onReceive(context, intent);
|
||||
// Check if the service is started
|
||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,33 +67,32 @@ public class BootReceiverTest {
|
||||
@Test
|
||||
public void testOnReceive_noStartService() {
|
||||
// Make sure no TrackRecordingService
|
||||
Intent stopIntent = new Intent(InstrumentationRegistry.getInstrumentation().getContext(), TrackRecordingService.class);
|
||||
InstrumentationRegistry.getInstrumentation().getContext().stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||
Intent stopIntent = new Intent(context, TrackRecordingService.class);
|
||||
context.stopService(stopIntent);
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
|
||||
BootReceiver bootReceiver = new BootReceiver();
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_BUG_REPORT);
|
||||
bootReceiver.onReceive(InstrumentationRegistry.getInstrumentation().getContext(), intent);
|
||||
bootReceiver.onReceive(context, intent);
|
||||
|
||||
// Check if the service is not started
|
||||
Assert.assertFalse(isServiceExisted(InstrumentationRegistry.getInstrumentation().getContext(), SERVICE_NAME));
|
||||
Assert.assertFalse(isServiceExisted(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a service is started in a context.
|
||||
*
|
||||
* @param context the context for checking a service
|
||||
* @param serviceName the service name to find if existed
|
||||
* @param context the context for checking a service
|
||||
*/
|
||||
private boolean isServiceExisted(Context context, String serviceName) {
|
||||
private boolean isServiceExisted(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
|
||||
|
||||
for (int i = 0; i < serviceList.size(); i++) {
|
||||
RunningServiceInfo serviceInfo = serviceList.get(i);
|
||||
ComponentName componentName = serviceInfo.service;
|
||||
if (componentName.getClassName().equals(serviceName)) {
|
||||
if (componentName.getClassName().equals(SERVICE_NAME)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
*/
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
@@ -34,9 +33,9 @@ public class ChartValueSeriesTest {
|
||||
private ChartValueSeries series;
|
||||
|
||||
@Before
|
||||
protected void setUp() {
|
||||
public void setUp() {
|
||||
series = new ChartValueSeries(
|
||||
InstrumentationRegistry.getInstrumentation().getContext(),
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
Integer.MIN_VALUE,
|
||||
Integer.MAX_VALUE,
|
||||
new int[]{100, 1000},
|
||||
@@ -46,6 +45,7 @@ public class ChartValueSeriesTest {
|
||||
R.color.chart_elevation_border);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialConditions() {
|
||||
Assert.assertEquals(1, series.getInterval());
|
||||
Assert.assertEquals(0, series.getMinMarkerValue());
|
||||
@@ -53,11 +53,13 @@ public class ChartValueSeriesTest {
|
||||
Assert.assertTrue(series.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnabled() {
|
||||
series.setEnabled(false);
|
||||
Assert.assertFalse(series.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallUpdates() {
|
||||
series.update(0);
|
||||
series.update(10);
|
||||
@@ -67,6 +69,7 @@ public class ChartValueSeriesTest {
|
||||
Assert.assertEquals(500, series.getMaxMarkerValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigUpdates() {
|
||||
series.update(0);
|
||||
series.update(901);
|
||||
@@ -76,6 +79,7 @@ public class ChartValueSeriesTest {
|
||||
Assert.assertEquals(5000, series.getMaxMarkerValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotZeroBasedUpdates() {
|
||||
series.update(220);
|
||||
series.update(250);
|
||||
|
||||
@@ -32,14 +32,14 @@ public class TrackStubUtils {
|
||||
|
||||
public static final double INITIAL_ALTITUDE = 22;
|
||||
public static final long INITIAL_TIME = 1000L;
|
||||
static final String LOCATION_PROVIDER = "gps";
|
||||
static final double INITIAL_LATITUDE = 22;
|
||||
static final double INITIAL_LONGITUDE = 22;
|
||||
static final float INITIAL_ACCURACY = 5;
|
||||
static final float INITIAL_SPEED = 10;
|
||||
static final float INITIAL_BEARING = 3.0f;
|
||||
private static final String LOCATION_PROVIDER = "gps";
|
||||
private static final double INITIAL_LATITUDE = 22;
|
||||
private static final double INITIAL_LONGITUDE = 22;
|
||||
private static final float INITIAL_ACCURACY = 5;
|
||||
private static final float INITIAL_SPEED = 10;
|
||||
private static final float INITIAL_BEARING = 3.0f;
|
||||
// Used to change the value of latitude, longitude, and altitude.
|
||||
static final double DIFFERENCE = 0.01;
|
||||
private static final double DIFFERENCE = 0.01;
|
||||
|
||||
/**
|
||||
* Gets a a {@link Track} stub with specified number of locations.
|
||||
@@ -71,8 +71,7 @@ public class TrackStubUtils {
|
||||
*
|
||||
* @return a SensorDataSetLocation stub.
|
||||
*/
|
||||
public static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude,
|
||||
double altitude) {
|
||||
public static SensorDataSetLocation createSensorDataSetLocation(double latitude, double longitude, double altitude) {
|
||||
// Initial Location
|
||||
Location loc = new Location(LOCATION_PROVIDER);
|
||||
loc.setLatitude(latitude);
|
||||
@@ -82,7 +81,7 @@ public class TrackStubUtils {
|
||||
loc.setSpeed(INITIAL_SPEED);
|
||||
loc.setTime(INITIAL_TIME);
|
||||
loc.setBearing(INITIAL_BEARING);
|
||||
SensorDataSet sd = new SensorDataSet(100, 100);
|
||||
SensorDataSet sd = new SensorDataSet(Float.NaN, Float.NaN);
|
||||
|
||||
return new SensorDataSetLocation(loc, sd);
|
||||
}
|
||||
|
||||
+19
-17
@@ -16,41 +16,43 @@
|
||||
|
||||
package de.dennisguse.opentracks.content;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider.DatabaseHelper;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider.DatabaseHelper;
|
||||
|
||||
/**
|
||||
* Tests {@link CustomContentProvider}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CustomContentProviderTest {
|
||||
|
||||
private static final String DATABASE_NAME = "test.db";
|
||||
|
||||
private SQLiteDatabase db;
|
||||
private CustomContentProvider customContentProvider;
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Before
|
||||
protected void setUp() {
|
||||
InstrumentationRegistry.getInstrumentation().getContext().deleteDatabase(DATABASE_NAME);
|
||||
db = (new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext(), DATABASE_NAME)).getWritableDatabase();
|
||||
customContentProvider = new CustomContentProvider();
|
||||
public void setUp() {
|
||||
context.deleteDatabase(DATABASE_NAME);
|
||||
db = (new DatabaseHelper(context, DATABASE_NAME)).getWritableDatabase();
|
||||
customContentProvider = new CustomContentProvider() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDatabaseHelper_OnCreate() {
|
||||
Assert.assertTrue(hasTable(TracksColumns.TABLE_NAME));
|
||||
Assert.assertTrue(hasTable(TrackPointsColumns.TABLE_NAME));
|
||||
@@ -60,19 +62,19 @@ public class CustomContentProviderTest {
|
||||
/**
|
||||
* Tests {@link CustomContentProvider#onCreate(android.content.Context)}.
|
||||
*/
|
||||
@Test
|
||||
public void testOnCreate() {
|
||||
Assert.assertTrue(customContentProvider.onCreate(InstrumentationRegistry.getInstrumentation().getContext()));
|
||||
Assert.assertTrue(customContentProvider.onCreate(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link CustomContentProvider#getType(Uri)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetType() {
|
||||
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
||||
Assert.assertEquals(
|
||||
TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
||||
Assert.assertEquals(
|
||||
WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
||||
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +119,7 @@ public class CustomContentProviderTest {
|
||||
try {
|
||||
db.execSQL("SElECT count(*) from " + table + " order by " + column);
|
||||
} catch (Exception e) {
|
||||
if (e.getMessage().indexOf("no such column") > -1) {
|
||||
if (e.getMessage().contains("no such column")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -137,7 +139,7 @@ public class CustomContentProviderTest {
|
||||
createTable(TrackPointsColumns.TABLE_NAME);
|
||||
createTable(WaypointsColumns.TABLE_NAME);
|
||||
|
||||
DatabaseHelper databaseHelper = new DatabaseHelper(InstrumentationRegistry.getInstrumentation().getContext());
|
||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
||||
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
|
||||
}
|
||||
}
|
||||
+120
-125
@@ -20,27 +20,27 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceTest.MockContext;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* A unit test for {@link ContentProviderUtilsImpl}.
|
||||
@@ -48,37 +48,36 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @author Bartlomiej Niechwiej
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class CustomContentProviderUtilsImplTest {
|
||||
private static final String NAME_PREFIX = "test name";
|
||||
private static final String MOCK_DESC = "Mock Next Waypoint Desc!";
|
||||
private static final String TEST_DESC = "Test Desc!";
|
||||
private static final String TEST_DESC_NEW = "Test Desc new!";
|
||||
private Context context;
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
private ContentProviderUtils providerUtils;
|
||||
private double INITIAL_LATITUDE = 37.0;
|
||||
private double INITIAL_LONGITUDE = -57.0;
|
||||
private double ALTITUDE_INTERVAL = 2.5;
|
||||
|
||||
@Before
|
||||
protected void setUp() {
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
context = new MockContext(mockContentResolver, targetContext);
|
||||
CustomContentProvider provider = new CustomContentProvider();
|
||||
provider.attachInfo(context, null);
|
||||
mockContentResolver.addProvider(ContentProviderUtils.AUTHORITY, provider);
|
||||
setContext(context);
|
||||
@Mock
|
||||
private Cursor cursorMock;
|
||||
|
||||
@Mock
|
||||
private ContentResolver contentResolverMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
providerUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noPoints() {
|
||||
testIterator(1, 0, 1, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_customFactory() {
|
||||
final Location location = new Location("test_location");
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
@@ -93,46 +92,50 @@ public class CustomContentProviderUtilsImplTest {
|
||||
Assert.assertEquals(15, counter.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_nullFactory() {
|
||||
try {
|
||||
testIterator(1, 15, 4, false, null);
|
||||
fail("Expecting IllegalArgumentException");
|
||||
Assert.fail("Expecting IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noBatchAscending() {
|
||||
testIterator(1, 50, 100, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_noBatchDescending() {
|
||||
testIterator(1, 50, 100, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_batchAscending() {
|
||||
testIterator(1, 50, 11, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_batchDescending() {
|
||||
testIterator(1, 50, 11, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationIterator_largeTrack() {
|
||||
testIterator(1, 20000, 2000, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
private List<Location> testIterator(long trackId, int numPoints, int batchSize,
|
||||
boolean descending, LocationFactory locationFactory) {
|
||||
private List<Location> testIterator(long trackId, int numPoints, int batchSize, boolean descending, LocationFactory locationFactory) {
|
||||
long lastPointId = initializeTrack(trackId, numPoints);
|
||||
((ContentProviderUtilsImpl) providerUtils).setDefaultCursorBatchSize(batchSize);
|
||||
List<Location> locations = new ArrayList<Location>(numPoints);
|
||||
LocationIterator it = providerUtils.getTrackPointLocationIterator(
|
||||
trackId, -1L, descending, locationFactory);
|
||||
LocationIterator it = providerUtils.getTrackPointLocationIterator(trackId, -1L, descending, locationFactory);
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
Location loc = it.next();
|
||||
@@ -172,8 +175,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
// Load all inserted locations.
|
||||
long lastPointId = -1;
|
||||
int counter = 0;
|
||||
LocationIterator it = providerUtils.getTrackPointLocationIterator(id, -1L, false,
|
||||
ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
LocationIterator it = providerUtils.getTrackPointLocationIterator(id, -1L, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
@@ -194,37 +196,35 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createTrack(Cursor)}.
|
||||
*/
|
||||
@UsesMocks(Cursor.class)
|
||||
@Test
|
||||
public void testCreateTrack() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
int startColumnIndex = 1;
|
||||
int columnIndex = startColumnIndex;
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TracksColumns._ID))
|
||||
.andReturn(columnIndex++);
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).andReturn(
|
||||
columnIndex++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex++);
|
||||
columnIndex = startColumnIndex;
|
||||
// Id
|
||||
AndroidMock.expect(cursorMock.isNull(columnIndex++)).andReturn(false);
|
||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||
// Name
|
||||
AndroidMock.expect(cursorMock.isNull(columnIndex++)).andReturn(false);
|
||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||
long trackId = System.currentTimeMillis();
|
||||
columnIndex = startColumnIndex;
|
||||
// Id
|
||||
AndroidMock.expect(cursorMock.getLong(columnIndex++)).andReturn(trackId);
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||
// Name
|
||||
String name = NAME_PREFIX + trackId;
|
||||
AndroidMock.expect(cursorMock.getString(columnIndex++)).andReturn(name);
|
||||
AndroidMock.replay(cursorMock);
|
||||
when(cursorMock.getString(columnIndex++)).thenReturn(name);
|
||||
|
||||
Track track = providerUtils.createTrack(cursorMock);
|
||||
Assert.assertEquals(trackId, track.getId());
|
||||
Assert.assertEquals(name, track.getName());
|
||||
AndroidMock.verify(cursorMock);
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#deleteAllTracks(Context)}
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteAllTracks() {
|
||||
// Insert track, points and waypoint at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -233,32 +233,27 @@ public class CustomContentProviderUtilsImplTest {
|
||||
Waypoint waypoint = new Waypoint();
|
||||
providerUtils.insertWaypoint(waypoint);
|
||||
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());
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
|
||||
null, TrackPointsColumns._ID);
|
||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
||||
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
|
||||
null, WaypointsColumns._ID);
|
||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||
Assert.assertEquals(1, waypointCursor.getCount());
|
||||
// Delete all.
|
||||
providerUtils.deleteAllTracks(context);
|
||||
// Check whether all have been deleted.
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null,
|
||||
TracksColumns._ID);
|
||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||
Assert.assertEquals(0, tracksCursor.getCount());
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null,
|
||||
null, TrackPointsColumns._ID);
|
||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
||||
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null,
|
||||
null, WaypointsColumns._ID);
|
||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||
Assert.assertEquals(0, waypointCursor.getCount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#deleteTrack(Context, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteTrack() {
|
||||
// Insert three tracks, points of two tracks and way point of one track.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -299,6 +294,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getAllTracks()}
|
||||
*/
|
||||
@Test
|
||||
public void testGetAllTracks() {
|
||||
int initialTrackNumber = providerUtils.getAllTracks().size();
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -311,6 +307,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastTrack()}
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
||||
@@ -320,6 +317,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrack(long)}
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
providerUtils.insertTrack(getTrack(trackId, 0));
|
||||
@@ -329,6 +327,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#updateTrack(Track)}
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateTrack() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 0);
|
||||
@@ -345,6 +344,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createContentValues(Waypoint)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateContentValues_waypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -380,8 +380,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
waypoint.setLocation(loc);
|
||||
providerUtils.insertWaypoint(waypoint);
|
||||
|
||||
ContentProviderUtilsImpl contentProviderUtils = new ContentProviderUtilsImpl(
|
||||
new MockContentResolver());
|
||||
ContentProviderUtilsImpl contentProviderUtils = new ContentProviderUtilsImpl(contentResolverMock);
|
||||
|
||||
long waypointId = System.currentTimeMillis();
|
||||
waypoint.setId(waypointId);
|
||||
@@ -396,40 +395,36 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createWaypoint(Cursor)}.
|
||||
*/
|
||||
@UsesMocks(Cursor.class)
|
||||
@Test
|
||||
public void testCreateWaypoint() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
int startColumnIndex = 1;
|
||||
int columnIndex = startColumnIndex;
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID))
|
||||
.andReturn(columnIndex++);
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).andReturn(
|
||||
columnIndex++);
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).andReturn(
|
||||
columnIndex++);
|
||||
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns._ID)).thenReturn(columnIndex++);
|
||||
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.NAME)).thenReturn(columnIndex++);
|
||||
when(cursorMock.getColumnIndexOrThrow(WaypointsColumns.TRACKID)).thenReturn(columnIndex++);
|
||||
columnIndex = startColumnIndex;
|
||||
// Id
|
||||
AndroidMock.expect(cursorMock.isNull(columnIndex++)).andReturn(false);
|
||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||
// Name
|
||||
AndroidMock.expect(cursorMock.isNull(columnIndex++)).andReturn(false);
|
||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||
// trackIdIndex
|
||||
AndroidMock.expect(cursorMock.isNull(columnIndex++)).andReturn(false);
|
||||
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
|
||||
long id = System.currentTimeMillis();
|
||||
columnIndex = startColumnIndex;
|
||||
// Id
|
||||
AndroidMock.expect(cursorMock.getLong(columnIndex++)).andReturn(id);
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(id);
|
||||
// Name
|
||||
String name = NAME_PREFIX + id;
|
||||
AndroidMock.expect(cursorMock.getString(columnIndex++)).andReturn(name);
|
||||
when(cursorMock.getString(columnIndex++)).thenReturn(name);
|
||||
// trackIdIndex
|
||||
long trackId = 11L;
|
||||
AndroidMock.expect(cursorMock.getLong(columnIndex++)).andReturn(trackId);
|
||||
AndroidMock.replay(cursorMock);
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||
|
||||
Waypoint waypoint = providerUtils.createWaypoint(cursorMock);
|
||||
Assert.assertEquals(id, waypoint.getId());
|
||||
Assert.assertEquals(name, waypoint.getName());
|
||||
Assert.assertEquals(trackId, waypoint.getTrackId());
|
||||
AndroidMock.verify(cursorMock);
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,6 +432,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
|
||||
* when there is only one waypoint in the track.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteWaypoint_onlyOneWayPoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -458,8 +454,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track aTrack, Vector<Double> distances,
|
||||
Vector<Double> elevations, boolean html) {
|
||||
public String generateTrackDescription(Track aTrack, boolean html) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -473,6 +468,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* {@link ContentProviderUtilsImpl#deleteWaypoint(Context, long, DescriptionGenerator)}
|
||||
* when there is more than one waypoint in the track.
|
||||
*/
|
||||
@Test
|
||||
public void testDeleteWaypoint_hasNextWayPoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -513,8 +509,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track aTrack, Vector<Double> distances,
|
||||
Vector<Double> elevations, boolean html) {
|
||||
public String generateTrackDescription(Track aTrack, boolean html) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -528,6 +523,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getFirstWaypointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFirstWaypointId() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -547,6 +543,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getNextWaypointNumber(long, WaypointType)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetNextWaypointNumber() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -577,6 +574,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtils#getLastWaypoint(long, WaypointType)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastStatisticsWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -605,6 +603,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* Tests the method {@link ContentProviderUtilsImpl#insertWaypoint(Waypoint)} and
|
||||
* {@link ContentProviderUtilsImpl#getWaypoint(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testInsertAndGetWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -621,6 +620,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#updateWaypoint(Waypoint)}.
|
||||
*/
|
||||
@Test
|
||||
public void testUpdateWaypoint() {
|
||||
long trackId = System.currentTimeMillis();
|
||||
Track track = getTrack(trackId, 10);
|
||||
@@ -639,9 +639,9 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#bulkInsertTrackPoint(Location[],
|
||||
* int, long)}.
|
||||
* Tests the method {@link ContentProviderUtilsImpl#bulkInsertTrackPoint(Location[], int, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testBulkInsertTrackPoint() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -657,74 +657,68 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#createTrackPoint(Cursor)}.
|
||||
*/
|
||||
@UsesMocks(Cursor.class)
|
||||
@Test
|
||||
public void testCreateTrackPoint() {
|
||||
Cursor cursorMock = AndroidMock.createNiceMock(Cursor.class);
|
||||
|
||||
// Set index.
|
||||
int index = 1;
|
||||
// Id
|
||||
AndroidMock.expect(cursorMock.getColumnIndex(TrackPointsColumns._ID)).andReturn(index++);
|
||||
when(cursorMock.getColumnIndex(TrackPointsColumns._ID)).thenReturn(index++);
|
||||
// Longitude
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).andReturn(
|
||||
index++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE)).thenReturn(index++);
|
||||
// Latitude
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).andReturn(
|
||||
index++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE)).thenReturn(index++);
|
||||
// Time
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME))
|
||||
.andReturn(index++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.TIME))
|
||||
.thenReturn(index++);
|
||||
// Speed
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).andReturn(
|
||||
index++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SPEED)).thenReturn(index++);
|
||||
// Sensor
|
||||
AndroidMock.expect(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).andReturn(
|
||||
index++);
|
||||
when(cursorMock.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE)).thenReturn(index++);
|
||||
|
||||
// Set return value of isNull().
|
||||
index = 2;
|
||||
// Longitude
|
||||
AndroidMock.expect(cursorMock.isNull(index++)).andReturn(false);
|
||||
when(cursorMock.isNull(index++)).thenReturn(false);
|
||||
// Latitude
|
||||
AndroidMock.expect(cursorMock.isNull(index++)).andReturn(false);
|
||||
when(cursorMock.isNull(index++)).thenReturn(false);
|
||||
// Time
|
||||
AndroidMock.expect(cursorMock.isNull(index++)).andReturn(false);
|
||||
when(cursorMock.isNull(index++)).thenReturn(false);
|
||||
// Speed
|
||||
AndroidMock.expect(cursorMock.isNull(index++)).andReturn(false);
|
||||
when(cursorMock.isNull(index++)).thenReturn(false);
|
||||
// Sensor
|
||||
AndroidMock.expect(cursorMock.isNull(index++)).andReturn(false);
|
||||
when(cursorMock.isNull(index++)).thenReturn(false);
|
||||
|
||||
// Set return value of isNull().
|
||||
index = 2;
|
||||
// Longitude
|
||||
int longitude = 11;
|
||||
AndroidMock.expect(cursorMock.getInt(index++)).andReturn(longitude * 1000000);
|
||||
when(cursorMock.getInt(index++)).thenReturn(longitude * 1000000);
|
||||
// Latitude.
|
||||
int latitude = 22;
|
||||
AndroidMock.expect(cursorMock.getInt(index++)).andReturn(latitude * 1000000);
|
||||
when(cursorMock.getInt(index++)).thenReturn(latitude * 1000000);
|
||||
// Time
|
||||
long time = System.currentTimeMillis();
|
||||
AndroidMock.expect(cursorMock.getLong(index++)).andReturn(time);
|
||||
when(cursorMock.getLong(index++)).thenReturn(time);
|
||||
// Speed
|
||||
float speed = 2.2f;
|
||||
AndroidMock.expect(cursorMock.getFloat(index++)).andReturn(speed);
|
||||
when(cursorMock.getFloat(index++)).thenReturn(speed);
|
||||
// Sensor
|
||||
byte[] sensor = "Sensor state".getBytes();
|
||||
AndroidMock.expect(cursorMock.getBlob(index++)).andReturn(sensor);
|
||||
when(cursorMock.getBlob(index++)).thenReturn(sensor);
|
||||
|
||||
AndroidMock.replay(cursorMock);
|
||||
Location location = providerUtils.createTrackPoint(cursorMock);
|
||||
Assert.assertEquals((double) longitude, location.getLongitude());
|
||||
Assert.assertEquals((double) latitude, location.getLatitude());
|
||||
Assert.assertEquals(time, location.getTime());
|
||||
Assert.assertEquals(speed, location.getSpeed());
|
||||
AndroidMock.verify(cursorMock);
|
||||
Assert.assertEquals((double) longitude, location.getLongitude(), 0.01);
|
||||
Assert.assertEquals((double) latitude, location.getLatitude(), 0.01);
|
||||
Assert.assertEquals(time, location.getTime(), 0.01);
|
||||
Assert.assertEquals(speed, location.getSpeed(), 0.01);
|
||||
verify(cursorMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtilsImpl#insertTrackPoint(Location, long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testInsertTrackPoint() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -738,6 +732,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getFirstTrackPointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetFirstTrackPointId() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -750,6 +745,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastTrackPointId(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastTrackPointId() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -762,6 +758,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
/**
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getLastValidTrackPoint(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetLastValidTrackPoint() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -777,6 +774,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)}
|
||||
* in descending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointCursor_desc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -792,6 +790,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* {@link ContentProviderUtilsImpl#getTrackPointCursor(long, long, int, boolean)}
|
||||
* in ascending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointCursor_asc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -803,10 +802,9 @@ public class CustomContentProviderUtilsImplTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)}
|
||||
* in descending.
|
||||
* Tests the method {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)} in descending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointLocationIterator_desc() {
|
||||
// Insert track, points at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -815,8 +813,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
|
||||
long startTrackPointId = 2L;
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId,
|
||||
startTrackPointId, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, true, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
for (int i = 1; i >= 0; i--) {
|
||||
Assert.assertTrue(locationIterator.hasNext());
|
||||
Location location = locationIterator.next();
|
||||
@@ -831,6 +828,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* {@link ContentProviderUtilsImpl#getTrackPointLocationIterator(long, long, boolean, LocationFactory)}
|
||||
* in ascending.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTrackPointLocationIterator_asc() {
|
||||
// Insert track, point at first.
|
||||
long trackId = System.currentTimeMillis();
|
||||
@@ -839,8 +837,7 @@ public class CustomContentProviderUtilsImplTest {
|
||||
|
||||
long startTrackPointId = 2L;
|
||||
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId,
|
||||
startTrackPointId, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
LocationIterator locationIterator = providerUtils.getTrackPointLocationIterator(trackId, startTrackPointId, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
|
||||
for (int i = 1; i < 10; i++) {
|
||||
Assert.assertTrue(locationIterator.hasNext());
|
||||
@@ -876,7 +873,6 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* @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);
|
||||
@@ -893,10 +889,10 @@ public class CustomContentProviderUtilsImplTest {
|
||||
* @param location the location to be checked
|
||||
*/
|
||||
private void checkLocation(int i, Location location) {
|
||||
Assert.assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude());
|
||||
Assert.assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude());
|
||||
Assert.assertEquals((float) i / 100.0f, location.getAccuracy());
|
||||
Assert.assertEquals(i * ALTITUDE_INTERVAL, location.getAltitude());
|
||||
Assert.assertEquals(INITIAL_LATITUDE + (double) i / 10000.0, location.getLatitude(), 0.01);
|
||||
Assert.assertEquals(INITIAL_LONGITUDE - (double) i / 10000.0, location.getLongitude(), 0.01);
|
||||
Assert.assertEquals((float) i / 100.0f, location.getAccuracy(), 0.01);
|
||||
Assert.assertEquals(i * ALTITUDE_INTERVAL, location.getAltitude(), 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -906,7 +902,6 @@ public class CustomContentProviderUtilsImplTest {
|
||||
*/
|
||||
private void insertTrackWithLocations(Track track) {
|
||||
providerUtils.insertTrack(track);
|
||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track
|
||||
.getLocations().size(), track.getId());
|
||||
providerUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||
}
|
||||
}
|
||||
|
||||
+31
-18
@@ -16,17 +16,20 @@
|
||||
|
||||
package de.dennisguse.opentracks.content;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import android.content.Context;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Tests for {@link DescriptionGeneratorImpl}.
|
||||
*
|
||||
@@ -38,15 +41,17 @@ public class DescriptionGeneratorImplTest {
|
||||
private static final long START_TIME = 1288721514000L;
|
||||
private DescriptionGeneratorImpl descriptionGenerator;
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Before
|
||||
protected void setUp() throws Exception {
|
||||
descriptionGenerator = new DescriptionGeneratorImpl(InstrumentationRegistry.getInstrumentation().getContext());
|
||||
public void setUp() {
|
||||
descriptionGenerator = new DescriptionGeneratorImpl(ApplicationProvider.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track,
|
||||
* java.util.Vector, java.util.Vector, boolean)}.
|
||||
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track, java.util.Vector, java.util.Vector, boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateTrackDescription() {
|
||||
Track track = new Track();
|
||||
TripStatistics stats = new TripStatistics();
|
||||
@@ -62,8 +67,8 @@ public class DescriptionGeneratorImplTest {
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTripStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
String expected = "Created by"
|
||||
+ " <a href='TODO'>TODO</a> on Android<p>"
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/dennisguse/opentracks'>OpenTracks</a><p>"
|
||||
+ "Name: -<br>"
|
||||
+ "Activity type: hiking<br>"
|
||||
+ "Description: -<br>"
|
||||
@@ -81,13 +86,15 @@ public class DescriptionGeneratorImplTest {
|
||||
+ "Elevation gain: 6000 m (19685 ft)<br>"
|
||||
+ "Max grade: 42 %<br>"
|
||||
+ "Min grade: 11 %<br>"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "<br>";
|
||||
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, null, null, true));
|
||||
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
|
||||
|
||||
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(TripStatistics)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGenerateWaypointDescription() {
|
||||
Waypoint waypoint = new Waypoint();
|
||||
TripStatistics stats = new TripStatistics();
|
||||
@@ -116,15 +123,15 @@ public class DescriptionGeneratorImplTest {
|
||||
+ "Elevation gain: 6000 m (19685 ft)\n"
|
||||
+ "Max grade: 42 %\n"
|
||||
+ "Min grade: 11 %\n"
|
||||
+ "Recorded: " + StringUtils.formatDateTime(InstrumentationRegistry.getInstrumentation().getContext(), START_TIME) + "\n";
|
||||
Assert.assertEquals(
|
||||
expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
|
||||
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "\n";
|
||||
Assert.assertEquals(expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder,
|
||||
* int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteDistance() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
|
||||
@@ -135,6 +142,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int,
|
||||
* String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteTime() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
|
||||
@@ -145,6 +153,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder,
|
||||
* int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteSpeed() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
|
||||
@@ -155,6 +164,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder,
|
||||
* int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteElevation() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
|
||||
@@ -165,6 +175,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writePace(double, StringBuilder, int,
|
||||
* String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWritePace() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
|
||||
@@ -175,6 +186,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||
* int, String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeGrade(.042, builder, R.string.description_max_grade, "<br>");
|
||||
@@ -185,6 +197,7 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||
* int, String)} with a NaN.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade_nan() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeGrade(Double.NaN, builder, R.string.description_max_grade, "<br>");
|
||||
@@ -195,10 +208,10 @@ public class DescriptionGeneratorImplTest {
|
||||
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
|
||||
* int, String)} with an infinite number.
|
||||
*/
|
||||
@Test
|
||||
public void testWriteGrade_infinite() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
descriptionGenerator.writeGrade(
|
||||
Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
|
||||
descriptionGenerator.writeGrade(Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
|
||||
Assert.assertEquals("Max grade: 0 %<br>", builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,26 +17,27 @@
|
||||
package de.dennisguse.opentracks.content;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceTest.MockContext;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
* Tests for {@link SearchEngine}.
|
||||
* These are not meant to be quality tests, but instead feature-by-feature tests
|
||||
@@ -53,24 +54,18 @@ public class SearchEngineTest {
|
||||
private ContentProviderUtils providerUtils;
|
||||
private SearchEngine engine;
|
||||
|
||||
@Before
|
||||
protected void setUp() {
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
MockContext context = new MockContext(mockContentResolver, targetContext);
|
||||
CustomContentProvider provider = new CustomContentProvider();
|
||||
provider.attachInfo(context, null);
|
||||
mockContentResolver.addProvider(ContentProviderUtils.AUTHORITY, provider);
|
||||
setContext(context);
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
engine = new SearchEngine(providerUtils);
|
||||
}
|
||||
|
||||
@After
|
||||
protected void tearDown() throws Exception {
|
||||
providerUtils.deleteAllTracks(getContext());
|
||||
public void tearDown() throws Exception {
|
||||
providerUtils.deleteAllTracks(context);
|
||||
}
|
||||
|
||||
private long insertTrack(String title, String description, String category, double distance, long hoursAgo) {
|
||||
@@ -141,6 +136,7 @@ public class SearchEngineTest {
|
||||
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchText() {
|
||||
// Insert 7 tracks (purposefully out of result order):
|
||||
// - one which won't match
|
||||
@@ -162,11 +158,10 @@ public class SearchEngineTest {
|
||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||
|
||||
// Title > Description > Category.
|
||||
assertTrackResults(results,
|
||||
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
||||
categoryMatchId);
|
||||
assertTrackResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWaypointText() {
|
||||
// Insert 7 waypoints (purposefully out of result order):
|
||||
// - one which won't match
|
||||
@@ -188,11 +183,10 @@ public class SearchEngineTest {
|
||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||
|
||||
// Title > Description > Category.
|
||||
assertWaypointResults(results,
|
||||
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
|
||||
categoryMatchId);
|
||||
assertWaypointResults(results, allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId, categoryMatchId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchMixedText() {
|
||||
// Insert 5 entries (purposefully out of result order):
|
||||
// - one waypoint which will match by description
|
||||
@@ -210,12 +204,13 @@ public class SearchEngineTest {
|
||||
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
|
||||
|
||||
// Title > Description > Category.
|
||||
assertEquals(results.toString(), 3, results.size());
|
||||
Assert.assertEquals(results.toString(), 3, results.size());
|
||||
assertTrackResult(trackId, results.get(0));
|
||||
assertWaypointResult(titleWaypointId, results.get(1));
|
||||
assertWaypointResult(descriptionWaypointId, results.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTrackDistance() {
|
||||
// All results match text, but they're at difference distances from the user.
|
||||
long farFarAwayId = insertTrack("aa", 0.3);
|
||||
@@ -229,6 +224,7 @@ public class SearchEngineTest {
|
||||
assertTrackResults(results, nearId, farId, farFarAwayId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWaypointDistance() {
|
||||
// All results match text, but they're at difference distances from the user.
|
||||
long farFarAwayId = insertWaypoint("aa", 0.3);
|
||||
@@ -255,6 +251,7 @@ public class SearchEngineTest {
|
||||
assertTrackResults(results, recentId, oldId, oldestId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWaypointRecent() {
|
||||
// All results match text, but they're were recorded at different times.
|
||||
long oldestId = insertWaypoint("aa", 2);
|
||||
@@ -268,6 +265,7 @@ public class SearchEngineTest {
|
||||
assertWaypointResults(results, recentId, oldId, oldestId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchCurrentTrack() {
|
||||
// All results match text, but one of them is the current track.
|
||||
long currentId = insertTrack("ab", 1);
|
||||
@@ -280,6 +278,7 @@ public class SearchEngineTest {
|
||||
assertTrackResults(results, otherId, currentId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchCurrentTrackWaypoint() {
|
||||
// All results match text, but one of them is in the current track.
|
||||
long otherId = insertWaypoint("aa", 1, 456);
|
||||
@@ -293,36 +292,36 @@ public class SearchEngineTest {
|
||||
}
|
||||
|
||||
private void assertTrackResult(long trackId, ScoredResult result) {
|
||||
assertNotNull("Not a track", result.track);
|
||||
assertNull("Ambiguous result", result.waypoint);
|
||||
assertEquals(trackId, result.track.getId());
|
||||
Assert.assertNotNull("Not a track", result.track);
|
||||
Assert.assertNull("Ambiguous result", result.waypoint);
|
||||
Assert.assertEquals(trackId, result.track.getId());
|
||||
}
|
||||
|
||||
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
|
||||
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
|
||||
assertEquals(results.size(), trackIds.length);
|
||||
Assert.assertEquals(results.size(), trackIds.length);
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
ScoredResult result = results.get(i);
|
||||
assertNotNull(errMsg, result.track);
|
||||
assertNull(errMsg, result.waypoint);
|
||||
assertEquals(errMsg, trackIds[i], result.track.getId());
|
||||
Assert.assertNotNull(errMsg, result.track);
|
||||
Assert.assertNull(errMsg, result.waypoint);
|
||||
Assert.assertEquals(errMsg, trackIds[i], result.track.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertWaypointResult(long waypointId, ScoredResult result) {
|
||||
assertNotNull("Not a waypoint", result.waypoint);
|
||||
assertNull("Ambiguous result", result.track);
|
||||
assertEquals(waypointId, result.waypoint.getId());
|
||||
Assert.assertNotNull("Not a waypoint", result.waypoint);
|
||||
Assert.assertNull("Ambiguous result", result.track);
|
||||
Assert.assertEquals(waypointId, result.waypoint.getId());
|
||||
}
|
||||
|
||||
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
|
||||
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
|
||||
assertEquals(results.size(), waypointIds.length);
|
||||
Assert.assertEquals(results.size(), waypointIds.length);
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
ScoredResult result = results.get(i);
|
||||
assertNotNull(errMsg, result.waypoint);
|
||||
assertNull(errMsg, result.track);
|
||||
assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
|
||||
Assert.assertNotNull(errMsg, result.waypoint);
|
||||
Assert.assertNull(errMsg, result.track);
|
||||
Assert.assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,134 +19,117 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.location.Location;
|
||||
import android.provider.BaseColumns;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.TrackStubUtils;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceTest.MockContext;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.IAnswer;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.capture;
|
||||
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import static com.google.android.testing.mocking.AndroidMock.isA;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationFactory;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.isA;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Tests for {@link TrackDataHub}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TrackDataHubTest {
|
||||
|
||||
private static final long TRACK_ID = 42L;
|
||||
private static final int TARGET_POINTS = 50;
|
||||
|
||||
private MockContext context;
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
@Mock
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@Mock
|
||||
private DataSource dataSource;
|
||||
private TrackDataManager trackDataManager;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
@Mock
|
||||
private TrackDataListener trackDataListener1;
|
||||
|
||||
@Mock
|
||||
private TrackDataListener trackDataListener2;
|
||||
private Capture<OnSharedPreferenceChangeListener> preferenceChangeListenerCapture = new Capture<
|
||||
SharedPreferences.OnSharedPreferenceChangeListener>();
|
||||
private ArgumentCaptor<OnSharedPreferenceChangeListener> preferenceChangeListenerCapture = ArgumentCaptor.forClass(OnSharedPreferenceChangeListener.class);
|
||||
|
||||
@Before
|
||||
@UsesMocks({ContentProviderUtils.class, DataSource.class, TrackDataListener.class})
|
||||
@Override
|
||||
protected void setUp() {
|
||||
context = new MockContext(new MockContentResolver(), new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test."));
|
||||
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
contentProviderUtils = AndroidMock.createMock(ContentProviderUtils.class);
|
||||
dataSource = AndroidMock.createMock(DataSource.class, context);
|
||||
trackDataManager = new TrackDataManager();
|
||||
trackDataHub = new TrackDataHub(
|
||||
context, trackDataManager, contentProviderUtils, TARGET_POINTS) {
|
||||
public void setUp() {
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
trackDataHub = new TrackDataHub(context, new TrackDataManager(), contentProviderUtils, TARGET_POINTS) {
|
||||
@Override
|
||||
protected DataSource newDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void runInHanderThread(Runnable runnable) {
|
||||
protected void runInHandlerThread(Runnable runnable) {
|
||||
// Run everything in the same thread
|
||||
runnable.run();
|
||||
}
|
||||
};
|
||||
|
||||
trackDataListener1 = AndroidMock.createStrictMock(
|
||||
"trackDataListener1", TrackDataListener.class);
|
||||
trackDataListener2 = AndroidMock.createStrictMock(
|
||||
"trackDataListener2", TrackDataListener.class);
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, TRACK_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
AndroidMock.reset(dataSource);
|
||||
@After
|
||||
public void tearDown() {
|
||||
reset(dataSource);
|
||||
|
||||
// Expect everything to be unregistered.
|
||||
dataSource.unregisterContentObserver(isA(ContentObserver.class));
|
||||
AndroidMock.expectLastCall().times(3);
|
||||
dataSource.unregisterOnSharedPreferenceChangeListener(
|
||||
isA(OnSharedPreferenceChangeListener.class));
|
||||
AndroidMock.replay(dataSource);
|
||||
|
||||
//TODO
|
||||
// AndroidMock.expectLastCall().times(3);
|
||||
dataSource.unregisterOnSharedPreferenceChangeListener(isA(OnSharedPreferenceChangeListener.class));
|
||||
verify(dataSource);
|
||||
trackDataHub.stop();
|
||||
trackDataHub = null;
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests registering for tracks table update.
|
||||
*/
|
||||
@Test
|
||||
public void testTracksTableUpdate() {
|
||||
|
||||
// Register two listeners
|
||||
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
|
||||
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
Track track = new Track();
|
||||
expect(contentProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
|
||||
dataSource.registerContentObserver(
|
||||
eq(TracksColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
|
||||
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), contentObserverCapture.capture());
|
||||
trackDataListener1.onTrackUpdated(track);
|
||||
trackDataListener2.onTrackUpdated(track);
|
||||
replay();
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener2, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Causes tracks table update
|
||||
ContentObserver contentObserver = contentObserverCapture.getValue();
|
||||
expect(contentProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
|
||||
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
|
||||
trackDataListener1.onTrackUpdated(track);
|
||||
trackDataListener2.onTrackUpdated(track);
|
||||
replay();
|
||||
@@ -155,7 +138,7 @@ public class TrackDataHubTest {
|
||||
verifyAndReset();
|
||||
|
||||
// Unregister one listener
|
||||
expect(contentProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
|
||||
when(contentProviderUtils.getTrack(TRACK_ID)).thenReturn(track);
|
||||
trackDataListener2.onTrackUpdated(track);
|
||||
replay();
|
||||
|
||||
@@ -172,111 +155,117 @@ public class TrackDataHubTest {
|
||||
verifyAndReset();
|
||||
}
|
||||
|
||||
//TODO
|
||||
/**
|
||||
* Tests registering for waypoints table update.
|
||||
*/
|
||||
public void testWaypointsTableUpdate() {
|
||||
Waypoint waypoint1 = new Waypoint();
|
||||
Waypoint waypoint2 = new Waypoint();
|
||||
Waypoint waypoint3 = new Waypoint();
|
||||
Waypoint waypoint4 = new Waypoint();
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10.0);
|
||||
location.setLongitude(8.0);
|
||||
waypoint1.setLocation(location);
|
||||
waypoint2.setLocation(location);
|
||||
waypoint3.setLocation(location);
|
||||
waypoint4.setLocation(location);
|
||||
|
||||
// Register two listeners
|
||||
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
|
||||
expect(contentProviderUtils.getWaypointCursor(
|
||||
eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
.andStubAnswer(new FixedSizeCursorAnswer(2));
|
||||
expect(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint1).andReturn(waypoint2);
|
||||
dataSource.registerContentObserver(
|
||||
eq(WaypointsColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
trackDataListener1.clearWaypoints();
|
||||
trackDataListener2.clearWaypoints();
|
||||
trackDataListener1.onNewWaypoint(waypoint1);
|
||||
trackDataListener2.onNewWaypoint(waypoint1);
|
||||
trackDataListener1.onNewWaypoint(waypoint2);
|
||||
trackDataListener2.onNewWaypoint(waypoint2);
|
||||
trackDataListener1.onNewWaypointsDone();
|
||||
trackDataListener2.onNewWaypointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener2, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Cause waypoints table update
|
||||
ContentObserver contentObserver = contentObserverCapture.getValue();
|
||||
expect(contentProviderUtils.getWaypointCursor(
|
||||
eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
.andStubAnswer(new FixedSizeCursorAnswer(3));
|
||||
expect(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint3);
|
||||
trackDataListener1.clearWaypoints();
|
||||
trackDataListener2.clearWaypoints();
|
||||
trackDataListener1.onNewWaypoint(waypoint1);
|
||||
trackDataListener2.onNewWaypoint(waypoint1);
|
||||
trackDataListener1.onNewWaypoint(waypoint2);
|
||||
trackDataListener2.onNewWaypoint(waypoint2);
|
||||
trackDataListener1.onNewWaypoint(waypoint3);
|
||||
trackDataListener2.onNewWaypoint(waypoint3);
|
||||
trackDataListener1.onNewWaypointsDone();
|
||||
trackDataListener2.onNewWaypointsDone();
|
||||
replay();
|
||||
|
||||
contentObserver.onChange(false);
|
||||
verifyAndReset();
|
||||
|
||||
// Unregister one listener
|
||||
expect(contentProviderUtils.getWaypointCursor(
|
||||
eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
.andStubAnswer(new FixedSizeCursorAnswer(4));
|
||||
expect(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint3).andReturn(waypoint4);
|
||||
trackDataListener2.clearWaypoints();
|
||||
trackDataListener2.onNewWaypoint(waypoint1);
|
||||
trackDataListener2.onNewWaypoint(waypoint2);
|
||||
trackDataListener2.onNewWaypoint(waypoint3);
|
||||
trackDataListener2.onNewWaypoint(waypoint4);
|
||||
trackDataListener2.onNewWaypointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.unregisterTrackDataListener(trackDataListener1);
|
||||
contentObserver.onChange(false);
|
||||
verifyAndReset();
|
||||
|
||||
// Unregister the second listener
|
||||
dataSource.unregisterContentObserver(contentObserver);
|
||||
replay();
|
||||
|
||||
trackDataHub.unregisterTrackDataListener(trackDataListener2);
|
||||
contentObserver.onChange(false);
|
||||
verifyAndReset();
|
||||
}
|
||||
// @Test
|
||||
// public void testWaypointsTableUpdate() {
|
||||
// Waypoint waypoint1 = new Waypoint();
|
||||
// Waypoint waypoint2 = new Waypoint();
|
||||
// Waypoint waypoint3 = new Waypoint();
|
||||
// Waypoint waypoint4 = new Waypoint();
|
||||
// Location location = new Location("gps");
|
||||
// location.setLatitude(10.0);
|
||||
// location.setLongitude(8.0);
|
||||
// waypoint1.setLocation(location);
|
||||
// waypoint2.setLocation(location);
|
||||
// waypoint3.setLocation(location);
|
||||
// waypoint4.setLocation(location);
|
||||
//
|
||||
// // Register two listeners
|
||||
// ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
// when(contentProviderUtils.getWaypointCursor(
|
||||
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
// .andStubAnswer(new FixedSizeCursorAnswer(2));
|
||||
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
// .thenReturn(waypoint1)
|
||||
// .thenReturn(waypoint2)
|
||||
// .thenReturn(waypoint1)
|
||||
// .thenReturn(waypoint2);
|
||||
// dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_BASE_URI), contentObserverCapture.capture());
|
||||
// trackDataListener1.clearWaypoints();
|
||||
// trackDataListener2.clearWaypoints();
|
||||
// trackDataListener1.onNewWaypoint(waypoint1);
|
||||
// trackDataListener2.onNewWaypoint(waypoint1);
|
||||
// trackDataListener1.onNewWaypoint(waypoint2);
|
||||
// trackDataListener2.onNewWaypoint(waypoint2);
|
||||
// trackDataListener1.onNewWaypointsDone();
|
||||
// trackDataListener2.onNewWaypointsDone();
|
||||
// replay();
|
||||
//
|
||||
// trackDataHub.start();
|
||||
// trackDataHub.loadTrack(TRACK_ID);
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
// verifyAndReset();
|
||||
//
|
||||
// // Cause waypoints table update
|
||||
// ContentObserver contentObserver = contentObserverCapture.getValue();
|
||||
// when(contentProviderUtils.getWaypointCursor(
|
||||
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
// .andStubAnswer(new FixedSizeCursorAnswer(3));
|
||||
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
// .thenReturn(waypoint1)
|
||||
// .thenReturn(waypoint2)
|
||||
// .thenReturn(waypoint3);
|
||||
// trackDataListener1.clearWaypoints();
|
||||
// trackDataListener2.clearWaypoints();
|
||||
// trackDataListener1.onNewWaypoint(waypoint1);
|
||||
// trackDataListener2.onNewWaypoint(waypoint1);
|
||||
// trackDataListener1.onNewWaypoint(waypoint2);
|
||||
// trackDataListener2.onNewWaypoint(waypoint2);
|
||||
// trackDataListener1.onNewWaypoint(waypoint3);
|
||||
// trackDataListener2.onNewWaypoint(waypoint3);
|
||||
// trackDataListener1.onNewWaypointsDone();
|
||||
// trackDataListener2.onNewWaypointsDone();
|
||||
// replay();
|
||||
//
|
||||
// contentObserver.onChange(false);
|
||||
// verifyAndReset();
|
||||
//
|
||||
// // Unregister one listener
|
||||
// when(contentProviderUtils.getWaypointCursor(
|
||||
// eq(TRACK_ID), AndroidMock.leq(-1L), eq(TrackDataHub.MAX_DISPLAYED_WAYPOINTS)))
|
||||
// .andStubAnswer(new FixedSizeCursorAnswer(4));
|
||||
// when(contentProviderUtils.createWaypoint(isA(Cursor.class)))
|
||||
// .thenReturn(waypoint1)
|
||||
// .thenReturn(waypoint2)
|
||||
// .thenReturn(waypoint3)
|
||||
// .thenReturn(waypoint4);
|
||||
// trackDataListener2.clearWaypoints();
|
||||
// trackDataListener2.onNewWaypoint(waypoint1);
|
||||
// trackDataListener2.onNewWaypoint(waypoint2);
|
||||
// trackDataListener2.onNewWaypoint(waypoint3);
|
||||
// trackDataListener2.onNewWaypoint(waypoint4);
|
||||
// trackDataListener2.onNewWaypointsDone();
|
||||
// replay();
|
||||
//
|
||||
// trackDataHub.unregisterTrackDataListener(trackDataListener1);
|
||||
// contentObserver.onChange(false);
|
||||
// verifyAndReset();
|
||||
//
|
||||
// // Unregister the second listener
|
||||
// dataSource.unregisterContentObserver(contentObserver);
|
||||
// replay();
|
||||
//
|
||||
// trackDataHub.unregisterTrackDataListener(trackDataListener2);
|
||||
// contentObserver.onChange(false);
|
||||
// verifyAndReset();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Tests track points table update.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackPointsTableUpdate() {
|
||||
// Register one listener
|
||||
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
|
||||
|
||||
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
@@ -284,30 +273,26 @@ public class TrackDataHubTest {
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Register a second listener
|
||||
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
|
||||
trackDataListener2.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener2);
|
||||
trackDataListener2.onNewTrackPointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Deliver more points - should go to both listeners without clearing
|
||||
ContentObserver contentObserver = contentObserverCapture.getValue();
|
||||
locationIterator = new FixedSizeLocationIterator(11, 10, 1);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(11L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(20L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(11L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(20L);
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
locationIterator.expectLocationsDelivered(trackDataListener2);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
@@ -319,10 +304,9 @@ public class TrackDataHubTest {
|
||||
|
||||
// Unregister one listener and change track
|
||||
locationIterator = new FixedSizeLocationIterator(101, 10);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
|
||||
.andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).andReturn(110L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
|
||||
.thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).thenReturn(110L);
|
||||
trackDataListener2.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener2);
|
||||
trackDataListener2.onNewTrackPointsDone();
|
||||
@@ -336,17 +320,16 @@ public class TrackDataHubTest {
|
||||
/**
|
||||
* Tests track points table update with registering the same listener.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackPointsTableUpdate_reRegister() {
|
||||
|
||||
// Register one listener
|
||||
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
|
||||
|
||||
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
|
||||
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
@@ -355,8 +338,7 @@ public class TrackDataHubTest {
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Unregister the listener
|
||||
@@ -368,36 +350,32 @@ public class TrackDataHubTest {
|
||||
verifyAndReset();
|
||||
|
||||
// Register again
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), contentObserverCapture.capture());
|
||||
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests tracks point able change. Register a listener after a track change.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackPointsTableUpdate_reRegisterAfterTrackChange() {
|
||||
|
||||
// Register one listener
|
||||
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
|
||||
ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
|
||||
|
||||
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(10L);
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
@@ -405,8 +383,7 @@ public class TrackDataHubTest {
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Unregister the listener
|
||||
@@ -418,73 +395,65 @@ public class TrackDataHubTest {
|
||||
verifyAndReset();
|
||||
|
||||
// Register the listener after a new track
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
|
||||
locationIterator = new FixedSizeLocationIterator(1, 10);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
|
||||
.andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).andReturn(10L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
|
||||
.thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID + 1)).thenReturn(10L);
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.loadTrack(TRACK_ID + 1);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
}
|
||||
|
||||
//TODO
|
||||
/**
|
||||
* Tests track points table update with large track sampling.
|
||||
*/
|
||||
public void testTrackPointsTableUpdate_largeTrackSampling() {
|
||||
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
|
||||
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(200L);
|
||||
AndroidMock.expectLastCall().anyTimes();
|
||||
FixedSizeLocationIterator locationIterator1 = new FixedSizeLocationIterator(
|
||||
1, 200, 4, 25, 71, 120);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator1);
|
||||
FixedSizeLocationIterator locationIterator2 = new FixedSizeLocationIterator(
|
||||
1, 200, 4, 25, 71, 120);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator2);
|
||||
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator1.expectSampledLocationsDelivered(trackDataListener1, 4, false);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
trackDataListener2.clearTrackPoints();
|
||||
locationIterator2.expectSampledLocationsDelivered(trackDataListener2, 4, true);
|
||||
trackDataListener2.onNewTrackPointsDone();
|
||||
replay();
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(
|
||||
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
}
|
||||
// @Test
|
||||
// public void testTrackPointsTableUpdate_largeTrackSampling() {
|
||||
// ArgumentCaptor<ContentObserver> contentObserverCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
// dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_BASE_URI), contentObserverCapture.capture());
|
||||
//
|
||||
// when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(200L);
|
||||
// AndroidMock.expectLastCall().anyTimes();
|
||||
// FixedSizeLocationIterator locationIterator1 = new FixedSizeLocationIterator(1, 200, 4, 25, 71, 120);
|
||||
// when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator1);
|
||||
// FixedSizeLocationIterator locationIterator2 = new FixedSizeLocationIterator(1, 200, 4, 25, 71, 120);
|
||||
// when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator2);
|
||||
//
|
||||
// trackDataListener1.clearTrackPoints();
|
||||
// locationIterator1.expectSampledLocationsDelivered(trackDataListener1, 4, false);
|
||||
// trackDataListener1.onNewTrackPointsDone();
|
||||
// trackDataListener2.clearTrackPoints();
|
||||
// locationIterator2.expectSampledLocationsDelivered(trackDataListener2, 4, true);
|
||||
// trackDataListener2.onNewTrackPointsDone();
|
||||
// replay();
|
||||
//
|
||||
// trackDataHub.start();
|
||||
// trackDataHub.loadTrack(TRACK_ID);
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
|
||||
// verifyAndReset();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Tests track points table update with resampling.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackPointsTableUpdate_resampling() {
|
||||
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(
|
||||
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
|
||||
ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
dataSource.registerContentObserver(eq(TrackPointsColumns.CONTENT_URI), observerCapture.capture());
|
||||
|
||||
// Deliver 30 points (no sampling happens)
|
||||
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 30, 5);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(30L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(30L);
|
||||
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectLocationsDelivered(trackDataListener1);
|
||||
@@ -493,16 +462,14 @@ public class TrackDataHubTest {
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.loadTrack(TRACK_ID);
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
|
||||
verifyAndReset();
|
||||
|
||||
// Now deliver 30 more (incrementally sampled)
|
||||
ContentObserver observer = observerCapture.getValue();
|
||||
locationIterator = new FixedSizeLocationIterator(31, 30);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(31L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(60L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(eq(TRACK_ID), eq(31L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(60L);
|
||||
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
replay();
|
||||
@@ -512,9 +479,9 @@ public class TrackDataHubTest {
|
||||
|
||||
// Now another 30 (triggers resampling)
|
||||
locationIterator = new FixedSizeLocationIterator(1, 90);
|
||||
expect(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID)).andReturn(90L);
|
||||
when(contentProviderUtils.getTrackPointLocationIterator(
|
||||
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).thenReturn(locationIterator);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID)).thenReturn(90L);
|
||||
trackDataListener1.clearTrackPoints();
|
||||
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
|
||||
trackDataListener1.onNewTrackPointsDone();
|
||||
@@ -527,66 +494,49 @@ public class TrackDataHubTest {
|
||||
/**
|
||||
* Tests preferences change.
|
||||
*/
|
||||
@Test
|
||||
public void testPreferencesChange() throws Exception {
|
||||
|
||||
// Register two listeners
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key,
|
||||
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
PreferencesUtils.setString(context, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
|
||||
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
|
||||
expect(trackDataListener1.onMetricUnitsChanged(true)).andReturn(false);
|
||||
expect(trackDataListener1.onReportSpeedChanged(true)).andReturn(false);
|
||||
expect(
|
||||
trackDataListener1.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT))
|
||||
.andReturn(false);
|
||||
expect(trackDataListener1.onRecordingDistanceIntervalChanged(
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).andReturn(false);
|
||||
expect(trackDataListener1.onMapTypeChanged(PreferencesUtils.MAP_TYPE_DEFAULT)).andReturn(false);
|
||||
dataSource.registerOnSharedPreferenceChangeListener(preferenceChangeListenerCapture.capture());
|
||||
when(trackDataListener1.onMetricUnitsChanged(true)).thenReturn(false);
|
||||
when(trackDataListener1.onReportSpeedChanged(true)).thenReturn(false);
|
||||
when(trackDataListener1.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT)).thenReturn(false);
|
||||
when(trackDataListener1.onRecordingDistanceIntervalChanged(PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).thenReturn(false);
|
||||
|
||||
expect(trackDataListener2.onMetricUnitsChanged(true)).andReturn(false);
|
||||
expect(trackDataListener2.onReportSpeedChanged(true)).andReturn(false);
|
||||
expect(
|
||||
trackDataListener2.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT))
|
||||
.andReturn(false);
|
||||
expect(trackDataListener2.onRecordingDistanceIntervalChanged(
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).andReturn(false);
|
||||
expect(trackDataListener2.onMapTypeChanged(PreferencesUtils.MAP_TYPE_DEFAULT)).andReturn(false);
|
||||
when(trackDataListener2.onMetricUnitsChanged(true)).thenReturn(false);
|
||||
when(trackDataListener2.onReportSpeedChanged(true)).thenReturn(false);
|
||||
when(trackDataListener2.onRecordingGpsAccuracy(PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT)).thenReturn(false);
|
||||
when(trackDataListener2.onRecordingDistanceIntervalChanged(PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT)).thenReturn(false);
|
||||
replay();
|
||||
|
||||
trackDataHub.start();
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.PREFERENCE));
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener2, EnumSet.of(TrackDataType.PREFERENCE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.PREFERENCE));
|
||||
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(TrackDataType.PREFERENCE));
|
||||
verifyAndReset();
|
||||
|
||||
// Change report speed to false
|
||||
expect(trackDataListener1.onReportSpeedChanged(false)).andReturn(false);
|
||||
expect(trackDataListener2.onReportSpeedChanged(false)).andReturn(false);
|
||||
when(trackDataListener1.onReportSpeedChanged(false)).thenReturn(false);
|
||||
when(trackDataListener2.onReportSpeedChanged(false)).thenReturn(false);
|
||||
replay();
|
||||
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_rate_key, context.getString(R.string.stats_rate_pace));
|
||||
PreferencesUtils.setString(context, R.string.stats_rate_key, context.getString(R.string.stats_rate_pace));
|
||||
OnSharedPreferenceChangeListener listener = preferenceChangeListenerCapture.getValue();
|
||||
listener.onSharedPreferenceChanged(
|
||||
sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_rate_key));
|
||||
listener.onSharedPreferenceChanged(sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_rate_key));
|
||||
verifyAndReset();
|
||||
|
||||
// Change metric units to false
|
||||
expect(trackDataListener1.onMetricUnitsChanged(false)).andReturn(false);
|
||||
expect(trackDataListener2.onMetricUnitsChanged(false)).andReturn(false);
|
||||
when(trackDataListener1.onMetricUnitsChanged(false)).thenReturn(false);
|
||||
when(trackDataListener2.onMetricUnitsChanged(false)).thenReturn(false);
|
||||
replay();
|
||||
|
||||
String imperialUnits = context.getString(R.string.stats_units_imperial);
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
|
||||
listener.onSharedPreferenceChanged(
|
||||
sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
listener.onSharedPreferenceChanged(sharedPreferences, PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
verifyAndReset();
|
||||
}
|
||||
|
||||
@@ -594,125 +544,128 @@ public class TrackDataHubTest {
|
||||
* Replays mocks.
|
||||
*/
|
||||
private void replay() {
|
||||
AndroidMock.replay(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
|
||||
// AndroidMock.replay(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies and resets mocks.
|
||||
*/
|
||||
private void verifyAndReset() {
|
||||
AndroidMock.verify(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
|
||||
AndroidMock.reset(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
|
||||
verify(contentProviderUtils);
|
||||
verify(dataSource);
|
||||
verify(trackDataListener1);
|
||||
verify(trackDataListener2);
|
||||
reset(contentProviderUtils, dataSource, trackDataListener1, trackDataListener2);
|
||||
}
|
||||
|
||||
//TODO
|
||||
/**
|
||||
* Tests the method {@link TrackDataHub#start()}.
|
||||
*/
|
||||
public void testRegisterTracksTableListener() {
|
||||
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), capture(observerCapture));
|
||||
Track track = TrackStubUtils.createTrack(1);
|
||||
expect(contentProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
|
||||
// Make the track id is unique.
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
|
||||
trackDataListener1.onTrackUpdated(track);
|
||||
replay();
|
||||
trackDataHub.start();
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
verifyAndReset();
|
||||
}
|
||||
// @Test
|
||||
// public void testRegisterTracksTableListener() {
|
||||
// ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
// dataSource.registerContentObserver(eq(TracksColumns.CONTENT_BASE_URI), observerCapture.capture());
|
||||
// Track track = TrackStubUtils.createTrack(1);
|
||||
//
|
||||
// when(contentProviderUtils.getTrack(capture(new Capture<Long>())).thenReturn(track);
|
||||
// // Make the track id is unique.
|
||||
// PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
|
||||
// trackDataListener1.onTrackUpdated(track);
|
||||
// replay();
|
||||
// trackDataHub.start();
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
|
||||
// verifyAndReset();
|
||||
// }
|
||||
|
||||
//TODO
|
||||
/**
|
||||
* Tests the method {@link TrackDataHub#start()}.
|
||||
*/
|
||||
public void testRegisterWaypointsTableListener() {
|
||||
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
|
||||
dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_URI), capture(observerCapture));
|
||||
expect(contentProviderUtils.getWaypointCursor(
|
||||
capture(new Capture<Long>()), capture(new Capture<Long>()),
|
||||
capture(new Capture<Integer>()))).andReturn(null);
|
||||
trackDataListener1.clearWaypoints();
|
||||
trackDataListener1.onNewWaypointsDone();
|
||||
replay();
|
||||
trackDataHub.start();
|
||||
trackDataHub.registerTrackDataListener(
|
||||
trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
verifyAndReset();
|
||||
}
|
||||
// @Test
|
||||
// public void testRegisterWaypointsTableListener() {
|
||||
// ArgumentCaptor<ContentObserver> observerCapture = ArgumentCaptor.forClass(ContentObserver.class);
|
||||
// dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_BASE_URI), observerCapture.capture());
|
||||
// when(contentProviderUtils.getWaypointCursor(
|
||||
// capture(new Capture<Long>()), capture(new Capture<Long>()),
|
||||
// capture(new Capture<Integer>()))).thenReturn(null);
|
||||
// trackDataListener1.clearWaypoints();
|
||||
// trackDataListener1.onNewWaypointsDone();
|
||||
// replay();
|
||||
// trackDataHub.start();
|
||||
// trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
|
||||
// verifyAndReset();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
|
||||
* the key is R.string.min_required_accuracy_key.
|
||||
*/
|
||||
@Test
|
||||
public void testNotifyPreferenceChanged_minRequiredAccuracy() {
|
||||
int value = 1;
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value);
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils
|
||||
.getKey(context, R.string.recording_gps_accuracy_key));
|
||||
assertEquals(value, trackDataHub.getRecordingGpsAccuracy());
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key));
|
||||
Assert.assertEquals(value, trackDataHub.getRecordingGpsAccuracy());
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, value + 1);
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils
|
||||
.getKey(context, R.string.recording_gps_accuracy_key));
|
||||
assertEquals(value + 1, trackDataHub.getRecordingGpsAccuracy());
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key));
|
||||
Assert.assertEquals(value + 1, trackDataHub.getRecordingGpsAccuracy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
|
||||
* the key is R.string.metric_units_key.
|
||||
*/
|
||||
@Test
|
||||
public void testNotifyPreferenceChanged_metricUnitsNoNotify() {
|
||||
String imperialUnits = context.getString(R.string.stats_units_imperial);
|
||||
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, imperialUnits);
|
||||
trackDataHub.notifyPreferenceChanged(
|
||||
PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
assertEquals(false, trackDataHub.isMetricUnits());
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
trackDataHub.notifyPreferenceChanged(
|
||||
PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
assertEquals(true, trackDataHub.isMetricUnits());
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
Assert.assertFalse(trackDataHub.isMetricUnits());
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_units_key));
|
||||
Assert.assertTrue(trackDataHub.isMetricUnits());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
|
||||
* the key is R.string.metric_units_key.
|
||||
*/
|
||||
@Test
|
||||
public void testNotifyPreferenceChanged_reportSpeedNoNotify() {
|
||||
String value = context.getString(R.string.stats_rate_pace);
|
||||
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils
|
||||
.getKey(context, R.string.stats_rate_key));
|
||||
assertEquals(false, trackDataHub.isReportSpeed());
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_rate_key));
|
||||
Assert.assertFalse(trackDataHub.isReportSpeed());
|
||||
|
||||
value = context.getString(R.string.stats_rate_speed);
|
||||
PreferencesUtils.setString(context, R.string.stats_rate_key, value);
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils
|
||||
.getKey(context, R.string.stats_rate_key));
|
||||
assertEquals(true, trackDataHub.isReportSpeed());
|
||||
trackDataHub.notifyPreferenceChanged(PreferencesUtils.getKey(context, R.string.stats_rate_key));
|
||||
Assert.assertTrue(trackDataHub.isReportSpeed());
|
||||
}
|
||||
|
||||
//TODO
|
||||
/**
|
||||
* Fixed size cursor answer.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
private static class FixedSizeCursorAnswer implements IAnswer<Cursor> {
|
||||
private static class FixedSizeCursorAnswer {
|
||||
private final int size;
|
||||
|
||||
public FixedSizeCursorAnswer(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor answer() throws Throwable {
|
||||
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID});
|
||||
for (long i = 0; i < size; i++) {
|
||||
cursor.addRow(new Object[]{i});
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public Cursor answer() throws Throwable {
|
||||
// MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID});
|
||||
// for (long i = 0; i < size; i++) {
|
||||
// cursor.addRow(new Object[]{i});
|
||||
// }
|
||||
// return cursor;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-4
@@ -19,11 +19,12 @@ import android.os.Parcel;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
|
||||
/**
|
||||
* Tests for the WaypointCreationRequest class.
|
||||
* {@link WaypointCreationRequest}
|
||||
@@ -33,6 +34,7 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class WaypointCreationRequestTest {
|
||||
|
||||
@Test
|
||||
public void testTypeParceling() {
|
||||
WaypointCreationRequest original = WaypointCreationRequest.DEFAULT_WAYPOINT;
|
||||
Parcel p = Parcel.obtain();
|
||||
@@ -46,9 +48,9 @@ public class WaypointCreationRequestTest {
|
||||
Assert.assertNull(copy.getIconUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllAttributesParceling() {
|
||||
WaypointCreationRequest original = new WaypointCreationRequest(
|
||||
WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null);
|
||||
WaypointCreationRequest original = new WaypointCreationRequest(WaypointType.WAYPOINT, false, "name", "category", "description", "img.png", null);
|
||||
Parcel p = Parcel.obtain();
|
||||
original.writeToParcel(p, 0);
|
||||
p.setDataPosition(0);
|
||||
|
||||
@@ -18,20 +18,20 @@ package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.ChartView;
|
||||
import de.dennisguse.opentracks.TrackStubUtils;
|
||||
import de.dennisguse.opentracks.content.SensorDataSetLocation;
|
||||
import de.dennisguse.opentracks.content.sensor.Sensor;
|
||||
import de.dennisguse.opentracks.content.sensor.Sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests {@link ChartFragment}.
|
||||
*
|
||||
@@ -45,94 +45,76 @@ public class ChartFragmentTest {
|
||||
private ChartFragment chartFragment;
|
||||
|
||||
@Before
|
||||
protected void setUp() {
|
||||
public void setUp() {
|
||||
chartFragment = new ChartFragment();
|
||||
chartFragment.setChartView(new ChartView(InstrumentationRegistry.getInstrumentation().getContext()));
|
||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext()));
|
||||
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}
|
||||
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#fillDataPoint(Location, double[])}
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_sensorIncorrect() {
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
|
||||
// Input incorrect state.
|
||||
// Creates SensorData.
|
||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||
.setValue(100).setState(Sensor.SensorState.NONE);
|
||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||
.setValue(101).setState(Sensor.SensorState.NONE);
|
||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||
.setValue(102).setState(Sensor.SensorState.NONE);
|
||||
|
||||
// Creates SensorDataSet.
|
||||
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
||||
sensorDataSet = sensorDataSet.toBuilder()
|
||||
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
||||
SensorDataSet sensorDataSet = new SensorDataSet(SensorDataSet.DATA_UNAVAILABLE, SensorDataSet.DATA_UNAVAILABLE);
|
||||
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
||||
// Test.
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the correct values of sensor in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}.
|
||||
* Tests the logic to get the correct values of sensor in {@link ChartFragment#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_sensorCorrect() {
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
// No input.
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.CADENCE_SERIES + 1]);
|
||||
Assert.assertEquals(Double.NaN, point[ChartView.POWER_SERIES + 1]);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(Float.NaN, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
|
||||
// Creates SensorData.
|
||||
Sensor.SensorData.Builder heartRateData = Sensor.SensorData.newBuilder()
|
||||
.setValue(100).setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder cadenceData = Sensor.SensorData.newBuilder()
|
||||
.setValue(101).setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder powerData = Sensor.SensorData.newBuilder()
|
||||
.setValue(102).setState(Sensor.SensorState.SENDING);
|
||||
SensorDataSet sensorDataSet = new SensorDataSet(100, 101, 102);
|
||||
|
||||
// Creates SensorDataSet.
|
||||
SensorDataSet sensorDataSet = sensorDataSetLocation.getSensorDataSet();
|
||||
sensorDataSet = sensorDataSet.toBuilder()
|
||||
.setHeartRate(heartRateData).setCadence(cadenceData).setPower(powerData).build();
|
||||
sensorDataSetLocation.setSensorDataSet(sensorDataSet);
|
||||
// Test.
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1]);
|
||||
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1]);
|
||||
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1]);
|
||||
Assert.assertEquals(100.0, point[ChartView.HEART_RATE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(101.0, point[ChartView.CADENCE_SERIES + 1], 0.01);
|
||||
Assert.assertEquals(102.0, point[ChartView.POWER_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of metric Distance in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}.
|
||||
* Tests the logic to get the value of metric Distance in {@link ChartFragment#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_distanceMetric() {
|
||||
// By distance.
|
||||
chartFragment.setChartByDistance(true);
|
||||
// Resets last location and writes first location.
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0]);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The second is a same location, just different time.
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals(0.0, point[0]);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The third location is a new location, and use metric.
|
||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
@@ -144,7 +126,7 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
||||
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
||||
double distance1 = results[0] * UnitConversions.M_TO_KM;
|
||||
Assert.assertEquals(distance1, point[0]);
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
|
||||
// The fourth location is a new location, and use metric.
|
||||
SensorDataSetLocation sensorDataSetLocation4 = TrackStubUtils.createSensorDataSetLocation();
|
||||
@@ -155,13 +137,13 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(),
|
||||
sensorDataSetLocation4.getLatitude(), sensorDataSetLocation4.getLongitude(), results);
|
||||
double distance2 = results[0] * UnitConversions.M_TO_KM;
|
||||
Assert.assertEquals((distance1 + distance2), point[0]);
|
||||
Assert.assertEquals((distance1 + distance2), point[0], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}.
|
||||
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_distanceImperial() {
|
||||
// By distance.
|
||||
chartFragment.setChartByDistance(true);
|
||||
@@ -171,7 +153,7 @@ public class ChartFragmentTest {
|
||||
// The first is a same location, just different time.
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0]);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
|
||||
// The second location is a new location, and use imperial.
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
@@ -186,7 +168,7 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(sensorDataSetLocation1.getLatitude(), sensorDataSetLocation1.getLongitude(),
|
||||
sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(), results);
|
||||
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||
Assert.assertEquals(distance1, point[0]);
|
||||
Assert.assertEquals(distance1, point[0], 0.01);
|
||||
|
||||
// The third location is a new location, and use imperial.
|
||||
SensorDataSetLocation sensorDataSetLocation3 = TrackStubUtils.createSensorDataSetLocation();
|
||||
@@ -200,30 +182,30 @@ public class ChartFragmentTest {
|
||||
Location.distanceBetween(sensorDataSetLocation2.getLatitude(), sensorDataSetLocation2.getLongitude(),
|
||||
sensorDataSetLocation3.getLatitude(), sensorDataSetLocation3.getLongitude(), results);
|
||||
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
||||
Assert.assertEquals(distance1 + distance2, point[0]);
|
||||
Assert.assertEquals(distance1 + distance2, point[0], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the values of time in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}.
|
||||
* Tests the logic to get the values of time in {@link ChartFragment#fillDataPoint(Location, double[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_time() {
|
||||
// By time
|
||||
chartFragment.setChartByDistance(false);
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[0]);
|
||||
Assert.assertEquals(0.0, point[0], 0.01);
|
||||
long timeSpan = 222;
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + timeSpan);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals((double) timeSpan, point[0]);
|
||||
Assert.assertEquals((double) timeSpan, point[0], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of elevation in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])} by one and two points.
|
||||
* Tests the logic to get the value of elevation in {@link ChartFragment#fillDataPoint(Location, double[])} by one and two points.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_elevation() {
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
@@ -232,7 +214,7 @@ public class ChartFragmentTest {
|
||||
* parameter. Then only one value INITIALLONGTITUDE in buffer.
|
||||
*/
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1]);
|
||||
Assert.assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point[ChartView.ELEVATION_SERIES + 1], 0.01);
|
||||
|
||||
/*
|
||||
* Send another value to buffer, now there are two values, INITIALALTITUDE
|
||||
@@ -242,15 +224,15 @@ public class ChartFragmentTest {
|
||||
sensorDataSetLocation2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0,
|
||||
point[ChartView.ELEVATION_SERIES + 1]);
|
||||
point[ChartView.ELEVATION_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get the value of speed in {@link ChartFragment#fillDataPoint(Location,
|
||||
* double[])}. In this test, firstly remove all
|
||||
* points in memory, and then fill in two points one by one. The speed values
|
||||
* of these points are 129, 130.
|
||||
* Tests the logic to get the value of speed in {@link ChartFragment#fillDataPoint(Location, double[])}.
|
||||
* In this test, firstly remove all points in memory, and then fill in two points one by one.
|
||||
* The speed values of these points are 129, 130.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_speed() {
|
||||
/*
|
||||
* At first, clear old points of speed, so give true to the second
|
||||
@@ -259,7 +241,7 @@ public class ChartFragmentTest {
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(128.5f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
|
||||
/*
|
||||
* Tests the logic when both metricUnits and reportSpeed are true.This
|
||||
@@ -274,12 +256,13 @@ public class ChartFragmentTest {
|
||||
sensorDataSetLocation2.setTime(sensorDataSetLocation1.getTime() + 222);
|
||||
sensorDataSetLocation2.setSpeed(130);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1]);
|
||||
Assert.assertEquals(130.0 * UnitConversions.MS_TO_KMH, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to compute speed when use Imperial.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_speedImperial() {
|
||||
// Setups to use imperial.
|
||||
chartFragment.setMetricUnits(false);
|
||||
@@ -288,7 +271,7 @@ public class ChartFragmentTest {
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(100.0f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
@@ -300,12 +283,13 @@ public class ChartFragmentTest {
|
||||
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
||||
sensorDataSetLocation2.setSpeed(102);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1]);
|
||||
Assert.assertEquals(102.0 * UnitConversions.MS_TO_KMH * UnitConversions.KM_TO_MI, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get pace value when reportSpeed is false.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_pace_nonZeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartFragment.setReportSpeed(false);
|
||||
@@ -314,7 +298,7 @@ public class ChartFragmentTest {
|
||||
SensorDataSetLocation sensorDataSetLocation1 = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation1.setSpeed(100.0f);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation1);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1]);
|
||||
Assert.assertEquals(0.0, point[ChartView.SPEED_SERIES + 1], 0.01);
|
||||
|
||||
SensorDataSetLocation sensorDataSetLocation2 = TrackStubUtils.createSensorDataSetLocation();
|
||||
|
||||
@@ -326,20 +310,21 @@ public class ChartFragmentTest {
|
||||
sensorDataSetLocation2.setTime(sensorDataSetLocation2.getTime() + 222);
|
||||
sensorDataSetLocation2.setSpeed(102);
|
||||
point = fillDataPointTestHelper(sensorDataSetLocation2);
|
||||
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1]);
|
||||
Assert.assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MS_TO_KMH), point[ChartView.PACE_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the logic to get pace value when reportSpeed is false and average
|
||||
* speed is zero.
|
||||
*/
|
||||
@Test
|
||||
public void testFillDataPoint_pace_zeroSpeed() {
|
||||
// Setups reportSpeed to false.
|
||||
chartFragment.setReportSpeed(false);
|
||||
SensorDataSetLocation sensorDataSetLocation = TrackStubUtils.createSensorDataSetLocation();
|
||||
sensorDataSetLocation.setSpeed(0);
|
||||
double[] point = fillDataPointTestHelper(sensorDataSetLocation);
|
||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1]);
|
||||
Assert.assertEquals(0.0, point[ChartView.PACE_SERIES + 1], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,5 +338,4 @@ public class ChartFragmentTest {
|
||||
chartFragment.fillDataPoint(location, point);
|
||||
return point;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+40
-60
@@ -17,36 +17,37 @@
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.Factory;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
||||
import de.dennisguse.opentracks.testing.TestingProviderUtilsFactory;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Assert;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.SimpleTimeZone;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.WaypointsColumns;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Abstract class for testing file track importers.
|
||||
*
|
||||
* @author Jimmy Shih.
|
||||
*/
|
||||
public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
||||
public abstract class AbstractTestFileTrackImporter {
|
||||
|
||||
protected static final String TRACK_NAME_0 = "blablub";
|
||||
protected static final String TRACK_DESCRIPTION_0 = "s'Laebe isch koi Schlotzer";
|
||||
@@ -63,22 +64,19 @@ public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
||||
protected static final String TRACK_TIME_2 = "2010-04-22T18:23:00.123";
|
||||
protected static final String TRACK_TIME_3 = "2010-04-22T18:24:50.123";
|
||||
|
||||
protected static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
|
||||
protected static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat(
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
|
||||
protected static final SimpleDateFormat DATE_FORMAT_0 = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'", Locale.US);
|
||||
protected static final SimpleDateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
|
||||
protected static final long TRACK_ID_0 = 1;
|
||||
protected static final long TRACK_ID_1 = 2;
|
||||
protected static final long TRACK_POINT_ID_0 = 1;
|
||||
protected static final long TRACK_POINT_ID_1 = 2;
|
||||
protected static final long TRACK_POINT_ID_3 = 4;
|
||||
protected static final long WAYPOINT_ID_0 = 1;
|
||||
protected static final Uri TRACK_ID_0_URI = ContentUris.appendId(
|
||||
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build();
|
||||
protected static final Uri TRACK_ID_1_URI = ContentUris.appendId(
|
||||
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_1).build();
|
||||
protected static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(
|
||||
WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
|
||||
protected static final Uri TRACK_ID_0_URI = ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_0).build();
|
||||
protected static final Uri TRACK_ID_1_URI = ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID_1).build();
|
||||
protected static final Uri WAYPOINT_ID_O_URI = ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), WAYPOINT_ID_0).build();
|
||||
|
||||
protected final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
static {
|
||||
/*
|
||||
@@ -90,24 +88,8 @@ public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
||||
DATE_FORMAT_1.setTimeZone(utc);
|
||||
}
|
||||
|
||||
protected ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private Factory oldMyTracksProviderUtilsFactory;
|
||||
|
||||
@UsesMocks(ContentProviderUtils.class)
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
contentProviderUtils = AndroidMock.createMock(ContentProviderUtils.class);
|
||||
oldMyTracksProviderUtilsFactory = TestingProviderUtilsFactory.installWithInstance(
|
||||
contentProviderUtils);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
TestingProviderUtilsFactory.restoreOldFactory(oldMyTracksProviderUtilsFactory);
|
||||
super.tearDown();
|
||||
}
|
||||
@Mock
|
||||
public ContentProviderUtils contentProviderUtils;
|
||||
|
||||
protected Location createLocation(int index, long time) {
|
||||
Location location = new Location(LocationManager.GPS_PROVIDER);
|
||||
@@ -126,37 +108,35 @@ public class AbstractTestFileTrackImporter extends AndroidTestCase {
|
||||
* @param trackPointId the track point id
|
||||
*/
|
||||
protected void expectFirstTrackPoint(Location location, long trackId, long trackPointId) {
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
location != null ? LocationsMatcher.eqLoc(location) : (Location[]) AndroidMock.anyObject(),
|
||||
eq(1), eq(trackId))).andReturn(1);
|
||||
expect(contentProviderUtils.getFirstTrackPointId(trackId)).andReturn(trackPointId);
|
||||
expect(contentProviderUtils.getLastTrackPointId(trackId)).andReturn(trackPointId);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint(location != null ? (Location[]) any() : (Location[]) any(), eq(1), eq(trackId))).thenReturn(1);
|
||||
when(contentProviderUtils.getFirstTrackPointId(trackId)).thenReturn(trackPointId);
|
||||
when(contentProviderUtils.getLastTrackPointId(trackId)).thenReturn(trackPointId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the track to be updated.
|
||||
*
|
||||
* @param track the track
|
||||
* @param lastTrack true if it is the last track in the gpx
|
||||
* @param trackId the track id
|
||||
* @param trackCaptor the track
|
||||
* @param lastTrack true if it is the last track in the gpx
|
||||
* @param trackId the track id
|
||||
*/
|
||||
protected void expectUpdateTrack(Capture<Track> track, boolean lastTrack, long trackId) {
|
||||
contentProviderUtils.updateTrack(AndroidMock.capture(track));
|
||||
expect(contentProviderUtils.insertWaypoint((Waypoint) AndroidMock.anyObject()))
|
||||
.andReturn(WAYPOINT_ID_O_URI);
|
||||
protected void expectUpdateTrack(ArgumentCaptor<Track> trackCaptor, boolean lastTrack, long trackId) {
|
||||
contentProviderUtils.updateTrack(trackCaptor.capture());
|
||||
|
||||
when(contentProviderUtils.insertWaypoint((Waypoint) any())).thenReturn(WAYPOINT_ID_O_URI);
|
||||
if (lastTrack) {
|
||||
// Return null to not add waypoints
|
||||
expect(contentProviderUtils.getTrack(trackId)).andReturn(null);
|
||||
when(contentProviderUtils.getTrack(trackId)).thenReturn(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void verifyTrack(Track track, String name, String description, long time) {
|
||||
assertEquals(name, track.getName());
|
||||
assertEquals(description, track.getDescription());
|
||||
Assert.assertEquals(name, track.getName());
|
||||
Assert.assertEquals(description, track.getDescription());
|
||||
if (time != -1L) {
|
||||
assertEquals(time, track.getTripStatistics().getStartTime());
|
||||
Assert.assertEquals(time, track.getTripStatistics().getStartTime());
|
||||
}
|
||||
assertNotSame(-1, track.getStartId());
|
||||
assertNotSame(-1, track.getStopId());
|
||||
Assert.assertNotSame(-1, track.getStartId());
|
||||
Assert.assertNotSame(-1, track.getStopId());
|
||||
}
|
||||
}
|
||||
|
||||
+85
-102
@@ -18,24 +18,33 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link GpxFileTrackImporter}.
|
||||
*
|
||||
* @author Steffen Horlacher
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
|
||||
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX = "<gpx><trk>"
|
||||
@@ -49,18 +58,12 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
+ getNameAndDescription(TRACK_NAME_0, TRACK_DESCRIPTION_0) + "<trkseg>"
|
||||
+ getTrackPoint(0, null) + getTrackPoint(1, null) + "</trkseg><trkseg>"
|
||||
+ getTrackPoint(2, null) + getTrackPoint(3, null) + "</trkseg></trk></gpx>";
|
||||
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(
|
||||
0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
|
||||
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
||||
Double.toString(TRACK_LATITUDE), "1000.0");
|
||||
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
||||
TRACK_TIME_0, "invalid");
|
||||
private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
||||
Double.toString(TRACK_ELEVATION), "invalid");
|
||||
private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
||||
Double.toString(TRACK_LATITUDE), "invalid");
|
||||
private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(
|
||||
Double.toString(TRACK_LONGITUDE), "invalid");
|
||||
private static final String INVALID_XML_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.substring(0, VALID_ONE_TRACK_ONE_SEGMENT_GPX.length() - 50);
|
||||
private static final String INVALID_LOCATION_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "1000.0");
|
||||
private static final String INVALID_TIME_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(TRACK_TIME_0, "invalid");
|
||||
private static final String INVALID_ALTITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_ELEVATION), "invalid");
|
||||
private static final String INVALID_LATITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LATITUDE), "invalid");
|
||||
private static final String INVALID_LONGITUDE_GPX = VALID_ONE_TRACK_ONE_SEGMENT_GPX.replaceAll(Double.toString(TRACK_LONGITUDE), "invalid");
|
||||
|
||||
private static String getNameAndDescription(String name, String description) {
|
||||
return "<name><![CDATA[" + name + "]]></name>" + "<desc><![CDATA[" + description + "]]></desc>";
|
||||
@@ -70,9 +73,8 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
String latitude = Double.toString(TRACK_LATITUDE + index);
|
||||
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
||||
String elevation = Double.toString(TRACK_ELEVATION + index);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(
|
||||
"<trkpt lat=\"" + latitude + "\" lon=\"" + longitude + "\"><ele>" + elevation + "</ele>");
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("<trkpt lat=\"" + latitude + "\" lon=\"" + longitude + "\"><ele>" + elevation + "</ele>");
|
||||
if (time != null) {
|
||||
buffer.append("<time>" + time + "</time>");
|
||||
}
|
||||
@@ -83,174 +85,155 @@ public class GpxFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
/**
|
||||
* Tests one track with one segment.
|
||||
*/
|
||||
@Test
|
||||
public void testOneTrackOneSegment() throws Exception {
|
||||
Capture<Track> track = new Capture<Track>();
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||
|
||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||
.andReturn(TRACK_ID_0_URI);
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(1), eq(TRACK_ID_0))).thenReturn(1);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||
getContext(), contentProviderUtils);
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests one track with two segments.
|
||||
*/
|
||||
@Test
|
||||
public void testOneTrackTwoSegments() throws Exception {
|
||||
Capture<Track> track = new Capture<Track>();
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
|
||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||
.andReturn(TRACK_ID_0_URI);
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
// A flush happens at the end
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||
getContext(), contentProviderUtils);
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||
assertEquals(
|
||||
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
||||
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
||||
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests one track with two segments, but no time in the track points.
|
||||
*/
|
||||
public void testOneTrackTwoSegmentsNoTime() throws Exception {
|
||||
Capture<Track> track = new Capture<Track>();
|
||||
@Test
|
||||
public void testOneTrackTwoSegmentsNoTime() {
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||
.andReturn(TRACK_ID_0_URI);
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
expectFirstTrackPoint(null, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(
|
||||
VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||
getContext(), contentProviderUtils);
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_NO_TIME_GPX.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(TRACK_ID_0, trackId);
|
||||
assertEquals(0, track.getValue().getTripStatistics().getTotalTime());
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid xml input.
|
||||
*/
|
||||
public void testInvalidXml() throws Exception {
|
||||
@Test
|
||||
public void testInvalidXml() {
|
||||
testInvalidGpx(INVALID_XML_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid location.
|
||||
*/
|
||||
public void testInvalidLocation() throws Exception {
|
||||
@Test
|
||||
public void testInvalidLocation() {
|
||||
testInvalidGpx(INVALID_LOCATION_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid time.
|
||||
*/
|
||||
public void testInvalidTime() throws Exception {
|
||||
@Test
|
||||
public void testInvalidTime() {
|
||||
testInvalidGpx(INVALID_TIME_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid altitude.
|
||||
*/
|
||||
public void testInvalidAltitude() throws Exception {
|
||||
@Test
|
||||
public void testInvalidAltitude() {
|
||||
testInvalidGpx(INVALID_ALTITUDE_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid latitude.
|
||||
*/
|
||||
public void testInvalidLatitude() throws Exception {
|
||||
@Test
|
||||
public void testInvalidLatitude() {
|
||||
testInvalidGpx(INVALID_LATITUDE_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an invalid longitude.
|
||||
*/
|
||||
public void testInvalidLongitude() throws Exception {
|
||||
@Test
|
||||
public void testInvalidLongitude() {
|
||||
testInvalidGpx(INVALID_LONGITUDE_GPX);
|
||||
}
|
||||
|
||||
private void testInvalidGpx(String xml) throws Exception {
|
||||
expect(contentProviderUtils.insertTrack((Track) AndroidMock.anyObject()))
|
||||
.andReturn(TRACK_ID_0_URI);
|
||||
private void testInvalidGpx(String xml) {
|
||||
when(contentProviderUtils.insertTrack((Track) any())).thenReturn(TRACK_ID_0_URI);
|
||||
|
||||
// For the following, use StubReturn since we don't care whether they are
|
||||
// invoked or not.
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
(Location[]) AndroidMock.anyObject(), AndroidMock.anyInt(), AndroidMock.anyLong()))
|
||||
.andStubReturn(1);
|
||||
expect(contentProviderUtils.getFirstTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andStubReturn(TRACK_POINT_ID_0);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
contentProviderUtils.deleteTrack(getContext(), TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), anyInt(), anyLong())).thenReturn(1);
|
||||
when(contentProviderUtils.getFirstTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_0);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_0);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
contentProviderUtils.deleteTrack(context, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(xml.getBytes());
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(
|
||||
getContext(), contentProviderUtils);
|
||||
GpxFileTrackImporter gpxFileTrackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||
long trackId = gpxFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(-1L, trackId);
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
Assert.assertEquals(-1L, trackId);
|
||||
verify(contentProviderUtils);
|
||||
}
|
||||
}
|
||||
|
||||
+41
-45
@@ -18,24 +18,31 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link KmlFileTrackImporter}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
|
||||
private static final String VALID_ONE_TRACK_ONE_SEGMENT_GPX =
|
||||
@@ -59,83 +66,72 @@ public class KmlFileTrackImporterTest extends AbstractTestFileTrackImporter {
|
||||
String latitude = Double.toString(TRACK_LATITUDE + index);
|
||||
String longitude = Double.toString(TRACK_LONGITUDE + index);
|
||||
String altitude = Double.toString(TRACK_ELEVATION + index);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " "
|
||||
+ altitude + "</gx:coord>");
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("<when>" + time + "</when>" + "<gx:coord>" + longitude + " " + latitude + " " + altitude + "</gx:coord>");
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests one track with one segment.
|
||||
*/
|
||||
@Test
|
||||
public void testOneTrackOneSegment() throws Exception {
|
||||
Capture<Track> track = new Capture<Track>();
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
Location location1 = createLocation(1, DATE_FORMAT_1.parse(TRACK_TIME_1).getTime());
|
||||
|
||||
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
||||
contentProviderUtils.clearTrack(context, TRACK_ID_0);
|
||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
LocationsMatcher.eqLoc(location1), eq(1), eq(TRACK_ID_0))).andReturn(1);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_1);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint(new Location[]{location1}, eq(1), eq(TRACK_ID_0))).thenReturn(1);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_1);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_ONE_SEGMENT_GPX.getBytes());
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
||||
getContext(), TRACK_ID_0, contentProviderUtils);
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
|
||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
assertEquals(time1 - time0, track.getValue().getTripStatistics().getTotalTime());
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
Assert.assertEquals(time1 - time0, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, time0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests one track with two segments.
|
||||
*/
|
||||
@Test
|
||||
public void testOneTrackTwoSegments() throws Exception {
|
||||
Capture<Track> track = new Capture<Track>();
|
||||
ArgumentCaptor<Track> trackCaptor = ArgumentCaptor.forClass(Track.class);
|
||||
|
||||
Location location0 = createLocation(0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
|
||||
contentProviderUtils.clearTrack(getContext(), TRACK_ID_0);
|
||||
contentProviderUtils.clearTrack(context, TRACK_ID_0);
|
||||
expectFirstTrackPoint(location0, TRACK_ID_0, TRACK_POINT_ID_0);
|
||||
|
||||
// A flush happens at the end
|
||||
expect(contentProviderUtils.bulkInsertTrackPoint(
|
||||
(Location[]) AndroidMock.anyObject(), eq(5), eq(TRACK_ID_0))).andStubReturn(5);
|
||||
expect(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).andReturn(TRACK_POINT_ID_3);
|
||||
expect(
|
||||
contentProviderUtils.getTrack(PreferencesUtils.getLong(getContext(),
|
||||
R.string.recording_track_id_key))).andStubReturn(null);
|
||||
expectUpdateTrack(track, true, TRACK_ID_0);
|
||||
AndroidMock.replay(contentProviderUtils);
|
||||
when(contentProviderUtils.bulkInsertTrackPoint((Location[]) any(), eq(5), eq(TRACK_ID_0))).thenReturn(5);
|
||||
when(contentProviderUtils.getLastTrackPointId(TRACK_ID_0)).thenReturn(TRACK_POINT_ID_3);
|
||||
when(contentProviderUtils.getTrack(PreferencesUtils.getLong(context, R.string.recording_track_id_key))).thenReturn(null);
|
||||
expectUpdateTrack(trackCaptor, true, TRACK_ID_0);
|
||||
|
||||
InputStream inputStream = new ByteArrayInputStream(VALID_ONE_TRACK_TWO_SEGMENTS_GPX.getBytes());
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(
|
||||
getContext(), TRACK_ID_0, contentProviderUtils);
|
||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context, TRACK_ID_0, contentProviderUtils);
|
||||
long trackId = kmlFileTrackImporter.importFile(inputStream);
|
||||
assertEquals(TRACK_ID_0, trackId);
|
||||
Assert.assertEquals(TRACK_ID_0, trackId);
|
||||
|
||||
long time0 = DATE_FORMAT_0.parse(TRACK_TIME_0).getTime();
|
||||
long time1 = DATE_FORMAT_1.parse(TRACK_TIME_1).getTime();
|
||||
long time2 = DATE_FORMAT_1.parse(TRACK_TIME_2).getTime();
|
||||
long time3 = DATE_FORMAT_1.parse(TRACK_TIME_3).getTime();
|
||||
assertEquals(
|
||||
time1 - time0 + time3 - time2, track.getValue().getTripStatistics().getTotalTime());
|
||||
Assert.assertEquals(time1 - time0 + time3 - time2, trackCaptor.getValue().getTripStatistics().getTotalTime());
|
||||
|
||||
AndroidMock.verify(contentProviderUtils);
|
||||
verifyTrack(track.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0,
|
||||
DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
verify(contentProviderUtils);
|
||||
verifyTrack(trackCaptor.getValue(), TRACK_NAME_0, TRACK_DESCRIPTION_0, DATE_FORMAT_0.parse(TRACK_TIME_0).getTime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +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.io.file.importer;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
|
||||
import org.easymock.IArgumentMatcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Locations matcher. Workaround because of capture bug 2617107 in easymock:
|
||||
* http://sourceforge.net/tracker/?func=detail&aid=2617107&group_id=82958&atid=567837
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class LocationsMatcher implements IArgumentMatcher {
|
||||
|
||||
private final Location[] expectedLocations;
|
||||
|
||||
private LocationsMatcher(Location[] expected) {
|
||||
this.expectedLocations = expected;
|
||||
}
|
||||
|
||||
public static Location[] eqLoc(Location expected) {
|
||||
return eqLoc(new Location[]{expected});
|
||||
}
|
||||
|
||||
public static Location[] eqLoc(Location[] expected) {
|
||||
IArgumentMatcher matcher = new LocationsMatcher(expected);
|
||||
AndroidMock.reportMatcher(matcher);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buf) {
|
||||
buf.append("eqLoc(").append(Arrays.toString(expectedLocations)).append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object obj) {
|
||||
if (!(obj instanceof Location[])) {
|
||||
return false;
|
||||
}
|
||||
Location[] locations = (Location[]) obj;
|
||||
if (locations.length < expectedLocations.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only check the first elements (those that will be taken into account)
|
||||
for (int i = 0; i < expectedLocations.length; i++) {
|
||||
if (!matchLocation(locations[i], expectedLocations[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean matchLocation(Location location1, Location location2) {
|
||||
return (location1.getTime() == location2.getTime())
|
||||
&& (location1.getLatitude() == location2.getLatitude())
|
||||
&& (location1.getLongitude() == location2.getLongitude())
|
||||
&& (location1.getAltitude() == location2.getAltitude());
|
||||
}
|
||||
}
|
||||
+40
-45
@@ -17,55 +17,58 @@ package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.RemoteException;
|
||||
import android.test.ServiceTestCase;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link ControlRecordingService}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordingService> {
|
||||
public class ControlRecordingServiceTest {
|
||||
|
||||
private Context context;
|
||||
private ControlRecordingService controlRecordingService;
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = new ServiceTestRule();
|
||||
|
||||
public ControlRecordingServiceTest() {
|
||||
super(ControlRecordingService.class);
|
||||
}
|
||||
@Rule
|
||||
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context = getContext();
|
||||
}
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private ITrackRecordingService iTrackRecordingServiceMock;
|
||||
|
||||
private ControlRecordingService subject;
|
||||
|
||||
/**
|
||||
* Tests the start of {@link ControlRecordingService} and tests the method
|
||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||
* to start a track recording.
|
||||
*/
|
||||
@UsesMocks(ITrackRecordingService.class)
|
||||
public void testStartRecording() {
|
||||
assertNull(controlRecordingService);
|
||||
@Test
|
||||
public void testStartRecording() throws TimeoutException {
|
||||
Assert.assertNull(subject);
|
||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_start));
|
||||
assertNotNull(controlRecordingService);
|
||||
Assert.assertNotNull(subject);
|
||||
|
||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||
.createStrictMock(ITrackRecordingService.class);
|
||||
try {
|
||||
EasyMock.expect(iTrackRecordingServiceMock.startNewTrack()).andReturn(1L);
|
||||
EasyMock.replay(iTrackRecordingServiceMock);
|
||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
EasyMock.verify(iTrackRecordingServiceMock);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
when(iTrackRecordingServiceMock.startNewTrack()).thenReturn(1L);
|
||||
subject.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
verify(iTrackRecordingServiceMock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,20 +76,13 @@ public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordin
|
||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||
* to stop a track recording.
|
||||
*/
|
||||
@UsesMocks(ITrackRecordingService.class)
|
||||
public void testStopRecording() {
|
||||
@Test
|
||||
public void testStopRecording() throws TimeoutException {
|
||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_end));
|
||||
iTrackRecordingServiceMock.endCurrentTrack();
|
||||
|
||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||
.createStrictMock(ITrackRecordingService.class);
|
||||
try {
|
||||
iTrackRecordingServiceMock.endCurrentTrack();
|
||||
EasyMock.replay(iTrackRecordingServiceMock);
|
||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
EasyMock.verify(iTrackRecordingServiceMock);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
subject.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
verify(iTrackRecordingServiceMock);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,12 +90,11 @@ public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordin
|
||||
*
|
||||
* @param action the action string in the start intent
|
||||
*/
|
||||
private Intent startControlRecordingService(String action) {
|
||||
private Intent startControlRecordingService(String action) throws TimeoutException {
|
||||
Intent intent = new Intent(context, ControlRecordingService.class);
|
||||
intent.setAction(action);
|
||||
startService(intent);
|
||||
controlRecordingService = getService();
|
||||
mServiceRule.startService(intent);
|
||||
subject = ((ControlRecordingService) mServiceRule.bindService(intent));
|
||||
return intent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+199
-297
@@ -16,28 +16,32 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.ServiceTestCase;
|
||||
import android.test.mock.MockContentProvider;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.MediumTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
@@ -46,175 +50,100 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the track recording service.
|
||||
* <p>
|
||||
* TODO: The original class, ServiceTestCase, has a few limitations, e.g. it's
|
||||
* not possible to properly shutdown the service, unless tearDown() is called,
|
||||
* which prevents from testing multiple scenarios in a single test (see
|
||||
* runFunctionTest for more details).
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingService> {
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTest {
|
||||
|
||||
private Context context;
|
||||
@Rule
|
||||
public final ServiceTestRule mServiceRule = new ServiceTestRule();
|
||||
private ContentProviderUtils providerUtils;
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
/*
|
||||
* In order to support starting and binding to the service in the same unit
|
||||
* test, we provide a workaround, as the original class doesn't allow to bind
|
||||
* after the service has been previously started.
|
||||
*/
|
||||
|
||||
private boolean bound;
|
||||
private Intent serviceIntent;
|
||||
|
||||
public TrackRecordingServiceTest() {
|
||||
super(TestRecordingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBinder bindService(Intent intent) {
|
||||
if (getService() != null) {
|
||||
if (bound) {
|
||||
throw new IllegalStateException("Service: " + getService() + " is already bound");
|
||||
}
|
||||
bound = true;
|
||||
serviceIntent = intent.cloneFilter();
|
||||
return getService().onBind(intent);
|
||||
} else {
|
||||
return super.bindService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownService() {
|
||||
if (bound) {
|
||||
assertNotNull(getService());
|
||||
getService().onUnbind(serviceIntent);
|
||||
bound = false;
|
||||
}
|
||||
super.shutdownService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
/*
|
||||
* Create a mock context that uses a mock content resolver and a renaming
|
||||
* delegating context.
|
||||
*/
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext renamingDelegatingContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
context = new MockContext(mockContentResolver, renamingDelegatingContext);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Set up the mock content resolver
|
||||
CustomContentProvider customContentProvider = new CustomContentProvider();
|
||||
customContentProvider.attachInfo(context, null);
|
||||
mockContentResolver.addProvider(ContentProviderUtils.AUTHORITY, customContentProvider);
|
||||
|
||||
MockContentProvider settingsProvider = new MockContentProvider(context) {
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
ContentProvider customContentProvider = new CustomContentProvider() {
|
||||
};
|
||||
mockContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
|
||||
|
||||
// Set the context
|
||||
setContext(context);
|
||||
customContentProvider.attachInfo(context, null);
|
||||
|
||||
providerUtils = ContentProviderUtils.Factory.get(context);
|
||||
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Disable auto resume by default.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, 0);
|
||||
// No recording track.
|
||||
PreferencesUtils.setLong(
|
||||
context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testStartable() {
|
||||
startService(createStartIntent());
|
||||
assertNotNull(getService());
|
||||
@Test
|
||||
public void testStartable() throws TimeoutException {
|
||||
Intent intent = new Intent(context, TrackRecordingService.class);
|
||||
mServiceRule.startService(createStartIntent());
|
||||
Assert.assertNotNull(mServiceRule.bindService(createStartIntent()));
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testBindable() {
|
||||
IBinder service = bindService(createStartIntent());
|
||||
assertNotNull(service);
|
||||
@Test
|
||||
public void testBindable() throws TimeoutException {
|
||||
IBinder service = mServiceRule.bindService(createStartIntent());
|
||||
Assert.assertNotNull(service);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_shouldResume() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(123L, System.currentTimeMillis(), true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We expect to resume the previous track.
|
||||
assertTrue(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(123L, service.getRecordingTrackId());
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertEquals(123L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: shutdownService() has a bug and doesn't set mServiceCreated to false,
|
||||
* thus preventing from a second call to onCreate(). Report the bug to Android
|
||||
* team. Until then, the following tests and checks must be commented out.
|
||||
*
|
||||
* TODO: If fixed, remove "disabled" prefix from the test name.
|
||||
*/
|
||||
@MediumTest
|
||||
public void disabledTestResumeAfterReboot_simulateReboot() throws Exception {
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
@Test
|
||||
public void testResumeAfterReboot_simulateReboot() throws Exception {
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
// Simulate recording a track.
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
shutdownService();
|
||||
assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertEquals(id, service.getRecordingTrackId());
|
||||
mServiceRule.unbindService();
|
||||
Assert.assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
mServiceRule.startService(startIntent);
|
||||
service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
assertTrue(getService().isRecording());
|
||||
Assert.assertTrue(service.isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_noRecordingTrack() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(123L, System.currentTimeMillis(), false);
|
||||
@@ -226,16 +155,16 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because it was stopped.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_expiredTrack() throws Exception {
|
||||
// Insert a dummy track last updated 20 min ago.
|
||||
createDummyTrack(123L, System.currentTimeMillis() - 20 * 60 * 1000, true);
|
||||
@@ -247,16 +176,16 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because it has expired.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testResumeAfterReboot_tooManyAttempts() throws Exception {
|
||||
// Insert a dummy track.
|
||||
createDummyTrack(123L, System.currentTimeMillis(), true);
|
||||
@@ -268,49 +197,52 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(TrackRecordingService.RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(startIntent));
|
||||
Assert.assertNotNull(service);
|
||||
|
||||
// We don't expect to resume the previous track, because there were already
|
||||
// too many attempts.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_noTracks() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
Assert.assertTrue(tracks.isEmpty());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
// Test if we start in no-recording mode by default.
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_oldTracks() throws Exception {
|
||||
createDummyTrack(123L, -1L, false);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||
// Just set recording track to a bogus value.
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 256L);
|
||||
|
||||
// Make sure that the service will not start recording and will clear
|
||||
// the bogus track.
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testStartNewTrack_noRecording() throws Exception {
|
||||
// NOTICE: due to the way Android permissions work, if this fails,
|
||||
// uninstall the test apk then retry - the test must be installed *after* the app (go figure).
|
||||
@@ -320,51 +252,50 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
context.registerReceiver(startReceiver, new IntentFilter(startAction));
|
||||
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
Assert.assertTrue(tracks.isEmpty());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(id >= 0);
|
||||
assertTrue(service.isRecording());
|
||||
Assert.assertTrue(id >= 0);
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Track track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
assertEquals(PreferencesUtils.getString(
|
||||
context, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT),
|
||||
track.getCategory());
|
||||
assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
Assert.assertNotNull(track);
|
||||
Assert.assertEquals(id, track.getId());
|
||||
Assert.assertEquals(PreferencesUtils.getString(context, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT), track.getCategory());
|
||||
Assert.assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(id, service.getRecordingTrackId());
|
||||
|
||||
// Verify that the start broadcast was received.
|
||||
assertTrue(startReceiver.waitUntilReceived(1));
|
||||
Assert.assertTrue(startReceiver.waitUntilReceived(1));
|
||||
List<Intent> receivedIntents = startReceiver.getReceivedIntents();
|
||||
assertEquals(1, receivedIntents.size());
|
||||
Assert.assertEquals(1, receivedIntents.size());
|
||||
Intent broadcastIntent = receivedIntents.get(0);
|
||||
assertEquals(startAction, broadcastIntent.getAction());
|
||||
assertEquals(id,
|
||||
broadcastIntent.getLongExtra(context.getString(R.string.track_id_broadcast_extra), -1L));
|
||||
Assert.assertEquals(startAction, broadcastIntent.getAction());
|
||||
Assert.assertEquals(id, broadcastIntent.getLongExtra(context.getString(R.string.track_id_broadcast_extra), -1L));
|
||||
|
||||
context.unregisterReceiver(startReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
|
||||
// Starting a new track when there is a recording should just return -1L.
|
||||
long newTrack = service.startNewTrack();
|
||||
assertEquals(-1L, newTrack);
|
||||
Assert.assertEquals(-1L, newTrack);
|
||||
|
||||
assertEquals(123L, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(123L, service.getRecordingTrackId());
|
||||
Assert.assertEquals(123L, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(123L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testEndCurrentTrack_alreadyRecording() throws Exception {
|
||||
// See comment above if this fails randomly.
|
||||
BlockingBroadcastReceiver stopReceiver = new BlockingBroadcastReceiver();
|
||||
@@ -373,32 +304,31 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
|
||||
// End the current track.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
|
||||
PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
|
||||
// Verify that the stop broadcast was received.
|
||||
assertTrue(stopReceiver.waitUntilReceived(1));
|
||||
Assert.assertTrue(stopReceiver.waitUntilReceived(1));
|
||||
List<Intent> receivedIntents = stopReceiver.getReceivedIntents();
|
||||
assertEquals(1, receivedIntents.size());
|
||||
Assert.assertEquals(1, receivedIntents.size());
|
||||
Intent broadcastIntent = receivedIntents.get(0);
|
||||
assertEquals(stopAction, broadcastIntent.getAction());
|
||||
assertEquals(123L,
|
||||
broadcastIntent.getLongExtra(context.getString(R.string.track_id_broadcast_extra), -1L));
|
||||
Assert.assertEquals(stopAction, broadcastIntent.getAction());
|
||||
Assert.assertEquals(123L, broadcastIntent.getLongExtra(context.getString(R.string.track_id_broadcast_extra), -1L));
|
||||
|
||||
context.unregisterReceiver(stopReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
/*
|
||||
* Ending the current track when there is no recording should not result in
|
||||
@@ -406,204 +336,196 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
*/
|
||||
service.endCurrentTrack();
|
||||
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
|
||||
PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testIntegration_completeRecordingSession() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
Assert.assertTrue(tracks.isEmpty());
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
assertEquals(-1L, waypointId);
|
||||
Assert.assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
assertFalse(service.isPaused());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Assert.assertFalse(service.isPaused());
|
||||
insertLocation(service);
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
Assert.assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
Assert.assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
|
||||
Waypoint wpt = providerUtils.getWaypoint(2);
|
||||
assertEquals(getContext().getString(R.string.marker_statistics_icon_url), wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.marker_split_name_format, 1), wpt.getName());
|
||||
assertEquals(WaypointType.STATISTICS, wpt.getType());
|
||||
assertEquals(123L, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNotNull(wpt.getTripStatistics());
|
||||
Assert.assertEquals(context.getString(R.string.marker_statistics_icon_url), wpt.getIcon());
|
||||
Assert.assertEquals(context.getString(R.string.marker_split_name_format, 1), wpt.getName());
|
||||
Assert.assertEquals(WaypointType.STATISTICS, wpt.getType());
|
||||
Assert.assertEquals(123L, wpt.getTrackId());
|
||||
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||
Assert.assertNotNull(wpt.getLocation());
|
||||
Assert.assertNotNull(wpt.getTripStatistics());
|
||||
// TODO check the rest of the params.
|
||||
|
||||
// TODO: Check waypoint 2.
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
long waypointId = service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT);
|
||||
assertEquals(-1L, waypointId);
|
||||
Assert.assertEquals(-1L, waypointId);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertTrue(service.isRecording());
|
||||
insertLocation(service);
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT));
|
||||
Assert.assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT));
|
||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||
assertEquals(getContext().getString(R.string.marker_waypoint_icon_url), wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
assertEquals(WaypointType.WAYPOINT, wpt.getType());
|
||||
assertEquals(123L, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNull(wpt.getTripStatistics());
|
||||
Assert.assertEquals(context.getString(R.string.marker_waypoint_icon_url), wpt.getIcon());
|
||||
Assert.assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
Assert.assertEquals(WaypointType.WAYPOINT, wpt.getType());
|
||||
Assert.assertEquals(123L, wpt.getTrackId());
|
||||
Assert.assertEquals(0.0, wpt.getLength(), 0.01);
|
||||
Assert.assertNotNull(wpt.getLocation());
|
||||
Assert.assertNull(wpt.getTripStatistics());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(
|
||||
context, R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, PreferencesUtils.VOICE_FREQUENCY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_voiceFrequencyByTime() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistanceDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key,
|
||||
PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_maxRecordingDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, 50);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistanceDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(
|
||||
context, R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, PreferencesUtils.SPLIT_FREQUENCY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_splitFrequencyByTime() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDisabled() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingIntervalDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key,
|
||||
PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRecordingInterval() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key,
|
||||
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_minRequiredAccuracy() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, 500);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_sensorTypeDefault() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.sensor_type_key, PreferencesUtils.SENSOR_TYPE_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_sensorTypeZephyr() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.sensor_type_key, context.getString(R.string.sensor_type_value_zephyr));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(bindService(intent));
|
||||
assertNotNull(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
private Track createDummyTrack(long id, long stopTime, boolean isRecording) {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(id);
|
||||
@@ -627,27 +549,26 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
}
|
||||
|
||||
private void addTrack(Track track, boolean isRecording) {
|
||||
assertTrue(track.getId() >= 0);
|
||||
Assert.assertTrue(track.getId() >= 0);
|
||||
providerUtils.insertTrack(track);
|
||||
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key,
|
||||
isRecording ? track.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
Assert.assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
||||
}
|
||||
|
||||
private void fullRecordingSession() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
ITrackRecordingService service = ((ITrackRecordingService) mServiceRule.bindService(createStartIntent()));
|
||||
Assert.assertFalse(service.isRecording());
|
||||
|
||||
// Start a track.
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(id >= 0);
|
||||
assertTrue(service.isRecording());
|
||||
Assert.assertTrue(id >= 0);
|
||||
Assert.assertTrue(service.isRecording());
|
||||
Track track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
Assert.assertNotNull(track);
|
||||
Assert.assertEquals(id, track.getId());
|
||||
Assert.assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
Assert.assertEquals(id, service.getRecordingTrackId());
|
||||
|
||||
// Insert a few points, markers and statistics.
|
||||
long startTime = System.currentTimeMillis();
|
||||
@@ -670,15 +591,15 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
|
||||
// Stop the track. Validate if it has correct data.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
Assert.assertFalse(service.isRecording());
|
||||
Assert.assertEquals(-1L, service.getRecordingTrackId());
|
||||
track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
Assert.assertNotNull(track);
|
||||
Assert.assertEquals(id, track.getId());
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
assertNotNull(tripStatistics);
|
||||
assertTrue(tripStatistics.getStartTime() > 0);
|
||||
assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
|
||||
Assert.assertNotNull(tripStatistics);
|
||||
Assert.assertTrue(tripStatistics.getStartTime() > 0);
|
||||
Assert.assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,8 +607,7 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
*
|
||||
* @param trackRecordingService the track recording service
|
||||
*/
|
||||
private void insertLocation(ITrackRecordingService trackRecordingService)
|
||||
throws RemoteException, InterruptedException {
|
||||
private void insertLocation(ITrackRecordingService trackRecordingService) throws InterruptedException {
|
||||
Location location = new Location("gps");
|
||||
location.setLongitude(35.0f);
|
||||
location.setLatitude(45.0f);
|
||||
@@ -699,30 +619,12 @@ public class TrackRecordingServiceTest extends ServiceTestCase<TestRecordingServ
|
||||
Thread.sleep(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* A context wrapper with the user provided {@link ContentResolver}. TODO:
|
||||
* Move to test utils package.
|
||||
*/
|
||||
public static class MockContext extends ContextWrapper {
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
public MockContext(ContentResolver contentResolver, Context base) {
|
||||
super(base);
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return contentResolver;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous/waitable broadcast receiver to be used in testing.
|
||||
*/
|
||||
private class BlockingBroadcastReceiver extends BroadcastReceiver {
|
||||
private static final long MAX_WAIT_TIME_MS = 10000;
|
||||
private List<Intent> receivedIntents = new ArrayList<Intent>();
|
||||
private final List<Intent> receivedIntents = new ArrayList<>();
|
||||
|
||||
public List<Intent> getReceivedIntents() {
|
||||
return receivedIntents;
|
||||
|
||||
+9
-2
@@ -16,10 +16,13 @@
|
||||
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
@@ -30,9 +33,13 @@ import org.junit.runner.RunWith;
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AnnouncementPeriodicTaskFactoryTest {
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
PeriodicTaskFactory factory = new AnnouncementPeriodicTaskFactory();
|
||||
PeriodicTask task = factory.create(InstrumentationRegistry.getInstrumentation().getContext());
|
||||
PeriodicTask task = factory.create(context);
|
||||
Assert.assertTrue(task instanceof AnnouncementPeriodicTask);
|
||||
}
|
||||
}
|
||||
|
||||
+114
-117
@@ -21,24 +21,31 @@ import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.capture;
|
||||
import static com.google.android.testing.mocking.AndroidMock.eq;
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import static com.google.android.testing.mocking.AndroidMock.same;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.same;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnouncementPeriodicTask}.
|
||||
@@ -47,28 +54,31 @@ import static com.google.android.testing.mocking.AndroidMock.same;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AnnouncementPeriodicTaskTest {
|
||||
|
||||
// Use something other than our hardcoded value
|
||||
private static final Locale DEFAULT_LOCALE = Locale.KOREAN;
|
||||
private static final String ANNOUNCEMENT = "I can haz cheeseburger?";
|
||||
private Locale oldDefaultLocale;
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private AnnouncementPeriodicTask task;
|
||||
@Mock
|
||||
private AnnouncementPeriodicTask mockTask;
|
||||
private Capture<OnInitListener> initListenerCapture;
|
||||
private Capture<PhoneStateListener> phoneListenerCapture;
|
||||
private ArgumentCaptor<OnInitListener> initListenerCapture = ArgumentCaptor.forClass(OnInitListener.class);
|
||||
private ArgumentCaptor<PhoneStateListener> phoneListenerCapture = ArgumentCaptor.forClass(PhoneStateListener.class);
|
||||
private TextToSpeechDelegate ttsDelegate;
|
||||
@Mock
|
||||
private TextToSpeechInterface tts;
|
||||
|
||||
@UsesMocks({
|
||||
AnnouncementPeriodicTask.class,
|
||||
StringUtils.class,
|
||||
})
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// @UsesMocks({
|
||||
// StringUtils.class,
|
||||
// })
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
oldDefaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(DEFAULT_LOCALE);
|
||||
|
||||
@@ -84,7 +94,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
};
|
||||
|
||||
ttsDelegate = new TextToSpeechDelegate(getContext(), blockingListener);
|
||||
ttsDelegate = new TextToSpeechDelegate(context, blockingListener);
|
||||
|
||||
// Wait for all async operations done in the constructor to finish.
|
||||
synchronized (blockingListener) {
|
||||
@@ -94,18 +104,10 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
// Phew, done, now we can start forwarding calls
|
||||
tts = AndroidMock.createMock(TextToSpeechInterface.class);
|
||||
|
||||
initListenerCapture = new Capture<OnInitListener>();
|
||||
phoneListenerCapture = new Capture<PhoneStateListener>();
|
||||
|
||||
// Create a partial forwarding mock
|
||||
mockTask = AndroidMock.createMock(AnnouncementPeriodicTask.class, getContext());
|
||||
task = new AnnouncementPeriodicTask(getContext()) {
|
||||
task = new AnnouncementPeriodicTask(context) {
|
||||
@Override
|
||||
protected TextToSpeech newTextToSpeech(Context ctx,
|
||||
OnInitListener onInitListener) {
|
||||
protected TextToSpeech newTextToSpeech(Context ctx, OnInitListener onInitListener) {
|
||||
return mockTask.newTextToSpeech(ctx, onInitListener);
|
||||
}
|
||||
|
||||
@@ -115,116 +117,109 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void listenToPhoneState(
|
||||
PhoneStateListener listener, int events) {
|
||||
protected void listenToPhoneState(PhoneStateListener listener, int events) {
|
||||
mockTask.listenToPhoneState(listener, events);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
@After
|
||||
public void tearDown() {
|
||||
Locale.setDefault(oldDefaultLocale);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStart() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
Assert.assertNotNull(ttsInitListener);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStart_notReady() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
Assert.assertNotNull(ttsInitListener);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.ERROR);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShutdown() {
|
||||
// First, start
|
||||
doStart();
|
||||
AndroidMock.verify(mockTask);
|
||||
AndroidMock.reset(mockTask);
|
||||
verify(mockTask);
|
||||
reset(mockTask);
|
||||
|
||||
// Then, shut down
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
mockTask.listenToPhoneState(
|
||||
same(phoneListener), eq(PhoneStateListener.LISTEN_NONE));
|
||||
tts.shutdown();
|
||||
AndroidMock.replay(mockTask, tts);
|
||||
task.shutdown();
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun() throws Exception {
|
||||
// Expect service data calls
|
||||
@Test
|
||||
public void testRun() {
|
||||
// when service data calls
|
||||
TripStatistics stats = new TripStatistics();
|
||||
|
||||
// Expect announcement building call
|
||||
expect(mockTask.getAnnouncement(same(stats))).andStubReturn(ANNOUNCEMENT);
|
||||
// when announcement building call
|
||||
when(mockTask.getAnnouncement(same(stats))).thenReturn(ANNOUNCEMENT);
|
||||
|
||||
// Put task in "ready" state
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isLanguageAvailable(DEFAULT_LOCALE)).andStubReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
expect(tts.setLanguage(DEFAULT_LOCALE)).andReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
expect(tts.setSpeechRate(AnnouncementPeriodicTask.TTS_SPEECH_RATE)).andReturn(
|
||||
TextToSpeech.SUCCESS);
|
||||
expect(tts.setOnUtteranceCompletedListener((OnUtteranceCompletedListener) EasyMock.anyObject()))
|
||||
.andReturn(0);
|
||||
|
||||
// Expect actual announcement call
|
||||
expect(
|
||||
tts.speak(eq(ANNOUNCEMENT), eq(TextToSpeech.QUEUE_FLUSH),
|
||||
eq(AnnouncementPeriodicTask.SPEECH_PARAMS))).andReturn(0);
|
||||
when(tts.isLanguageAvailable(DEFAULT_LOCALE)).thenReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
when(tts.setLanguage(DEFAULT_LOCALE)).thenReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
when(tts.setSpeechRate(AnnouncementPeriodicTask.TTS_SPEECH_RATE)).thenReturn(TextToSpeech.SUCCESS);
|
||||
when(tts.setOnUtteranceCompletedListener((OnUtteranceCompletedListener) any())).thenReturn(0);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.announce(stats);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_notReady() throws Exception {
|
||||
@Test
|
||||
public void testRun_notReady() {
|
||||
// Put task in "not ready" state
|
||||
startTask(TextToSpeech.ERROR);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_duringCall() throws Exception {
|
||||
@Test
|
||||
public void testRun_duringCall() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
when(tts.isSpeaking()).thenReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_ringWhileSpeaking() throws Exception {
|
||||
@Test
|
||||
public void testRun_ringWhileSpeaking() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(true);
|
||||
expect(tts.stop()).andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
when(tts.isSpeaking()).thenReturn(true);
|
||||
when(tts.stop()).thenReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
// Update the state to ringing - this should stop the current announcement.
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
@@ -232,111 +227,118 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
|
||||
// Run the announcement - this should do nothing.
|
||||
task.run(null);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_whileRinging() throws Exception {
|
||||
@Test
|
||||
public void testRun_whileRinging() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
when(tts.isSpeaking()).thenReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_noService() throws Exception {
|
||||
@Test
|
||||
public void testRun_noService() {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_noStats() throws Exception {
|
||||
// Expect service data calls
|
||||
@Test
|
||||
public void testRun_noStats() {
|
||||
// when service data calls
|
||||
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
//TODO
|
||||
//AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time zero.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_time_zero() {
|
||||
long time = 0; // 0 seconds
|
||||
assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with time one.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_time_one() {
|
||||
long time = 1 * 1000; // 1 second
|
||||
assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||
* numbers with the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_singular_has_hour() {
|
||||
long time = (1 * 60 * 60 * 1000) + (1 * 60 * 1000) + (1 * 1000); // 1 hour 1 minute 1 second
|
||||
assertEquals("1 hour 1 minute 1 second", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("1 hour 1 minute 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||
* with the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_plural_has_hour() {
|
||||
long time = (2 * 60 * 60 * 1000) + (2 * 60 * 1000) + (2 * 1000); // 2 hours 2 minutes 2 seconds
|
||||
assertEquals("2 hours 2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("2 hours 2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with singular
|
||||
* numbers without the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_singular_no_hour() {
|
||||
long time = (1 * 60 * 1000) + (1 * 1000); // 1 minute 1 second
|
||||
assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link AnnouncementPeriodicTask#getAnnounceTime(long)} with plural numbers
|
||||
* without the hour unit.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAnnounceTime_plural_no_hour() {
|
||||
long time = (2 * 60 * 1000) + (2 * 1000); // 2 minutes 2 seconds
|
||||
assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
Assert.assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
private void startTask(int state) {
|
||||
AndroidMock.resetToNice(tts);
|
||||
AndroidMock.replay(tts);
|
||||
reset(tts);
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
ttsInitListener.onInit(state);
|
||||
AndroidMock.resetToDefault(tts);
|
||||
//TODO
|
||||
reset(tts);
|
||||
//AndroidMock.resetToDefault(tts);
|
||||
}
|
||||
|
||||
private void doStart() {
|
||||
mockTask.listenToPhoneState(capture(phoneListenerCapture),
|
||||
eq(PhoneStateListener.LISTEN_CALL_STATE));
|
||||
expect(mockTask.newTextToSpeech(
|
||||
same(getContext()), capture(initListenerCapture)))
|
||||
.andStubReturn(ttsDelegate);
|
||||
AndroidMock.replay(mockTask);
|
||||
mockTask.listenToPhoneState(phoneListenerCapture.capture(), eq(PhoneStateListener.LISTEN_CALL_STATE));
|
||||
when(mockTask.newTextToSpeech(same(context), initListenerCapture.capture())).thenReturn(ttsDelegate);
|
||||
|
||||
task.start();
|
||||
}
|
||||
|
||||
@@ -401,8 +403,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
* set) will go up to the original class, but after tts is set we'll forward
|
||||
* them all to the mock.
|
||||
*/
|
||||
private class TextToSpeechDelegate
|
||||
extends TextToSpeech implements TextToSpeechInterface {
|
||||
private class TextToSpeechDelegate extends TextToSpeech implements TextToSpeechInterface {
|
||||
public TextToSpeechDelegate(Context context, OnInitListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
@@ -464,8 +465,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playEarcon(String earcon, int queueMode,
|
||||
HashMap<String, String> params) {
|
||||
public int playEarcon(String earcon, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playEarcon(earcon, queueMode, params);
|
||||
}
|
||||
@@ -473,8 +473,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playSilence(long durationInMs, int queueMode,
|
||||
HashMap<String, String> params) {
|
||||
public int playSilence(long durationInMs, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playSilence(durationInMs, queueMode, params);
|
||||
}
|
||||
@@ -490,8 +489,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOnUtteranceCompletedListener(
|
||||
OnUtteranceCompletedListener listener) {
|
||||
public int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener) {
|
||||
if (tts == null) {
|
||||
return super.setOnUtteranceCompletedListener(listener);
|
||||
}
|
||||
@@ -541,8 +539,7 @@ public class AnnouncementPeriodicTaskTest extends AndroidTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int synthesizeToFile(String text, HashMap<String, String> params,
|
||||
String filename) {
|
||||
public int synthesizeToFile(String text, HashMap<String, String> params, String filename) {
|
||||
if (tts == null) {
|
||||
return super.synthesizeToFile(text, params, filename);
|
||||
}
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
@@ -33,9 +36,13 @@ import java.util.TimeZone;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StringUtilsTest {
|
||||
|
||||
private Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testFormatDateTimeIso8601() {
|
||||
Assert.assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
||||
}
|
||||
@@ -43,6 +50,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#formatElapsedTime(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testformatElapsedTime() {
|
||||
// 1 second
|
||||
Assert.assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
||||
@@ -63,6 +71,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
|
||||
*/
|
||||
@Test
|
||||
public void testformatElapsedTimeWithHour() {
|
||||
// 1 second
|
||||
Assert.assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
||||
@@ -84,20 +93,22 @@ public class StringUtilsTest {
|
||||
* Tests {@link StringUtils#formatDistance(android.content.Context, double,
|
||||
* boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testFormatDistance() {
|
||||
// A large number in metric
|
||||
Assert.assertEquals("5.00 km", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, true));
|
||||
Assert.assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
|
||||
// A large number in imperial
|
||||
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 5000, false));
|
||||
Assert.assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
|
||||
// A small number in metric
|
||||
Assert.assertEquals("100.00 m", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, true));
|
||||
Assert.assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
|
||||
// A small number in imperial
|
||||
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(InstrumentationRegistry.getInstrumentation().getContext(), 100, false));
|
||||
Assert.assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StringUtils#formatCData(String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testFormatCData() {
|
||||
Assert.assertEquals("<![CDATA[hello]]>", StringUtils.formatCData("hello"));
|
||||
Assert.assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
||||
@@ -106,6 +117,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime() {
|
||||
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
||||
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||
@@ -114,6 +126,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_fractional() {
|
||||
assertGetTime("2010-05-04T03:02:01.3", 2010, 5, 4, 3, 2, 1, 300);
|
||||
assertGetTime("2010-05-04T03:02:01.35", 2010, 5, 4, 3, 2, 1, 350);
|
||||
@@ -124,6 +137,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#getTime(String)} with time zone.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_timezone() {
|
||||
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
|
||||
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
|
||||
@@ -138,6 +152,7 @@ public class StringUtilsTest {
|
||||
* Tests {@link StringUtils#getTime(String)} with fractional seconds and time
|
||||
* zone.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTime_fractionalAndTimezone() {
|
||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||
assertGetTime("2010-05-04T03:02:01.47+00:00", 2010, 5, 4, 3, 2, 1, 470);
|
||||
@@ -169,6 +184,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTimeParts_postive() {
|
||||
int[] parts = StringUtils.getTimeParts(61000);
|
||||
Assert.assertEquals(1, parts[0]);
|
||||
@@ -179,6 +195,7 @@ public class StringUtilsTest {
|
||||
/**
|
||||
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
|
||||
*/
|
||||
@Test
|
||||
public void testGetTimeParts_negative() {
|
||||
int[] parts = StringUtils.getTimeParts(-61000);
|
||||
Assert.assertEquals(-1, parts[0]);
|
||||
|
||||
@@ -18,18 +18,19 @@ package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* Tests {@link TrackNameUtils}.
|
||||
*
|
||||
@@ -41,37 +42,36 @@ public class TrackNameUtilsTest {
|
||||
private static final long TRACK_ID = 1L;
|
||||
private static final long START_TIME = 1288213406000L;
|
||||
|
||||
private static final Context CONTEXT = InstrumentationRegistry.getInstrumentation().getContext();
|
||||
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
|
||||
|
||||
/**
|
||||
* Tests when the track_name_key is
|
||||
* settings_recording_track_name_date_local_value.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_local() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME),
|
||||
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||
Assert.assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when the track_name_key is
|
||||
* settings_recording_track_name_date_iso_8601_value.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_iso_8601() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
|
||||
TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)),
|
||||
TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||
Assert.assertEquals(simpleDateFormat.format(new Date(START_TIME)), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when the track_name_key is
|
||||
* settings_recording_track_name_number_value.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_number() {
|
||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||
Assert.assertEquals(
|
||||
"Track " + TRACK_ID, TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME, null));
|
||||
Assert.assertEquals("Track " + TRACK_ID, TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ limitations under the License.
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="de.dennisguse.opentracks"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="2916"
|
||||
android:versionName="v3.0.0"> <!-- For FDroid. -->
|
||||
android:versionCode="2991"
|
||||
android:versionName="v3.1.0"> <!-- For FDroid. -->
|
||||
<!-- Permission to initialize services -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -70,7 +70,8 @@ limitations under the License.
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true" />
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".Startup"
|
||||
android:allowBackup="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_logo"
|
||||
android:label="@string/app_name"
|
||||
@@ -82,7 +83,9 @@ limitations under the License.
|
||||
android:name=".AggregatedStatsActivity"
|
||||
android:label="@string/menu_aggregated_statistics" />
|
||||
<activity android:name=".DeleteActivity" />
|
||||
<activity android:name=".io.file.importer.ImportActivity">
|
||||
<activity
|
||||
android:name=".io.file.importer.ImportActivity"
|
||||
android:theme="@style/ThemeCustomTransparent">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ATTACH_DATA" />
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
@@ -118,11 +121,7 @@ limitations under the License.
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".TrackDetailActivity"
|
||||
android:parentActivityName=".TrackListActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".TrackListActivity" />
|
||||
</activity>
|
||||
android:screenOrientation="userPortrait" />
|
||||
<activity android:name=".TrackEditActivity" />
|
||||
<activity android:name=".TrackListActivity">
|
||||
<intent-filter>
|
||||
@@ -133,11 +132,10 @@ limitations under the License.
|
||||
android:name="android.app.default_searchable"
|
||||
android:value=".SearchListActivity" />
|
||||
</activity>
|
||||
<activity android:name=".io.file.exporter.SaveActivity" />
|
||||
<activity android:name=".settings.AdvancedSettingsActivity" />
|
||||
<activity android:name=".settings.ChartSettingsActivity" />
|
||||
<activity
|
||||
android:name=".io.file.exporter.ExportActivity"
|
||||
android:theme="@style/ThemeCustomTransparent" />
|
||||
<activity android:name=".settings.RecordingSettingsActivity" />
|
||||
<activity android:name=".settings.SensorSettingsActivity" />
|
||||
<activity android:name=".settings.SettingsActivity" />
|
||||
<activity android:name=".settings.StatsSettingsActivity" />
|
||||
<activity
|
||||
@@ -178,16 +176,10 @@ limitations under the License.
|
||||
</receiver>
|
||||
<!-- Providers -->
|
||||
<provider
|
||||
android:name=".content.CustomContentProvider"
|
||||
android:authorities="${applicationId}"
|
||||
android:name=".content.ShareContentProvider"
|
||||
android:authorities="${applicationId}.content"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" />
|
||||
<!-- Search suggestion provider -->
|
||||
<provider
|
||||
android:name=".content.SearchEngineProvider"
|
||||
android:authorities="${applicationId}.search"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.os.Bundle;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
@@ -39,15 +37,6 @@ public abstract class AbstractActivity extends FragmentActivity {
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
setContentView(getLayoutResId());
|
||||
|
||||
// Configure action bar must be after setContentView
|
||||
if (configureActionBarHomeAsUp()) {
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,33 +44,9 @@ public abstract class AbstractActivity extends FragmentActivity {
|
||||
*/
|
||||
protected abstract int getLayoutResId();
|
||||
|
||||
/**
|
||||
* Returns true to configure the action bar home button as the up button (go to previous activity).
|
||||
*/
|
||||
protected boolean configureActionBarHomeAsUp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
TrackIconUtils.setMenuIconColor(menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handles actionBar's home menu item (arrow top left corner) and triggers, up navigation to previous activity.
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onHomeSelected();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the home menu item is selected. E.g., setup the back stack when home is selected.
|
||||
*/
|
||||
protected void onHomeSelected() {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -29,15 +29,14 @@ import android.widget.AbsListView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An abstract class for the following common tasks across
|
||||
@@ -49,7 +48,7 @@ import java.util.ArrayList;
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public abstract class AbstractTrackActivity extends AbstractActivity implements ConfirmDeleteCaller {
|
||||
public abstract class AbstractListActivity extends AbstractActivity implements ConfirmDeleteCaller {
|
||||
|
||||
protected static final int GPS_REQUEST_CODE = 6;
|
||||
private static final int DELETE_REQUEST_CODE = 3;
|
||||
@@ -27,10 +27,6 @@ public class Constants {
|
||||
* Maximum number of waypoints that will be loaded at one time.
|
||||
*/
|
||||
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
||||
/**
|
||||
* The settings file name.
|
||||
*/
|
||||
public static final String SETTINGS_NAME = "SettingsActivity";
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* An activity to add/edit a marker.
|
||||
@@ -121,6 +120,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
} else {
|
||||
saveMarker();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -168,8 +168,7 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
waypointDescription.getText().toString(),
|
||||
null,
|
||||
null);
|
||||
TrackRecordingServiceConnectionUtils.addMarker(
|
||||
this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||
TrackRecordingServiceConnectionUtils.addMarker(this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,6 @@ import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarke
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* Activity to show a list of markers in a track.
|
||||
@@ -129,7 +128,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
long trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, -1L);
|
||||
track = trackId != -1L ? contentProviderUtils.getTrack(trackId) : null;
|
||||
@@ -173,7 +172,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
}
|
||||
};
|
||||
listView.setAdapter(resourceCursorAdapter);
|
||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
|
||||
LoaderManager.getInstance(this).initLoader(0, null, new LoaderCallbacks<Cursor>() {
|
||||
@NonNull
|
||||
@@ -232,7 +231,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
insertMarkerMenuItem = menu.findItem(R.id.marker_list_insert_marker);
|
||||
|
||||
searchMenuItem = menu.findItem(R.id.marker_list_search);
|
||||
AbstractTrackActivity.configureSearchWidget(this, searchMenuItem, null);
|
||||
AbstractListActivity.configureSearchWidget(this, searchMenuItem, null);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -284,10 +283,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
switch (itemId) {
|
||||
case R.id.list_context_menu_show_on_map:
|
||||
if (markerIds.length == 1) {
|
||||
intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
||||
.putExtra(TrackDetailActivity.EXTRA_MARKER_ID, markerIds[0]);
|
||||
//TODO Use IntentUtils.newShowOnMapIntent()
|
||||
startActivity(intent);
|
||||
IntentUtils.showCoordinateOnMap(this, contentProviderUtils.getWaypoint(markerIds[0]));
|
||||
}
|
||||
return true;
|
||||
case R.id.list_context_menu_edit:
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
@@ -38,11 +36,17 @@ import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.SearchEngine;
|
||||
import de.dennisguse.opentracks.content.SearchEngine.ScoredResult;
|
||||
import de.dennisguse.opentracks.content.SearchEngine.SearchQuery;
|
||||
import de.dennisguse.opentracks.content.SearchEngineProvider;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
@@ -57,14 +61,6 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* An activity to display a list of search results.
|
||||
@@ -73,7 +69,7 @@ import java.util.SortedSet;
|
||||
* <p>
|
||||
* TODO: allow to refine search (present search in context menu)
|
||||
*/
|
||||
public class SearchListActivity extends AbstractTrackActivity implements DeleteMarkerCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
public class SearchListActivity extends AbstractListActivity implements DeleteMarkerCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
|
||||
private static final String TAG = SearchListActivity.class.getSimpleName();
|
||||
|
||||
@@ -98,17 +94,11 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private SearchEngine searchEngine;
|
||||
private SearchRecentSuggestions searchRecentSuggestions;
|
||||
private ArrayAdapter<Map<String, Object>> arrayAdapter;
|
||||
private boolean metricUnits = true;
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
@Override
|
||||
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||
return handleContextItem(itemId, positions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
@@ -136,6 +126,11 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
// Disable select all, no action is available for multiple selection
|
||||
menu.findItem(R.id.list_context_menu_select_all).setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(int itemId, int[] positions, long[] ids) {
|
||||
return handleContextItem(itemId, positions);
|
||||
}
|
||||
};
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private final OnSharedPreferenceChangeListener
|
||||
@@ -168,10 +163,9 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, null);
|
||||
searchEngine = new SearchEngine(contentProviderUtils);
|
||||
searchRecentSuggestions = SearchEngineProvider.newHelper(this);
|
||||
|
||||
arrayAdapter = new ArrayAdapter<Map<String, Object>>(this, R.layout.list_item, R.id.list_item_name) {
|
||||
@NonNull
|
||||
@@ -224,7 +218,7 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
handleIntent(getIntent());
|
||||
}
|
||||
|
||||
@@ -322,8 +316,7 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
Log.e(TAG, "Not implemented");
|
||||
return true;
|
||||
case R.id.list_context_menu_show_on_map:
|
||||
intent = IntentUtils.newShowOnMapIntent((double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
|
||||
startActivity(intent);
|
||||
IntentUtils.showCoordinateOnMap(this, (double) item.get(MARKER_LATITUDE_FIELD), (double) item.get(MARKER_LONGITUDE_FIELD), item.get(NAME_FIELD) + "");
|
||||
return true;
|
||||
case R.id.list_context_menu_edit:
|
||||
if (markerId != null) {
|
||||
@@ -386,9 +379,6 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
arrayAdapter.addAll(displayResults);
|
||||
}
|
||||
});
|
||||
|
||||
// Save the query as a suggestion for the future
|
||||
searchRecentSuggestions.saveRecentQuery(query.textQuery, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,10 +410,7 @@ public class SearchListActivity extends AbstractTrackActivity implements DeleteM
|
||||
*/
|
||||
@Deprecated
|
||||
private void prepareMarkerForDisplay(Waypoint waypoint, Map<String, Object> resultMap) {
|
||||
/*
|
||||
* TODO: It may be more appropriate to obtain the track name as a join in
|
||||
* the retrieval phase of the search.
|
||||
*/
|
||||
//TODO: It may be more appropriate to obtain the track name as a join in the retrieval phase of the search.
|
||||
String trackName = null;
|
||||
long trackId = waypoint.getTrackId();
|
||||
if (trackId != -1L) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
public class Startup extends Application {
|
||||
|
||||
private final static String TAG = Startup.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
//Include version information into stack traces.
|
||||
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
|
||||
|
||||
//Set default values of preferences on first start.
|
||||
PreferencesUtils.resetPreferences(this, false);
|
||||
|
||||
|
||||
//In debug builds: show thread and VM warnings.
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Enabling strict mode");
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,19 +17,20 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ShareActionProvider;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TabHost.TabSpec;
|
||||
@@ -37,8 +38,6 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
@@ -53,7 +52,6 @@ import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.Choos
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.FrequencyDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.StatsFragment;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
@@ -63,17 +61,13 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* An activity to show the track detail.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TrackDetailActivity extends AbstractTrackActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
public class TrackDetailActivity extends AbstractListActivity implements ChooseActivityTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_ID = "track_id";
|
||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||
@@ -81,7 +75,6 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
|
||||
private static final String PHOTO_URI_KEY = "photo_uri_key";
|
||||
private static final String HAS_PHOTO_KEY = "has_photo_key";
|
||||
private static final String JPEG_EXTENSION = "jpeg";
|
||||
|
||||
private static final int CAMERA_REQUEST_CODE = 5;
|
||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||
@@ -103,6 +96,8 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
// Preferences
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
private boolean showOnLockScreen = PreferencesUtils.SHOW_TRACKDETAIL_WHILE_RECORDING_ON_LOCKSCREEN;
|
||||
|
||||
private final Runnable bindChangedCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -117,6 +112,7 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
WaypointCreationRequest waypointCreationRequest = new WaypointCreationRequest(WaypointType.WAYPOINT, false, null, null, null, null, photoUri.toString());
|
||||
long markerId = TrackRecordingServiceConnectionUtils.addMarker(TrackDetailActivity.this, trackRecordingServiceConnection, waypointCreationRequest);
|
||||
if (markerId != -1L) {
|
||||
//TODO: Make configurable.
|
||||
FileUtils.updateMediaScanner(TrackDetailActivity.this, photoUri);
|
||||
}
|
||||
}
|
||||
@@ -140,6 +136,11 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
recordingTrackPaused = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
}
|
||||
|
||||
if (key == null || key.equals(PreferencesUtils.getKey(TrackDetailActivity.this, R.string.trackdetail_show_on_lockscreen_while_recording_key))) {
|
||||
showOnLockScreen = PreferencesUtils.getBoolean(TrackDetailActivity.this, R.string.trackdetail_show_on_lockscreen_while_recording_key, PreferencesUtils.SHOW_TRACKDETAIL_WHILE_RECORDING_ON_LOCKSCREEN);
|
||||
setLockscreenPolicy();
|
||||
}
|
||||
|
||||
if (key == null) return;
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@@ -193,7 +194,7 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
handleIntent(getIntent());
|
||||
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
||||
trackDataHub = TrackDataHub.newInstance(this);
|
||||
@@ -222,6 +223,22 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
trackController = new TrackController(this, trackRecordingServiceConnection, false, recordListener, stopListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
setLockscreenPolicy();
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
|
||||
private void setLockscreenPolicy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(showOnLockScreen);
|
||||
} else {
|
||||
if (showOnLockScreen)
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
else getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
@@ -275,14 +292,6 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
return;
|
||||
}
|
||||
hasPhoto = resultCode == RESULT_OK;
|
||||
|
||||
if (hasPhoto) {
|
||||
//Register photo in media scanner
|
||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||
mediaScanIntent.setData(photoUri);
|
||||
this.sendBroadcast(mediaScanIntent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
@@ -292,20 +301,6 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
return R.layout.track_detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHomeSelected() {
|
||||
/*
|
||||
* TODO: Investigate
|
||||
* According to
|
||||
* http://developer.android.com/training/implementing-navigation
|
||||
* /ancestral.html, we should use NavUtils.shouldUpRecreateTask instead of
|
||||
* always creating a new back stack. However, NavUtils.shouldUpRecreateTask
|
||||
* seems to always return false.
|
||||
*/
|
||||
TaskStackBuilder.create(this).addParentStack(TrackDetailActivity.class).startActivities();
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
setIntent(intent);
|
||||
@@ -322,8 +317,7 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
|
||||
shareMenuItem = menu.findItem(R.id.track_detail_share);
|
||||
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();
|
||||
//TODO: Share the actual track when track is finished? How to get the file path or create a new file?
|
||||
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, trackId, "", TrackFileFormat.KML));
|
||||
shareActionProvider.setShareIntent(IntentUtils.newShareFileIntent(this, new long[]{trackId}));
|
||||
|
||||
voiceFrequencyMenuItem = menu.findItem(R.id.track_detail_voice_frequency);
|
||||
splitFrequencyMenuItem = menu.findItem(R.id.track_detail_split_frequency);
|
||||
@@ -353,7 +347,10 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||
return false;
|
||||
}
|
||||
takePicture();
|
||||
createWaypointWithPicture();
|
||||
return true;
|
||||
case R.id.track_detail_menu_show_on_map:
|
||||
IntentUtils.showTrackOnMap(this, new long[]{trackId});
|
||||
return true;
|
||||
case R.id.track_detail_markers:
|
||||
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
|
||||
@@ -385,29 +382,13 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
}
|
||||
}
|
||||
|
||||
private void takePicture() {
|
||||
File dir = FileUtils.getPhotoDir(trackId);
|
||||
FileUtils.ensureDirectoryExists(dir);
|
||||
|
||||
String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date());
|
||||
File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION));
|
||||
|
||||
if (file != null) {
|
||||
photoUri = FileProvider.getUriForFile(this, FileUtils.FILEPROVIDER, file);
|
||||
Log.d(TAG, "Taking photo to URI: " + photoUri);
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
|
||||
startActivityForResult(intent, CAMERA_REQUEST_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
takePicture();
|
||||
createWaypointWithPicture();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -496,6 +477,12 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
private void createWaypointWithPicture() {
|
||||
Pair<Intent, Uri> intentAndPhotoUri = IntentUtils.createTakePictureIntent(this, trackId);
|
||||
photoUri = intentAndPhotoUri.second;
|
||||
startActivityForResult(intentAndPhotoUri.first, CAMERA_REQUEST_CODE);
|
||||
}
|
||||
|
||||
public void chooseActivityType(String category) {
|
||||
ChooseActivityTypeDialogFragment.newInstance(category).show(getSupportFragmentManager(), ChooseActivityTypeDialogFragment.CHOOSE_ACTIVITY_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.StrictMode;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
@@ -43,6 +41,7 @@ import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.ShareActionProvider;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -58,11 +57,6 @@ import java.util.Locale;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.TracksColumns;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment.FileTypeCaller;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.SaveActivity;
|
||||
import de.dennisguse.opentracks.io.file.importer.ImportActivity;
|
||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
@@ -79,7 +73,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class TrackListActivity extends AbstractTrackActivity implements FileTypeCaller, ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
|
||||
|
||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||
private static final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
||||
@@ -113,16 +107,16 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
private boolean metricUnits = true;
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private final ContextualActionModeCallback
|
||||
contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
|
||||
@Override
|
||||
public void onPrepare(Menu menu, int[] positions, long[] ids, boolean showSelectAll) {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isSingleSelection = ids.length == 1;
|
||||
public void onPrepare(Menu menu, int[] positions, long[] trackIds, boolean showSelectAll) {
|
||||
boolean isSingleSelection = trackIds.length == 1;
|
||||
|
||||
MenuItem shareMenuItem = menu.findItem(R.id.list_context_menu_share);
|
||||
ShareActionProvider shareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();
|
||||
shareActionProvider.setShareIntent(trackIds.length == 0 ? null : IntentUtils.newShareFileIntent(TrackListActivity.this, trackIds));
|
||||
|
||||
menu.findItem(R.id.list_context_menu_share).setVisible(!isRecording && isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_show_on_map).setVisible(false);
|
||||
menu.findItem(R.id.list_context_menu_edit).setVisible(isSingleSelection);
|
||||
menu.findItem(R.id.list_context_menu_select_all).setVisible(showSelectAll);
|
||||
}
|
||||
@@ -134,7 +128,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
};
|
||||
private boolean recordingTrackPaused = PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT;
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||
* Note that sharedPreferenceChangeListener cannot be an anonymous inner class.
|
||||
* Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
@@ -175,8 +169,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
private MenuItem searchMenuItem;
|
||||
private MenuItem startGpsMenuItem;
|
||||
private MenuItem aggregatedStatisticsMenuItem;
|
||||
private MenuItem exportAllMenuItem;
|
||||
private MenuItem importAllMenuItem;
|
||||
private MenuItem deleteAllMenuItem;
|
||||
private final OnClickListener stopListener = new OnClickListener() {
|
||||
@Override
|
||||
@@ -256,20 +248,8 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(TAG, "Enabling strict mode");
|
||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectAll()
|
||||
.penaltyLog()
|
||||
.build());
|
||||
}
|
||||
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(this);
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(this, bindChangedCallback);
|
||||
trackController = new TrackController(this, trackRecordingServiceConnection, true, recordListener, stopListener);
|
||||
@@ -324,7 +304,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
}
|
||||
};
|
||||
listView.setAdapter(resourceCursorAdapter);
|
||||
AbstractTrackActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
AbstractListActivity.configureListViewContextualMenu(listView, contextualActionModeCallback);
|
||||
|
||||
LoaderManager.getInstance(this).initLoader(0, null, loaderCallbacks);
|
||||
showStartupDialogs();
|
||||
@@ -382,22 +362,15 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
return R.layout.track_list;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean configureActionBarHomeAsUp() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.track_list, menu);
|
||||
|
||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||
AbstractTrackActivity.configureSearchWidget(this, searchMenuItem, trackController);
|
||||
AbstractListActivity.configureSearchWidget(this, searchMenuItem, trackController);
|
||||
|
||||
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
|
||||
aggregatedStatisticsMenuItem = menu.findItem(R.id.track_list_aggregated_statistics);
|
||||
exportAllMenuItem = menu.findItem(R.id.track_list_export_all);
|
||||
importAllMenuItem = menu.findItem(R.id.track_list_import_all);
|
||||
deleteAllMenuItem = menu.findItem(R.id.track_list_delete_all);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
@@ -457,16 +430,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
intent = IntentUtils.newIntent(this, AggregatedStatsActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
case R.id.track_list_export_all:
|
||||
FileTypeDialogFragment.newInstance(R.id.track_list_export_all,
|
||||
R.string.export_all_title, R.string.export_all_option)
|
||||
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.track_list_import_all:
|
||||
FileTypeDialogFragment.newInstance(R.id.track_list_import_all,
|
||||
R.string.import_selection_title, R.string.import_selection_option)
|
||||
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
|
||||
return true;
|
||||
case R.id.track_list_delete_all:
|
||||
deleteTracks(new long[]{-1L});
|
||||
return true;
|
||||
@@ -484,8 +447,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
contextualActionModeCallback.onPrepare(
|
||||
menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||
contextualActionModeCallback.onPrepare(menu, new int[]{info.position}, new long[]{info.id}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -522,26 +484,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat) {
|
||||
Intent intent;
|
||||
switch (menuId) {
|
||||
case R.id.track_list_export_all:
|
||||
intent = IntentUtils.newIntent(this, SaveActivity.class)
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_IDS, new long[]{-1L})
|
||||
.putExtra(SaveActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
|
||||
startActivity(intent);
|
||||
break;
|
||||
case R.id.track_list_import_all:
|
||||
intent = IntentUtils.newIntent(this, ImportActivity.class)
|
||||
.putExtra(ImportActivity.EXTRA_IMPORT_ALL, true)
|
||||
.putExtra(ImportActivity.EXTRA_TRACK_FILE_FORMAT, (Parcelable) trackFileFormat);
|
||||
startActivity(intent);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows start up dialogs.
|
||||
*/
|
||||
@@ -580,12 +522,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
if (aggregatedStatisticsMenuItem != null) {
|
||||
aggregatedStatisticsMenuItem.setVisible(hasTrack);
|
||||
}
|
||||
if (exportAllMenuItem != null) {
|
||||
exportAllMenuItem.setVisible(hasTrack && !isRecording);
|
||||
}
|
||||
if (importAllMenuItem != null) {
|
||||
importAllMenuItem.setVisible(!isRecording);
|
||||
}
|
||||
if (deleteAllMenuItem != null) {
|
||||
deleteAllMenuItem.setVisible(hasTrack && !isRecording);
|
||||
}
|
||||
@@ -616,9 +552,8 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
|
||||
*/
|
||||
private boolean handleContextItem(int itemId, long[] trackIds) {
|
||||
switch (itemId) {
|
||||
case R.id.list_context_menu_share:
|
||||
//TODO
|
||||
Log.e(TAG, "Not implemented");
|
||||
case R.id.list_context_menu_show_on_map:
|
||||
IntentUtils.showTrackOnMap(this, trackIds);
|
||||
return true;
|
||||
case R.id.list_context_menu_edit:
|
||||
Intent intent = IntentUtils.newIntent(this, TrackEditActivity.class)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.dennisguse.opentracks.android;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ContentResolverWrapper implements IContentResolver {
|
||||
|
||||
private ContentResolver contentResolver;
|
||||
|
||||
public ContentResolverWrapper(ContentResolver contentResolver) {
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
|
||||
return contentResolver.insert(url, values);
|
||||
}
|
||||
|
||||
public int bulkInsert(@NonNull Uri url, @NonNull ContentValues[] values) {
|
||||
return contentResolver.bulkInsert(url, values);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||
return contentResolver.query(uri, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs) {
|
||||
return contentResolver.update(uri, values, where, selectionArgs);
|
||||
}
|
||||
|
||||
public int delete(@NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs) {
|
||||
return contentResolver.delete(url, where, selectionArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.dennisguse.opentracks.android;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresPermission;
|
||||
|
||||
/**
|
||||
* Defines the interface actually shared by {@link android.content.ContentProvider} and {@link android.content.ContentResolver}.
|
||||
* So, both can be used interchangeably.
|
||||
*/
|
||||
public interface IContentResolver {
|
||||
@Nullable
|
||||
Uri insert(@RequiresPermission.Write @NonNull Uri url, @Nullable ContentValues values);
|
||||
|
||||
int bulkInsert(@RequiresPermission.Write @NonNull Uri url, @NonNull ContentValues[] values);
|
||||
|
||||
@Nullable
|
||||
Cursor query(@RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder);
|
||||
|
||||
int update(@RequiresPermission.Write @NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs);
|
||||
|
||||
int delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs);
|
||||
}
|
||||
@@ -22,12 +22,12 @@ import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
|
||||
/**
|
||||
* Utilities to access data from the app's content provider.
|
||||
*
|
||||
@@ -36,10 +36,15 @@ import java.util.List;
|
||||
public interface ContentProviderUtils {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
String AUTHORITY = BuildConfig.APPLICATION_ID;
|
||||
String AUTHORITY_PACKAGE = BuildConfig.APPLICATION_ID + ".content";
|
||||
|
||||
/**
|
||||
* The base URI for the app's content provider.
|
||||
*/
|
||||
String CONTENT_BASE_URI = "content://" + AUTHORITY_PACKAGE;
|
||||
|
||||
/**
|
||||
* The default {@link LocationFactory} which creates a location each time.
|
||||
*/
|
||||
@@ -354,8 +359,7 @@ public interface ContentProviderUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory which can produce instances of {@link ContentProviderUtils}, and
|
||||
* can be overridden for testing.
|
||||
* A factory which can produce instances of {@link ContentProviderUtils}, and can be overridden for testing.
|
||||
*/
|
||||
class Factory {
|
||||
|
||||
|
||||
@@ -24,18 +24,21 @@ import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import de.dennisguse.opentracks.android.ContentResolverWrapper;
|
||||
import de.dennisguse.opentracks.android.IContentResolver;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
/**
|
||||
* {@link ContentProviderUtils} implementation.
|
||||
* Allows to use {@link ContentResolver} and {@link android.content.ContentProvider} interchangeably via {@link IContentResolver}.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
@@ -45,10 +48,14 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
|
||||
|
||||
private static final int MAX_LATITUDE = 90000000;
|
||||
|
||||
private final ContentResolver contentResolver;
|
||||
private final IContentResolver contentResolver;
|
||||
private int defaultCursorBatchSize = 2000;
|
||||
|
||||
public ContentProviderUtilsImpl(ContentResolver contentResolver) {
|
||||
this.contentResolver = new ContentResolverWrapper(contentResolver);
|
||||
}
|
||||
|
||||
public ContentProviderUtilsImpl(IContentResolver contentResolver) {
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
@@ -716,8 +723,7 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
|
||||
if (maxWaypoints >= 0) {
|
||||
sortOrder += " LIMIT " + maxWaypoints;
|
||||
}
|
||||
return contentResolver.query(
|
||||
WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
|
||||
return contentResolver.query(WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,35 +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;
|
||||
|
||||
/**
|
||||
* Utilities for serializing primitive types.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ContentTypeIds {
|
||||
|
||||
public static final byte BOOLEAN_TYPE_ID = 0;
|
||||
public static final byte LONG_TYPE_ID = 1;
|
||||
public static final byte INT_TYPE_ID = 2;
|
||||
public static final byte FLOAT_TYPE_ID = 3;
|
||||
public static final byte DOUBLE_TYPE_ID = 4;
|
||||
public static final byte STRING_TYPE_ID = 5;
|
||||
public static final byte BLOB_TYPE_ID = 6;
|
||||
private ContentTypeIds() {
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
@@ -29,25 +28,19 @@ import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* A {@link ContentProvider} that handles access to track points, tracks, and
|
||||
* waypoints tables.
|
||||
*
|
||||
* @author Leif Hendrik Wilden
|
||||
*/
|
||||
public class CustomContentProvider extends ContentProvider {
|
||||
public abstract class CustomContentProvider extends ContentProvider {
|
||||
|
||||
@VisibleForTesting
|
||||
static final int DATABASE_VERSION = 23;
|
||||
@@ -59,12 +52,12 @@ public class CustomContentProvider extends ContentProvider {
|
||||
|
||||
public CustomContentProvider() {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME, UrlType.TRACKS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, TracksColumns.TABLE_NAME + "/#", UrlType.TRACKS_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME, UrlType.WAYPOINTS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_ID.ordinal());
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME, UrlType.TRACKS.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, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_ID.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -277,23 +270,6 @@ public class CustomContentProvider extends ContentProvider {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
try {
|
||||
File file = new File(uri.getPath());
|
||||
if (file.exists()) {
|
||||
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||
}
|
||||
throw new FileNotFoundException(uri.getPath());
|
||||
} finally {
|
||||
File dir = new File(getContext().getCacheDir(), FileUtils.PLAY_TRACKS_DIR);
|
||||
for (File file : dir.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
getContext().revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link UrlType} for a url.
|
||||
*
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Data source on the phone.
|
||||
@@ -37,7 +37,7 @@ public class DataSource {
|
||||
|
||||
public DataSource(Context context) {
|
||||
contentResolver = context.getContentResolver();
|
||||
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,8 +17,6 @@ package de.dennisguse.opentracks.content;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* An interface for an object that can generate descriptions of track and
|
||||
* waypoint.
|
||||
@@ -31,12 +29,9 @@ public interface DescriptionGenerator {
|
||||
* Generates a track description.
|
||||
*
|
||||
* @param track the track
|
||||
* @param distances a vector of distances to generate the elevation chart
|
||||
* @param elevations a vector of elevations to generate the elevation chart
|
||||
* @param html true to output html, false to output plain text
|
||||
*/
|
||||
String generateTrackDescription(
|
||||
Track track, Vector<Double> distances, Vector<Double> elevations, boolean html);
|
||||
String generateTrackDescription(Track track, boolean html);
|
||||
|
||||
/**
|
||||
* Generate a waypoint description from a trip statistics.
|
||||
|
||||
@@ -20,12 +20,10 @@ import android.content.Context;
|
||||
|
||||
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;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* @author Jimmy Shih
|
||||
@@ -44,14 +42,14 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track track, Vector<Double> distances, Vector<Double> elevations, 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='http://" + context.getString(R.string.app_web_url) + "'>" + context.getString(R.string.app_name) + "</a>"
|
||||
? "<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);
|
||||
|
||||
|
||||
@@ -20,10 +20,6 @@ import android.location.Location;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -31,6 +27,10 @@ import java.util.Locale;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
/**
|
||||
* Engine for searching for tracks and waypoints by text.
|
||||
*
|
||||
@@ -381,6 +381,7 @@ public class SearchEngine {
|
||||
public final Location currentLocation;
|
||||
public final long currentTrackId;
|
||||
public final long currentTimestamp;
|
||||
|
||||
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
|
||||
long currentTimestamp) {
|
||||
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
|
||||
@@ -397,11 +398,13 @@ public class SearchEngine {
|
||||
public final Track track;
|
||||
public final Waypoint waypoint;
|
||||
public final double score;
|
||||
|
||||
ScoredResult(Track track, double score) {
|
||||
this.track = track;
|
||||
this.waypoint = null;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
ScoredResult(Waypoint waypoint, double score) {
|
||||
this.track = null;
|
||||
this.waypoint = waypoint;
|
||||
|
||||
@@ -1,46 +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.content.SearchRecentSuggestionsProvider;
|
||||
import android.provider.SearchRecentSuggestions;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
|
||||
/**
|
||||
* Content provider for search suggestions.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SearchEngineProvider extends SearchRecentSuggestionsProvider {
|
||||
|
||||
private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".search";
|
||||
private static final int MODE = DATABASE_MODE_QUERIES;
|
||||
|
||||
public SearchEngineProvider() {
|
||||
setupSuggestions(AUTHORITY, MODE);
|
||||
}
|
||||
|
||||
// TODO: Also add suggestions from the database.
|
||||
|
||||
/**
|
||||
* Creates and returns a helper for adding recent queries or clearing the recent query history.
|
||||
*/
|
||||
public static SearchRecentSuggestions newHelper(Context context) {
|
||||
return new SearchRecentSuggestions(context, AUTHORITY, MODE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package de.dennisguse.opentracks.content;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.dennisguse.opentracks.android.IContentResolver;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
|
||||
/**
|
||||
* A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) KML-files.
|
||||
* The actual content of the virtual files is generated by using the functionality defined in {@link CustomContentProvider}.
|
||||
* <p>
|
||||
* Moreover, it manages access to OpenTrack's database via {@link CustomContentProvider}.
|
||||
* <p>
|
||||
* Explanation:
|
||||
* Although a request is handled by a {@link android.content.ContentProvider} (with temporarily granted permission), Android's security infrastructure prevents forwarding queries to non-exported {@link android.content.ContentProvider}.
|
||||
* Thus, if {@link ShareContentProvider} and {@link CustomContentProvider} would be two different instances, the data would not be accessible to external apps.
|
||||
* While handling a request {@link ShareContentProvider} could `grantPermissions()` to the calling app for {@link CustomContentProvider}'s URI.
|
||||
* However, while handling the request this would allow the calling app to actually contact {@link CustomContentProvider} directly and get access to stored data that should remain private.
|
||||
*/
|
||||
public class ShareContentProvider extends CustomContentProvider implements IContentResolver {
|
||||
|
||||
private static final String[] COLUMNS = {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
|
||||
|
||||
public static final String TAG = ShareContentProvider.class.getSimpleName();
|
||||
|
||||
private static final int URI_GPX = 0;
|
||||
private static final int URI_KML_ONLY = 1;
|
||||
private static final int URI_KML_WITH_SENSORDATA = 2;
|
||||
private static final int URI_KMZ_ONLY_TRACK = 3;
|
||||
private static final int URI_KMZ_WITH_SENSORDATA = 4;
|
||||
private static final int URI_KMZ_WITH_SENSORDATA_AND_PICTURES = 5;
|
||||
|
||||
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
private static final String TRACKID_DELIMITER = "_";
|
||||
|
||||
static {
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KML_ONLY_TRACK.getName() + "/*", URI_KML_ONLY);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KML_WITH_SENSORDATA.getName() + "/*", URI_KML_WITH_SENSORDATA);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_ONLY_TRACK.getName() + "/*", URI_KMZ_ONLY_TRACK);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_WITH_SENSORDATA.getName() + "/*", URI_KMZ_WITH_SENSORDATA);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.KMZ_WITH_SENSORDATA_AND_PICTURES.getName() + "/*", URI_KMZ_WITH_SENSORDATA_AND_PICTURES);
|
||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/" + TrackFileFormat.GPX.getName() + "/*", URI_GPX);
|
||||
}
|
||||
|
||||
public static Pair<Uri, String> createURI(long[] trackIds, @NonNull TrackFileFormat trackFileFormat) {
|
||||
if (trackIds.length == 0) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (long trackId : trackIds) {
|
||||
builder.append(trackId).append(TRACKID_DELIMITER);
|
||||
}
|
||||
builder.deleteCharAt(builder.lastIndexOf(TRACKID_DELIMITER));
|
||||
|
||||
Uri uri = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TracksColumns.TABLE_NAME + "/" + trackFileFormat.getName() + "/" + builder + "." + trackFileFormat.getExtension());
|
||||
String mime = getTypeMime(uri);
|
||||
|
||||
Log.d(TAG, "Created uri " + uri.toString() + " with MIME " + mime);
|
||||
|
||||
return new Pair<>(uri, mime);
|
||||
}
|
||||
|
||||
private static long[] parseURI(Uri uri) {
|
||||
String lastPathSegment = uri.getLastPathSegment();
|
||||
if (lastPathSegment == null) {
|
||||
return new long[]{};
|
||||
}
|
||||
|
||||
String fileExtension = "." + getTrackFileFormat(uri).getExtension();
|
||||
String[] lastPathSegmentSplit = lastPathSegment.replace(fileExtension, "").split(TRACKID_DELIMITER);
|
||||
|
||||
long[] trackIds = new long[lastPathSegmentSplit.length];
|
||||
for (int i = 0; i < trackIds.length; i++) {
|
||||
trackIds[i] = Long.valueOf(lastPathSegmentSplit[i]);
|
||||
}
|
||||
return trackIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not allow to be exported via AndroidManifest.
|
||||
* Check that caller has permissions to access {@link CustomContentProvider}.
|
||||
*/
|
||||
@Override
|
||||
public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {
|
||||
super.attachInfo(context, info);
|
||||
|
||||
// Sanity check our security
|
||||
if (info.exported) {
|
||||
throw new UnsupportedOperationException("Provider must not be exported");
|
||||
}
|
||||
|
||||
if (!info.grantUriPermissions) {
|
||||
throw new SecurityException("Provider must grant uri permissions");
|
||||
}
|
||||
}
|
||||
|
||||
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case URI_GPX:
|
||||
return TrackFileFormat.GPX;
|
||||
case URI_KML_ONLY:
|
||||
return TrackFileFormat.KML_ONLY_TRACK;
|
||||
case URI_KML_WITH_SENSORDATA:
|
||||
return TrackFileFormat.KML_WITH_SENSORDATA;
|
||||
case URI_KMZ_ONLY_TRACK:
|
||||
return TrackFileFormat.KMZ_ONLY_TRACK;
|
||||
case URI_KMZ_WITH_SENSORDATA:
|
||||
return TrackFileFormat.KMZ_WITH_SENSORDATA;
|
||||
case URI_KMZ_WITH_SENSORDATA_AND_PICTURES:
|
||||
return TrackFileFormat.KMZ_WITH_SENSORDATA_AND_PICTURES;
|
||||
default: //URI_KML_ONLY
|
||||
return TrackFileFormat.KMZ_ONLY_TRACK;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getTypeMime(@NonNull Uri uri) {
|
||||
return getTrackFileFormat(uri).getMimeType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
|
||||
if (uriMatcher.match(uri) == -1) {
|
||||
return super.query(uri, projection, selection, selectionArgs, sortOrder);
|
||||
}
|
||||
|
||||
// ContentProvider has already checked granted permissions
|
||||
if (projection == null) {
|
||||
projection = COLUMNS;
|
||||
}
|
||||
|
||||
String[] cols = new String[projection.length];
|
||||
Object[] values = new Object[projection.length];
|
||||
int i = 0;
|
||||
for (String col : projection) {
|
||||
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
|
||||
cols[i] = OpenableColumns.DISPLAY_NAME;
|
||||
values[i++] = uri.getLastPathSegment();
|
||||
} else if (OpenableColumns.SIZE.equals(col)) {
|
||||
cols[i] = OpenableColumns.SIZE;
|
||||
values[i++] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
cols = Arrays.copyOf(cols, i);
|
||||
values = Arrays.copyOf(values, i);
|
||||
|
||||
final MatrixCursor cursor = new MatrixCursor(cols, 1);
|
||||
cursor.addRow(values);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
String mime = getTypeMime(uri);
|
||||
if (mime != null) {
|
||||
return mime;
|
||||
}
|
||||
|
||||
return super.getType(uri);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtilsImpl(this);
|
||||
|
||||
long[] trackIds = parseURI(uri);
|
||||
final Track[] tracks = new Track[trackIds.length];
|
||||
for (int i = 0; i < trackIds.length; i++) {
|
||||
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
||||
}
|
||||
|
||||
final TrackExporter trackExporter = getTrackFileFormat(uri).newTrackExporter(getContext(), tracks, null);
|
||||
|
||||
PipeDataWriter pipeDataWriter = new PipeDataWriter<String>() {
|
||||
@Override
|
||||
public void writeDataToPipe(@NonNull ParcelFileDescriptor output, @NonNull Uri uri, @NonNull String mimeType, @Nullable Bundle opts, @Nullable String args) {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||
trackExporter.writeTrack(fileOutputStream);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return openPipeHelper(uri, getType(uri), null, null, pipeDataWriter);
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,15 @@ import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Track data hub. Receives data from {@link DataSource} and distributes it to
|
||||
* {@link TrackDataListener} after some processing.
|
||||
@@ -120,7 +120,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
dataSourceManager = new DataSourceManager(dataSource, this);
|
||||
|
||||
notifyPreferenceChanged(null);
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (dataSourceManager != null) {
|
||||
@@ -157,7 +157,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public void loadTrack(final long trackId) {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (trackId == selectedTrackId) {
|
||||
@@ -177,7 +177,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
* @param trackDataTypes the track data types
|
||||
*/
|
||||
public void registerTrackDataListener(final TrackDataListener trackDataListener, final EnumSet<TrackDataType> trackDataTypes) {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
trackDataManager.registerListener(trackDataListener, trackDataTypes);
|
||||
@@ -195,7 +195,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
* @param trackDataListener the track data listener
|
||||
*/
|
||||
public void unregisterTrackDataListener(final TrackDataListener trackDataListener) {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
trackDataManager.unregisterListener(trackDataListener);
|
||||
@@ -210,7 +210,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
* Reloads data for a {@link TrackDataListener}.
|
||||
*/
|
||||
public void reloadDataForListener(final TrackDataListener trackDataListener) {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadDataForListener(trackDataListener);
|
||||
@@ -235,7 +235,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
|
||||
@Override
|
||||
public void notifyTracksTableUpdated() {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyTracksTableUpdate(trackDataManager.getListeners(TrackDataType.TRACKS_TABLE));
|
||||
@@ -245,7 +245,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
|
||||
@Override
|
||||
public void notifyWaypointsTableUpdated() {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyWaypointsTableUpdate(trackDataManager.getListeners(TrackDataType.WAYPOINTS_TABLE));
|
||||
@@ -255,7 +255,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
|
||||
@Override
|
||||
public void notifyTrackPointsTableUpdated() {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyTrackPointsTableUpdate(
|
||||
@@ -267,7 +267,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
|
||||
@Override
|
||||
public void notifyPreferenceChanged(final String key) {
|
||||
runInHanderThread(new Runnable() {
|
||||
runInHandlerThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (key == null
|
||||
@@ -565,7 +565,7 @@ public class TrackDataHub implements DataSourceListener {
|
||||
* @param runnable the runnable
|
||||
*/
|
||||
@VisibleForTesting
|
||||
protected void runInHanderThread(Runnable runnable) {
|
||||
protected void runInHandlerThread(Runnable runnable) {
|
||||
if (handler == null) {
|
||||
// Use a Throwable to ensure the stack trace is logged.
|
||||
Log.d(TAG, "handler is null.", new Throwable());
|
||||
|
||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
|
||||
/**
|
||||
* Constants for the track points table.
|
||||
*
|
||||
@@ -29,7 +27,7 @@ import de.dennisguse.opentracks.BuildConfig;
|
||||
public interface TrackPointsColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "trackpoints";
|
||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/trackpoints");
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/trackpoints");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
|
||||
/**
|
||||
* Constants for the tracks table.
|
||||
*
|
||||
@@ -33,7 +31,7 @@ public interface TracksColumns extends BaseColumns {
|
||||
/**
|
||||
* Tracks provider uri.
|
||||
*/
|
||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/tracks");
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/tracks");
|
||||
|
||||
/**
|
||||
* Track content type.
|
||||
|
||||
@@ -19,8 +19,6 @@ package de.dennisguse.opentracks.content;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
|
||||
/**
|
||||
* Constants for waypoints table.
|
||||
*
|
||||
@@ -29,7 +27,7 @@ import de.dennisguse.opentracks.BuildConfig;
|
||||
public interface WaypointsColumns extends BaseColumns {
|
||||
|
||||
String TABLE_NAME = "waypoints";
|
||||
Uri CONTENT_URI = Uri.parse("content://" + BuildConfig.APPLICATION_ID + "/waypoints");
|
||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/waypoints");
|
||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.waypoint";
|
||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.waypoint";
|
||||
String DEFAULT_SORT_ORDER = "_id";
|
||||
|
||||
+4
-9
@@ -32,13 +32,13 @@ import android.widget.GridView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
* A DialogFragment to choose an activity type.
|
||||
*
|
||||
@@ -119,11 +119,6 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment {
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void updateWeightContainer(View weightContainer, int position) {
|
||||
boolean showWeight = position == 0 || position == 1 || position == 2;
|
||||
weightContainer.setVisibility(showWeight ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
|
||||
+3
-4
@@ -23,10 +23,10 @@ import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* Image adapter for choosing an activity type.
|
||||
*
|
||||
@@ -41,8 +41,7 @@ public class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
private final int padding;
|
||||
private int selected = -1;
|
||||
|
||||
public ChooseActivityTypeImageAdapter(
|
||||
Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
public ChooseActivityTypeImageAdapter(Context context, List<Integer> imageIds, int width, int height, int padding) {
|
||||
this.context = context;
|
||||
this.imageIds = imageIds;
|
||||
this.width = width;
|
||||
|
||||
@@ -23,9 +23,9 @@ import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.os.Bundle;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* A DialogFragment to select a file type, gpx, kml, etc.
|
||||
@@ -35,14 +35,12 @@ import de.dennisguse.opentracks.R;
|
||||
public class FileTypeDialogFragment extends AbstractDialogFragment {
|
||||
|
||||
public static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||
private static final String KEY_MENU_ID = "menuId";
|
||||
private static final String KEY_TITLE_ID = "titleId";
|
||||
private static final String KEY_OPTION_ID = "optionId";
|
||||
private FileTypeCaller caller;
|
||||
|
||||
public static FileTypeDialogFragment newInstance(int menuId, int titleId, int optionId) {
|
||||
public static FileTypeDialogFragment newInstance(int titleId, int optionId) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(KEY_MENU_ID, menuId);
|
||||
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||
bundle.putInt(KEY_OPTION_ID, optionId);
|
||||
|
||||
@@ -65,22 +63,39 @@ public class FileTypeDialogFragment extends AbstractDialogFragment {
|
||||
protected Dialog createDialog() {
|
||||
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
||||
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||
final int menuId = getArguments().getInt(KEY_MENU_ID);
|
||||
TrackFileFormat[] trackFileFormats = TrackFileFormat.values();
|
||||
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KML_WITH_SENSORDATA, TrackFileFormat.KMZ_WITH_SENSORDATA_AND_PICTURES, TrackFileFormat.GPX};
|
||||
String[] choices = new String[trackFileFormats.length];
|
||||
for (int i = 0; i < choices.length; i++) {
|
||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||
choices[i] = getString(optionId, trackFileFormat.name(),
|
||||
FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
|
||||
choices[i] = getString(optionId, trackFileFormat.getExtension().toUpperCase(), FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
|
||||
}
|
||||
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setNegativeButton(R.string.generic_cancel, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
onDismissed();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(R.string.generic_ok, new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||
caller.onFileTypeDone(menuId, TrackFileFormat.values()[position]);
|
||||
caller.onFileTypeDone(trackFileFormats[position]);
|
||||
}
|
||||
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
|
||||
})
|
||||
.setSingleChoiceItems(choices, 0, null)
|
||||
.setTitle(titleId)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
onDismissed();
|
||||
}
|
||||
|
||||
protected void onDismissed() {
|
||||
dismiss();
|
||||
caller.onDismissed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +108,8 @@ public class FileTypeDialogFragment extends AbstractDialogFragment {
|
||||
/**
|
||||
* Called when file type selection is done.
|
||||
*/
|
||||
void onFileTypeDone(int menuId, TrackFileFormat trackFileFormat);
|
||||
void onFileTypeDone(TrackFileFormat trackFileFormat);
|
||||
|
||||
void onDismissed();
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import de.dennisguse.opentracks.MarkerEditActivity;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
@@ -48,7 +49,6 @@ import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.StatsUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* A fragment to show marker details.
|
||||
@@ -190,8 +190,7 @@ public class MarkerDetailFragment extends Fragment {
|
||||
Intent intent;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.marker_detail_show_on_map:
|
||||
intent = IntentUtils.newShowOnMapIntent(waypoint);
|
||||
startActivity(intent);
|
||||
IntentUtils.showCoordinateOnMap(getContext(), waypoint);
|
||||
return true;
|
||||
case R.id.marker_detail_edit:
|
||||
intent = IntentUtils.newIntent(fragmentActivity, MarkerEditActivity.class)
|
||||
|
||||
@@ -31,6 +31,9 @@ import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||
@@ -40,6 +43,7 @@ import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.ITrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManager;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
@@ -47,9 +51,6 @@ import de.dennisguse.opentracks.util.StatsUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackRecordingServiceConnectionUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user.
|
||||
@@ -297,7 +298,7 @@ public class StatsFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to fetch most recent {@link SensorDataSet} {@link de.dennisguse.opentracks.services.sensors.SensorManager}.
|
||||
* Tries to fetch most recent {@link SensorDataSet} {@link RemoteSensorManager}.
|
||||
*/
|
||||
private void updateSensorDataUI() {
|
||||
ITrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
|
||||
@@ -4,30 +4,124 @@ import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.io.file.exporter.FileTrackExporter;
|
||||
import de.dennisguse.opentracks.io.file.exporter.GpxTrackWriter;
|
||||
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
|
||||
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporterListener;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackWriter;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Definition of all possible track formats.
|
||||
*/
|
||||
public enum TrackFileFormat implements Parcelable {
|
||||
KML {
|
||||
|
||||
KML_ONLY_TRACK {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
return new KmlTrackWriter(context, multiple, playTrack);
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new KmlTrackWriter(context, multiple, false, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return MIME_KML;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "kml";
|
||||
}
|
||||
},
|
||||
KML_WITH_SENSORDATA {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new KmlTrackWriter(context, multiple, true, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return MIME_KML;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "kml";
|
||||
}
|
||||
},
|
||||
KMZ_ONLY_TRACK {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new KmlTrackWriter(context, multiple, false, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return MIME_KMZ;
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "kmz";
|
||||
}
|
||||
},
|
||||
KMZ_WITH_SENSORDATA {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new KmlTrackWriter(context, multiple, true, true, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return MIME_KMZ;
|
||||
}
|
||||
|
||||
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
|
||||
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener);
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "kmz";
|
||||
}
|
||||
},
|
||||
KMZ_WITH_SENSORDATA_AND_PICTURES {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new KmlTrackWriter(context, multiple, true, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return MIME_KMZ;
|
||||
}
|
||||
|
||||
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
|
||||
return newKmzTrackExporter(context, this.newTrackWriter(context, tracks.length > 1), tracks, trackExporterListener);
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "kmz";
|
||||
}
|
||||
},
|
||||
GPX {
|
||||
@Override
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
public TrackWriter newTrackWriter(Context context, boolean multiple) {
|
||||
return new GpxTrackWriter(context.getString(R.string.app_name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return "application/gpx+xml";
|
||||
}
|
||||
|
||||
public String getExtension() {
|
||||
return "gpx";
|
||||
}
|
||||
};
|
||||
|
||||
@Deprecated
|
||||
public static final Creator<TrackFileFormat> CREATOR = new Creator<TrackFileFormat>() {
|
||||
@Override
|
||||
public TrackFileFormat createFromParcel(final Parcel source) {
|
||||
@@ -50,26 +144,46 @@ public enum TrackFileFormat implements Parcelable {
|
||||
dest.writeInt(ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new track writer for the format.
|
||||
*
|
||||
* @param context the context
|
||||
* @param multiple true for writing multiple tracks
|
||||
* @param playTrack true to play track
|
||||
*/
|
||||
public abstract TrackWriter newTrackWriter(Context context, boolean multiple, boolean playTrack);
|
||||
private static final String MIME_KMZ = "application/vnd.google-earth.kmz";
|
||||
|
||||
private static final String MIME_KML = "application/vnd.google-earth.kml+xml";
|
||||
|
||||
public TrackExporter newTrackExporter(Context context, Track[] tracks, TrackExporterListener trackExporterListener) {
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
TrackWriter trackWriter = this.newTrackWriter(context, tracks.length > 1);
|
||||
return new FileTrackExporter(contentProviderUtils, trackWriter, tracks, trackExporterListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mime type for each format.
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return "application/" + getExtension() + "+xml";
|
||||
public abstract String getMimeType();
|
||||
|
||||
private static TrackExporter newKmzTrackExporter(Context context, TrackWriter trackWriter, Track[] tracks, TrackExporterListener trackExporterListener) {
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
|
||||
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, trackWriter, tracks, trackExporterListener);
|
||||
|
||||
return new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new track writer for the format.
|
||||
*
|
||||
* @param context the context
|
||||
* @param multiple true for writing multiple tracks
|
||||
*/
|
||||
public abstract TrackWriter newTrackWriter(Context context, boolean multiple);
|
||||
|
||||
/**
|
||||
* Returns the file extension for each format.
|
||||
*/
|
||||
public String getExtension() {
|
||||
public abstract String getExtension();
|
||||
|
||||
/**
|
||||
* Returns the name of for each format.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name().toLowerCase(Locale.US);
|
||||
}
|
||||
}
|
||||
+59
-65
@@ -16,22 +16,26 @@
|
||||
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An activity for saving tracks to the external storage. If saving a specific
|
||||
@@ -39,23 +43,16 @@ import java.io.File;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class SaveActivity extends Activity {
|
||||
public class ExportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
|
||||
|
||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||
|
||||
@Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed.
|
||||
public static final String EXTRA_PLAY_TRACK = "play_track";
|
||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
|
||||
private TrackFileFormat trackFileFormat;
|
||||
private long[] trackIds;
|
||||
private boolean playTrack;
|
||||
private String directoryDisplayName;
|
||||
|
||||
private SaveAsyncTask saveAsyncTask;
|
||||
private ExportAsyncTask exportAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
// the number of tracks successfully saved
|
||||
@@ -64,55 +61,67 @@ public class SaveActivity extends Activity {
|
||||
// the number of tracks to save
|
||||
private int totalCount;
|
||||
|
||||
// the last successfully saved path
|
||||
private String savedPath;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
|
||||
playTrack = intent.getBooleanExtra(EXTRA_PLAY_TRACK, false);
|
||||
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);
|
||||
} else {
|
||||
fileTypeDialogStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
fileTypeDialogStart();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void fileTypeDialogStart() {
|
||||
FileTypeDialogFragment
|
||||
.newInstance(R.string.export_all_title, R.string.export_all_option)
|
||||
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
||||
if (!FileUtils.isExternalStorageWriteable()) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
File directory = playTrack ? new File(getCacheDir(), FileUtils.PLAY_TRACKS_DIR)
|
||||
: new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
||||
File directory = new File(FileUtils.getPath(trackFileFormat.getExtension()));
|
||||
if (!FileUtils.ensureDirectoryExists(directory)) {
|
||||
Toast.makeText(this, R.string.external_storage_not_writable, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (playTrack) {
|
||||
for (File file : directory.listFiles()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
|
||||
directoryDisplayName = playTrack ? directory.getName()
|
||||
: FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof SaveAsyncTask) {
|
||||
saveAsyncTask = (SaveAsyncTask) retained;
|
||||
saveAsyncTask.setActivity(this);
|
||||
} else {
|
||||
saveAsyncTask = new SaveAsyncTask(this, trackIds, trackFileFormat, playTrack, directory);
|
||||
saveAsyncTask.execute();
|
||||
}
|
||||
//TODO (still needed?): getLastNonConfiguration instance returned ExportAsyncTask before
|
||||
// Object retained = getLastNonConfigurationInstance();
|
||||
// if (retained instanceof ExportAsyncTask) {
|
||||
// exportAsyncTask = (ExportAsyncTask) retained;
|
||||
// exportAsyncTask.setActivity(this);
|
||||
// } else {
|
||||
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, directory);
|
||||
exportAsyncTask.execute();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
saveAsyncTask.setActivity(null);
|
||||
return saveAsyncTask;
|
||||
public void onDismissed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,8 +133,9 @@ public class SaveActivity extends Activity {
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
saveAsyncTask.cancel(true);
|
||||
exportAsyncTask.cancel(true);
|
||||
dialog.dismiss();
|
||||
onDismissed();
|
||||
}
|
||||
}, directoryDisplayName);
|
||||
return progressDialog;
|
||||
@@ -152,35 +162,21 @@ public class SaveActivity extends Activity {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
onDismissed();
|
||||
}
|
||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int arg1) {
|
||||
dialog.dismiss();
|
||||
finish();
|
||||
onDismissed();
|
||||
}
|
||||
}).setTitle(titleId);
|
||||
if (!playTrack && trackIds.length == 1 && trackIds[0] != -1L && successCount == totalCount
|
||||
&& savedPath != null) {
|
||||
builder.setNegativeButton(
|
||||
R.string.share_track_share_file, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Intent intent = IntentUtils.newShareFileIntent(
|
||||
SaveActivity.this, trackIds[0], savedPath, trackFileFormat);
|
||||
startActivity(
|
||||
Intent.createChooser(intent, getString(R.string.share_track_picker_title)));
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
final Dialog dialog = builder.create();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
|
||||
@Override
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
DialogUtils.setDialogTitleDivider(SaveActivity.this, dialog);
|
||||
DialogUtils.setDialogTitleDivider(ExportActivity.this, dialog);
|
||||
}
|
||||
});
|
||||
return dialog;
|
||||
@@ -194,12 +190,10 @@ public class SaveActivity extends Activity {
|
||||
*
|
||||
* @param aSuccessCount the number of tracks successfully saved
|
||||
* @param aTotalCount the number of tracks to save
|
||||
* @param aSavedPath the last successfully saved path
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, String aSavedPath) {
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
savedPath = aSavedPath;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
+50
-89
@@ -22,6 +22,12 @@ import android.os.AsyncTask;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.TracksColumns;
|
||||
@@ -29,29 +35,21 @@ 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.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Async Task to save tracks to the external storage.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
//TODO Make independent from ExportActivity?
|
||||
public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private static final String TAG = SaveAsyncTask.class.getSimpleName();
|
||||
private final long[] trackIds;
|
||||
private static final String TAG = ExportAsyncTask.class.getSimpleName();
|
||||
private final TrackFileFormat trackFileFormat;
|
||||
@Deprecated //TODO Seems to be a left over from Google Earth integration and can be removed.
|
||||
private final boolean playTrack;
|
||||
private final File directory;
|
||||
private final Context context;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private SaveActivity saveActivity;
|
||||
private ExportActivity exportActivity;
|
||||
private WakeLock wakeLock;
|
||||
|
||||
// true if the AsyncTask has completed
|
||||
@@ -69,20 +67,15 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
/**
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param saveActivity the activity currently associated with this task
|
||||
* @param trackIds the track ids to save. To save all, set to size 1 with
|
||||
* trackIds[0] == -1L
|
||||
* @param exportActivity the activity currently associated with this task
|
||||
* @param trackFileFormat the track file format
|
||||
* @param playTrack true to play track
|
||||
* @param directory the directory to write the file
|
||||
*/
|
||||
public SaveAsyncTask(SaveActivity saveActivity, long[] trackIds, TrackFileFormat trackFileFormat, boolean playTrack, File directory) {
|
||||
this.saveActivity = saveActivity;
|
||||
this.trackIds = trackIds;
|
||||
public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat, File directory) {
|
||||
this.exportActivity = exportActivity;
|
||||
this.trackFileFormat = trackFileFormat;
|
||||
this.playTrack = playTrack;
|
||||
this.directory = directory;
|
||||
context = saveActivity.getApplicationContext();
|
||||
context = exportActivity.getApplicationContext();
|
||||
contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
|
||||
completed = false;
|
||||
@@ -94,19 +87,19 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
/**
|
||||
* Sets the current activity associated with this AsyncTask.
|
||||
*
|
||||
* @param saveActivity the current activity, can be null
|
||||
* @param exportActivity the current activity, can be null
|
||||
*/
|
||||
public void setActivity(SaveActivity saveActivity) {
|
||||
this.saveActivity = saveActivity;
|
||||
if (completed && saveActivity != null) {
|
||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
||||
public void setActivity(ExportActivity exportActivity) {
|
||||
this.exportActivity = exportActivity;
|
||||
if (completed && exportActivity != null) {
|
||||
exportActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
if (saveActivity != null) {
|
||||
saveActivity.showProgressDialog();
|
||||
if (exportActivity != null) {
|
||||
exportActivity.showProgressDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,31 +107,13 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try {
|
||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||
boolean isRecording = PreferencesUtils.getLong(saveActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isPaused = PreferencesUtils.getBoolean(saveActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
boolean isRecording = PreferencesUtils.getLong(exportActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isPaused = PreferencesUtils.getBoolean(exportActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
// Get the wake lock if not recording or paused
|
||||
if (!isRecording || isPaused) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(saveActivity, wakeLock);
|
||||
}
|
||||
if (trackIds.length == 1 && trackIds[0] == -1L) {
|
||||
return saveAllTracks();
|
||||
} else {
|
||||
totalCount = 1;
|
||||
Track[] tracks = new Track[trackIds.length];
|
||||
for (int i = 0; i < trackIds.length; i++) {
|
||||
tracks[i] = contentProviderUtils.getTrack(trackIds[i]);
|
||||
if (tracks[i] == null) {
|
||||
Log.d(TAG, "No track for " + trackIds[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (saveTracks(tracks)) {
|
||||
successCount = 1;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
||||
}
|
||||
return saveAllTracks();
|
||||
} finally {
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
@@ -148,29 +123,29 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
if (saveActivity != null) {
|
||||
saveActivity.setProgressDialogValue(values[0], values[1]);
|
||||
if (exportActivity != null) {
|
||||
exportActivity.setProgressDialogValue(values[0], values[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
completed = true;
|
||||
if (saveActivity != null) {
|
||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, savedPath);
|
||||
if (exportActivity != null) {
|
||||
exportActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled() {
|
||||
completed = true;
|
||||
if (saveActivity != null) {
|
||||
saveActivity.onAsyncTaskCompleted(successCount, totalCount, null);
|
||||
if (exportActivity != null) {
|
||||
exportActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves tracks to one file.
|
||||
* Saves tracks to one file (uses first track to determine the filename).
|
||||
*
|
||||
* @param tracks the tracks
|
||||
*/
|
||||
@@ -179,32 +154,23 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
TrackExporterListener trackExporterListener = new TrackExporterListener() {
|
||||
@Override
|
||||
public void onProgressUpdate(int number, int max) {
|
||||
//Update the progress dialog once every 500 points.
|
||||
if (number % 500 == 0) {
|
||||
publishProgress(number, max);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, tracks, trackExporterListener);
|
||||
|
||||
Track track = tracks[0];
|
||||
boolean useKmz = trackFileFormat == TrackFileFormat.KML && !playTrack;
|
||||
String extension = useKmz ? KmzTrackExporter.KMZ_EXTENSION : trackFileFormat.getExtension();
|
||||
FileTrackExporter fileTrackExporter = new FileTrackExporter(contentProviderUtils, tracks,
|
||||
trackFileFormat.newTrackWriter(context, tracks.length > 1, playTrack),
|
||||
new TrackExporterListener() {
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(int number, int max) {
|
||||
/*
|
||||
* If only saving one track, update the progress dialog once every
|
||||
* 500 points
|
||||
*/
|
||||
if (trackIds.length == 1 && trackIds[0] != -1L && number % 500 == 0) {
|
||||
publishProgress(number, max);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks, context) : fileTrackExporter;
|
||||
|
||||
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), extension);
|
||||
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), trackFileFormat.getExtension());
|
||||
File file = new File(directory, fileName);
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(file);
|
||||
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
|
||||
if (trackExporter.writeTrack(fileOutputStream)) {
|
||||
savedPath = file.getAbsolutePath();
|
||||
return true;
|
||||
@@ -218,14 +184,9 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Unable to open file " + file.getName(), e);
|
||||
return false;
|
||||
} finally {
|
||||
if (fileOutputStream != null) {
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close file output stream", e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close file output stream", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils.LocationIterator;
|
||||
@@ -28,8 +30,6 @@ import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Track Writer for writing tracks to an {@link OutputStream}.
|
||||
*
|
||||
@@ -49,12 +49,11 @@ public class FileTrackExporter implements TrackExporter {
|
||||
* Constructor.
|
||||
*
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param tracks the tracks
|
||||
* @param trackWriter the track writer
|
||||
* @param tracks the tracks
|
||||
* @param trackExporterListener the track export listener
|
||||
*/
|
||||
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks,
|
||||
TrackWriter trackWriter, TrackExporterListener trackExporterListener) {
|
||||
public FileTrackExporter(ContentProviderUtils contentProviderUtils, TrackWriter trackWriter, Track[] tracks, TrackExporterListener trackExporterListener) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.tracks = tracks;
|
||||
this.trackWriter = trackWriter;
|
||||
@@ -95,8 +94,7 @@ public class FileTrackExporter implements TrackExporter {
|
||||
* load them into objects all at the same time.
|
||||
*/
|
||||
boolean hasWaypoints = false;
|
||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(
|
||||
track.getId(), -1L, Constants.MAX_LOADED_WAYPOINTS_POINTS)) {
|
||||
try (Cursor cursor = contentProviderUtils.getWaypointCursor(track.getId(), -1L, Constants.MAX_LOADED_WAYPOINTS_POINTS)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
/*
|
||||
* Yes, this will skip the first waypoint and that is intentional as the
|
||||
@@ -132,8 +130,7 @@ public class FileTrackExporter implements TrackExporter {
|
||||
LocationIterator locationIterator = null;
|
||||
|
||||
try {
|
||||
locationIterator = contentProviderUtils.getTrackPointLocationIterator(
|
||||
track.getId(), -1L, false, locationFactory);
|
||||
locationIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), -1L, false, locationFactory);
|
||||
|
||||
while (locationIterator.hasNext()) {
|
||||
if (Thread.interrupted()) {
|
||||
@@ -177,6 +174,11 @@ public class FileTrackExporter implements TrackExporter {
|
||||
isLastLocationValid = isLocationValid;
|
||||
}
|
||||
|
||||
if (wroteSegment) {
|
||||
//Close the last segment
|
||||
trackWriter.writeCloseSegment();
|
||||
}
|
||||
|
||||
if (wroteTrack) {
|
||||
Location lastValidTrackPoint = contentProviderUtils.getLastValidTrackPoint(track.getId());
|
||||
setLocationTime(lastValidTrackPoint, offset);
|
||||
|
||||
@@ -18,16 +18,15 @@ package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Write track as GPX to a file.
|
||||
*
|
||||
@@ -59,11 +58,6 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return TrackFileFormat.GPX.getExtension();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(OutputStream outputStream) {
|
||||
this.printWriter = new PrintWriter(outputStream);
|
||||
@@ -83,11 +77,9 @@ public class GpxTrackWriter implements TrackWriter {
|
||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
printWriter.println("<gpx");
|
||||
printWriter.println("version=\"1.1\"");
|
||||
printWriter.println(
|
||||
"creator=\"" + creator + "\"");
|
||||
printWriter.println("creator=\"" + creator + "\"");
|
||||
printWriter.println("xmlns=\"http://www.topografix.com/GPX/1/1\"");
|
||||
printWriter.println(
|
||||
"xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"");
|
||||
printWriter.println("xmlns:topografix=\"http://www.topografix.com/GPX/Private/TopoGrafix/0/1\"");
|
||||
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
||||
printWriter.println("xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1"
|
||||
+ " http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||
|
||||
@@ -20,8 +20,11 @@ import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.DescriptionGenerator;
|
||||
import de.dennisguse.opentracks.content.DescriptionGeneratorImpl;
|
||||
@@ -30,14 +33,7 @@ import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Write track as KML to a file.
|
||||
@@ -56,20 +52,17 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
private static final String HEART_RATE = "heart_rate";
|
||||
private static final String POWER = "power";
|
||||
|
||||
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
|
||||
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 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 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 final Context context;
|
||||
private final boolean multiple;
|
||||
private final boolean playTrack;
|
||||
private final boolean hasMultipleTracks;
|
||||
private final boolean exportPictures;
|
||||
private final boolean exportTrackDetails;
|
||||
private final boolean exportSensorData;
|
||||
private final DescriptionGenerator descriptionGenerator;
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@@ -81,24 +74,25 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
private boolean hasCadence;
|
||||
private boolean hasHeartRate;
|
||||
|
||||
public KmlTrackWriter(Context context, boolean multiple, boolean playTrack) {
|
||||
this(context, multiple, playTrack, new DescriptionGeneratorImpl(context));
|
||||
}
|
||||
private Location startLocation;
|
||||
|
||||
@VisibleForTesting
|
||||
KmlTrackWriter(Context context, boolean multiple, boolean playTrack, DescriptionGenerator descriptionGenerator) {
|
||||
/**
|
||||
* @param context the context
|
||||
* @param hasMultipleTracks should encode multiple tracks into one file?
|
||||
* @param exportPictures should pictures be exported (if true: exports to KMZ)?
|
||||
* @param exportTrackDetails should detailed information about the track be exported (e.g., title, description, waypoints, timing)?
|
||||
* @param exportSensorData should {@link SensorDataSet} be exported?
|
||||
*/
|
||||
public KmlTrackWriter(Context context, boolean hasMultipleTracks, boolean exportTrackDetails, boolean exportSensorData, boolean exportPictures) {
|
||||
this.context = context;
|
||||
this.multiple = multiple;
|
||||
this.playTrack = playTrack;
|
||||
this.descriptionGenerator = descriptionGenerator;
|
||||
this.hasMultipleTracks = hasMultipleTracks;
|
||||
this.exportTrackDetails = exportTrackDetails;
|
||||
this.exportSensorData = exportSensorData;
|
||||
this.exportPictures = exportPictures;
|
||||
this.descriptionGenerator = new DescriptionGeneratorImpl(context);
|
||||
this.contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtension() {
|
||||
return TrackFileFormat.KML.getExtension();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(OutputStream outputStream) {
|
||||
this.printWriter = new PrintWriter(outputStream);
|
||||
@@ -122,20 +116,25 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
printWriter.println("<Document>");
|
||||
printWriter.println("<open>1</open>");
|
||||
printWriter.println("<visibility>1</visibility>");
|
||||
Track track = tracks[0];
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
printWriter.println("<atom:author><atom:name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.app_name))
|
||||
+ "</atom:name></atom:author>");
|
||||
|
||||
if (exportTrackDetails) {
|
||||
Track track = tracks[0];
|
||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||
}
|
||||
|
||||
printWriter.println("<atom:author><atom:name>" + StringUtils.formatCData(context.getString(R.string.app_name)) + "</atom:name></atom:author>");
|
||||
writeTrackStyle();
|
||||
writePlacemarkerStyle(START_STYLE, START_ICON, 32, 1);
|
||||
writePlacemarkerStyle(END_STYLE, END_ICON, 32, 1);
|
||||
writePlacemarkerStyle(STATISTICS_STYLE, STATISTICS_ICON, 20, 2);
|
||||
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
|
||||
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
|
||||
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power));
|
||||
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence));
|
||||
writeSensorStyle(HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
|
||||
|
||||
if (exportSensorData) {
|
||||
writeSensorStyle(POWER, context.getString(R.string.description_sensor_power));
|
||||
writeSensorStyle(CADENCE, context.getString(R.string.description_sensor_cadence));
|
||||
writeSensorStyle(HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
|
||||
}
|
||||
printWriter.println("</Schema>");
|
||||
}
|
||||
}
|
||||
@@ -151,9 +150,10 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
@Override
|
||||
public void writeBeginWaypoints(Track track) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<Folder><name>"
|
||||
+ StringUtils.formatCData(context.getString(R.string.track_markers, track.getName()))
|
||||
+ "</name>");
|
||||
printWriter.println("<Folder>");
|
||||
if (exportTrackDetails) {
|
||||
printWriter.println("<name>" + StringUtils.formatCData(context.getString(R.string.track_markers, track.getName())) + "</name>");
|
||||
}
|
||||
printWriter.println("<open>1</open>");
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
|
||||
@Override
|
||||
public void writeWaypoint(Waypoint waypoint) {
|
||||
if (printWriter != null) {
|
||||
if (printWriter != null && exportTrackDetails) {
|
||||
String styleName = waypoint.getType() == WaypointType.STATISTICS ? STATISTICS_STYLE : WAYPOINT_STYLE;
|
||||
if (waypoint.hasPhoto()) {
|
||||
float heading = getHeading(waypoint.getTrackId(), waypoint.getLocation());
|
||||
@@ -180,7 +180,7 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
|
||||
@Override
|
||||
public void writeBeginTracks() {
|
||||
if (printWriter != null && multiple) {
|
||||
if (printWriter != null && hasMultipleTracks) {
|
||||
printWriter.println("<Folder id=tour>");
|
||||
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
||||
printWriter.println("<open>1</open>");
|
||||
@@ -189,20 +189,20 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
|
||||
@Override
|
||||
public void writeEndTracks() {
|
||||
if (printWriter != null && multiple) {
|
||||
if (printWriter != null && hasMultipleTracks) {
|
||||
printWriter.println("</Folder>");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBeginTrack(Track track, Location startLocation) {
|
||||
this.startLocation = startLocation;
|
||||
if (printWriter != null) {
|
||||
String name = context.getString(R.string.marker_label_start, track.getName());
|
||||
writePlacemark(name, "", "", START_STYLE, startLocation);
|
||||
printWriter.println("<Placemark>");
|
||||
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("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||
writeCategory(track.getCategory());
|
||||
printWriter.println("<gx:MultiTrack>");
|
||||
@@ -217,7 +217,7 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
printWriter.println("</gx:MultiTrack>");
|
||||
printWriter.println("</Placemark>");
|
||||
String name = context.getString(R.string.marker_label_end, track.getName());
|
||||
String description = descriptionGenerator.generateTrackDescription(track, null, null, false);
|
||||
String description = descriptionGenerator.generateTrackDescription(track, false);
|
||||
writePlacemark(name, "", description, END_STYLE, endLocation);
|
||||
}
|
||||
}
|
||||
@@ -240,14 +240,16 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
if (printWriter != null) {
|
||||
printWriter.println("<ExtendedData>");
|
||||
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
||||
if (hasPower) {
|
||||
writeSensorData(powerList, POWER);
|
||||
}
|
||||
if (hasCadence) {
|
||||
writeSensorData(cadenceList, CADENCE);
|
||||
}
|
||||
if (hasHeartRate) {
|
||||
writeSensorData(heartRateList, HEART_RATE);
|
||||
if (exportSensorData) {
|
||||
if (hasPower) {
|
||||
writeSensorData(powerList, POWER);
|
||||
}
|
||||
if (hasCadence) {
|
||||
writeSensorData(cadenceList, CADENCE);
|
||||
}
|
||||
if (hasHeartRate) {
|
||||
writeSensorData(heartRateList, HEART_RATE);
|
||||
}
|
||||
}
|
||||
printWriter.println("</SchemaData>");
|
||||
printWriter.println("</ExtendedData>");
|
||||
@@ -258,10 +260,13 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
@Override
|
||||
public void writeLocation(Location location) {
|
||||
if (printWriter != null) {
|
||||
printWriter.println(
|
||||
"<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
|
||||
if (exportTrackDetails) {
|
||||
printWriter.println("<when>" + getTime(location) + "</when>");
|
||||
}
|
||||
|
||||
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
|
||||
if (location instanceof SensorDataSetLocation) {
|
||||
|
||||
if (exportSensorData && location instanceof SensorDataSetLocation) {
|
||||
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
|
||||
if (sensorDataSet != null) {
|
||||
if (sensorDataSet.hasHeartRate()) {
|
||||
@@ -301,15 +306,12 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
* @param styleName the style name
|
||||
* @param location the location
|
||||
*/
|
||||
private void writePlacemark(
|
||||
String name, String category, String description, String styleName, Location location) {
|
||||
if (location != null) {
|
||||
private void writePlacemark(String name, String category, String description, String styleName, Location location) {
|
||||
if (location != null && exportTrackDetails) {
|
||||
printWriter.println("<Placemark>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||
printWriter.println(
|
||||
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||
printWriter.println("<TimeStamp><when>"
|
||||
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
||||
printWriter.println("<description>" + StringUtils.formatCData(description) + "</description>");
|
||||
printWriter.println("<TimeStamp><when>" + getTime(location) + "</when></TimeStamp>");
|
||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||
writeCategory(category);
|
||||
printWriter.println("<Point>");
|
||||
@@ -330,13 +332,11 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
* @param photoUrl the photo url
|
||||
* @param heading the heading
|
||||
*/
|
||||
private void writePhotoOverlay(String name, String category, String description, String styleName,
|
||||
Location location, String photoUrl, float heading) {
|
||||
if (location != null) {
|
||||
private void writePhotoOverlay(String name, String category, String description, String styleName, Location location, String photoUrl, float heading) {
|
||||
if (location != null && exportTrackDetails) {
|
||||
printWriter.println("<PhotoOverlay>");
|
||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||
printWriter.println(
|
||||
"<description>" + StringUtils.formatCData(description) + "</description>");
|
||||
printWriter.println("<description>" + StringUtils.formatCData(description) + "</description>");
|
||||
printWriter.print("<Camera>");
|
||||
printWriter.print("<longitude>" + location.getLongitude() + "</longitude>");
|
||||
printWriter.print("<latitude>" + location.getLatitude() + "</latitude>");
|
||||
@@ -344,16 +344,12 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
printWriter.print("<heading>" + heading + "</heading>");
|
||||
printWriter.print("<tilt>90</tilt>");
|
||||
printWriter.println("</Camera>");
|
||||
printWriter.println("<TimeStamp><when>"
|
||||
+ StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
|
||||
printWriter.println("<TimeStamp><when>" + getTime(location) + "</when></TimeStamp>");
|
||||
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
|
||||
writeCategory(category);
|
||||
if (playTrack) {
|
||||
|
||||
if (exportPictures) {
|
||||
printWriter.println("<Icon><href>" + Uri.decode(photoUrl) + "</href></Icon>");
|
||||
} else {
|
||||
Uri uri = Uri.parse(photoUrl);
|
||||
printWriter.println("<Icon><href>" + KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar
|
||||
+ uri.getLastPathSegment() + "</href></Icon>");
|
||||
}
|
||||
|
||||
printWriter.print("<ViewVolume>");
|
||||
@@ -370,6 +366,19 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted time of the location; either absolute or relative depending exportTrackDetails.
|
||||
*
|
||||
* @param location the location
|
||||
*/
|
||||
private String getTime(Location location) {
|
||||
if (exportTrackDetails) {
|
||||
return StringUtils.formatDateTimeIso8601(location.getTime());
|
||||
} else {
|
||||
return StringUtils.formatDateTimeIso8601(location.getTime() - startLocation.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the heading to a location.
|
||||
*
|
||||
@@ -411,8 +420,7 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
return;
|
||||
}
|
||||
printWriter.println("<ExtendedData>");
|
||||
printWriter.println(
|
||||
"<Data name=\"type\"><value>" + StringUtils.formatCData(category) + "</value></Data>");
|
||||
printWriter.println("<Data name=\"type\"><value>" + StringUtils.formatCData(category) + "</value></Data>");
|
||||
printWriter.println("</ExtendedData>");
|
||||
}
|
||||
|
||||
@@ -441,8 +449,7 @@ public class KmlTrackWriter implements TrackWriter {
|
||||
printWriter.println("<Style id=\"" + name + "\"><IconStyle>");
|
||||
printWriter.println("<scale>1.3</scale>");
|
||||
printWriter.println("<Icon><href>" + url + "</href></Icon>");
|
||||
printWriter.println(
|
||||
"<hotSpot x=\"" + x + "\" y=\"" + y + "\" xunits=\"pixels\" yunits=\"pixels\"/>");
|
||||
printWriter.println("<hotSpot x=\"" + x + "\" y=\"" + y + "\" xunits=\"pixels\" yunits=\"pixels\"/>");
|
||||
printWriter.println("</IconStyle></Style>");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,19 +16,10 @@
|
||||
|
||||
package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -36,14 +27,16 @@ import java.io.OutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
|
||||
/**
|
||||
* KMZ track exporter.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class KmzTrackExporter implements TrackExporter {
|
||||
|
||||
public static final String KMZ_EXTENSION = "kmz";
|
||||
public static final String KMZ_IMAGES_DIR = "images";
|
||||
public static final String KMZ_KML_FILE = "doc.kml";
|
||||
|
||||
@@ -53,7 +46,6 @@ public class KmzTrackExporter implements TrackExporter {
|
||||
private final ContentProviderUtils contentProviderUtils;
|
||||
private final FileTrackExporter fileTrackExporter;
|
||||
private final Track[] tracks;
|
||||
private final long photoSize;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -61,15 +53,11 @@ public class KmzTrackExporter implements TrackExporter {
|
||||
* @param contentProviderUtils the content provider utils
|
||||
* @param fileTrackExporter the file track exporter
|
||||
* @param tracks the tracks to export
|
||||
* @param context the context
|
||||
*/
|
||||
public KmzTrackExporter(ContentProviderUtils contentProviderUtils,
|
||||
FileTrackExporter fileTrackExporter, Track[] tracks, Context context) {
|
||||
public KmzTrackExporter(ContentProviderUtils contentProviderUtils, FileTrackExporter fileTrackExporter, Track[] tracks) {
|
||||
this.contentProviderUtils = contentProviderUtils;
|
||||
this.fileTrackExporter = fileTrackExporter;
|
||||
this.tracks = tracks;
|
||||
this.photoSize = PreferencesUtils.getInt(
|
||||
context, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,40 +127,13 @@ public class KmzTrackExporter implements TrackExporter {
|
||||
return;
|
||||
}
|
||||
|
||||
ZipEntry zipEntry = new ZipEntry(
|
||||
KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
||||
ZipEntry zipEntry = new ZipEntry(KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment());
|
||||
zipOutputStream.putNextEntry(zipEntry);
|
||||
|
||||
int sampleSize;
|
||||
if (photoSize == -1) {
|
||||
sampleSize = 1;
|
||||
} else {
|
||||
long size = file.length();
|
||||
// Convert from kilobytes to bytes.
|
||||
long limit = photoSize * 1024;
|
||||
sampleSize = size > limit ? (int) Math.ceil(size / limit) : 1;
|
||||
}
|
||||
if (sampleSize == 1) {
|
||||
readFromFile(zipOutputStream, uri);
|
||||
} else {
|
||||
readFromScaledBitmap(zipOutputStream, uri, sampleSize);
|
||||
}
|
||||
readFromFile(zipOutputStream, uri);
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
|
||||
private void readFromScaledBitmap(ZipOutputStream zipOutputStream, Uri uri, int sampleSize) {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = sampleSize;
|
||||
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(uri.getPath(), options);
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, zipOutputStream);
|
||||
bitmap.recycle();
|
||||
}
|
||||
|
||||
private void readFromFile(ZipOutputStream zipOutputStream, Uri uri) throws IOException {
|
||||
try (FileInputStream fileInputStream = new FileInputStream(new File(uri.getPath()))) {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
|
||||
@@ -17,11 +17,11 @@ package de.dennisguse.opentracks.io.file.exporter;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Interface for writing tracks to a file. The expected sequence of calls is:
|
||||
*
|
||||
@@ -51,11 +51,6 @@ import java.io.OutputStream;
|
||||
*/
|
||||
public interface TrackWriter {
|
||||
|
||||
/**
|
||||
* Gets the file extension (e.g, gpx, kml, ...).
|
||||
*/
|
||||
String getExtension();
|
||||
|
||||
/**
|
||||
* Prepares the output stream.
|
||||
*
|
||||
|
||||
@@ -16,27 +16,26 @@
|
||||
|
||||
package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||
import java.io.File;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.fragments.FileTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An activity to import files from the external storage. Optionally to import
|
||||
@@ -44,20 +43,16 @@ import java.io.File;
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ImportActivity extends Activity {
|
||||
public class ImportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
|
||||
|
||||
public static final String EXTRA_IMPORT_ALL = "import_all";
|
||||
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
|
||||
private static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 6;
|
||||
|
||||
private static final String TAG = ImportActivity.class.getSimpleName();
|
||||
private static final int DIALOG_PROGRESS_ID = 0;
|
||||
private static final int DIALOG_RESULT_ID = 1;
|
||||
|
||||
private ImportAsyncTask importAsyncTask;
|
||||
private ProgressDialog progressDialog;
|
||||
|
||||
private boolean importAll;
|
||||
|
||||
// the path on the external storage to import
|
||||
private String directoryDisplayName;
|
||||
|
||||
@@ -67,68 +62,67 @@ public class ImportActivity extends Activity {
|
||||
// the number of files to import
|
||||
private int totalCount;
|
||||
|
||||
// the last successfully imported track id
|
||||
private long trackId;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
|
||||
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
|
||||
if (trackFileFormat == null) {
|
||||
trackFileFormat = TrackFileFormat.GPX;
|
||||
if (!FileUtils.isExternalStorageWriteable() || ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
|
||||
} else {
|
||||
fileTypeDialogStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
|
||||
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
|
||||
Toast.makeText(this, R.string.external_storage_not_readable, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
fileTypeDialogStart();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
private void fileTypeDialogStart() {
|
||||
FileTypeDialogFragment
|
||||
.newInstance(R.string.import_selection_title, R.string.import_selection_option)
|
||||
.show(getSupportFragmentManager(), FileTypeDialogFragment.FILE_TYPE_DIALOG_TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFileTypeDone(TrackFileFormat trackFileFormat) {
|
||||
if (!FileUtils.isExternalStorageAvailable()) {
|
||||
Toast.makeText(this, R.string.external_storage_not_available, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
String directoryPath;
|
||||
if (importAll) {
|
||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
String action = intent.getAction();
|
||||
if (!(Intent.ACTION_ATTACH_DATA.equals(action) || Intent.ACTION_VIEW.equals(action))) {
|
||||
Log.d(TAG, "Invalid action: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
Uri data = intent.getData();
|
||||
if ("file".equals(data.getScheme())) {
|
||||
Log.d(TAG, "Invalid data: " + intent);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
directoryDisplayName = data.getPath();
|
||||
directoryPath = data.getPath();
|
||||
directoryDisplayName = FileUtils.getPathDisplayName(trackFileFormat.getExtension());
|
||||
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;
|
||||
}
|
||||
|
||||
Object retained = getLastNonConfigurationInstance();
|
||||
if (retained instanceof ImportAsyncTask) {
|
||||
importAsyncTask = (ImportAsyncTask) retained;
|
||||
importAsyncTask.setActivity(this);
|
||||
} else {
|
||||
importAsyncTask = new ImportAsyncTask(this, importAll, trackFileFormat, directoryPath);
|
||||
importAsyncTask.execute();
|
||||
}
|
||||
//TODO (still needed?): getLastNonConfiguration instance returned ExportAsyncTask before
|
||||
// Object retained = getLastNonConfigurationInstance();
|
||||
// if (retained instanceof ImportAsyncTask) {
|
||||
// importAsyncTask = (ImportAsyncTask) retained;
|
||||
// importAsyncTask.setActivity(this);
|
||||
// } else {
|
||||
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, directoryPath);
|
||||
importAsyncTask.execute();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onRetainNonConfigurationInstance() {
|
||||
importAsyncTask.setActivity(null);
|
||||
return importAsyncTask;
|
||||
public void onDismissed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,6 +135,7 @@ public class ImportActivity extends Activity {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
importAsyncTask.cancel(true);
|
||||
dialog.dismiss();
|
||||
onDismissed();
|
||||
}
|
||||
}, directoryDisplayName);
|
||||
return progressDialog;
|
||||
@@ -171,21 +166,13 @@ public class ImportActivity extends Activity {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialogInterface) {
|
||||
dialogInterface.dismiss();
|
||||
finish();
|
||||
onDismissed();
|
||||
}
|
||||
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int which) {
|
||||
if (successCount == totalCount && !importAll && trackId != -1L) {
|
||||
Intent intent = IntentUtils.newIntent(
|
||||
ImportActivity.this, TrackDetailActivity.class)
|
||||
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, trackId);
|
||||
TaskStackBuilder.create(ImportActivity.this)
|
||||
.addParentStack(TrackDetailActivity.class).addNextIntent(intent)
|
||||
.startActivities();
|
||||
}
|
||||
dialogInterface.dismiss();
|
||||
finish();
|
||||
onDismissed();
|
||||
}
|
||||
}).setTitle(titleId).create();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
@@ -206,12 +193,10 @@ public class ImportActivity extends Activity {
|
||||
*
|
||||
* @param aSuccessCount the number of files successfully imported
|
||||
* @param aTotalCount the number of files to import
|
||||
* @param aTrackId the last successfully imported track id
|
||||
*/
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount, long aTrackId) {
|
||||
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount) {
|
||||
successCount = aSuccessCount;
|
||||
totalCount = aTotalCount;
|
||||
trackId = aTrackId;
|
||||
removeDialog(DIALOG_PROGRESS_ID);
|
||||
showDialog(DIALOG_RESULT_ID);
|
||||
}
|
||||
|
||||
@@ -22,22 +22,20 @@ import android.os.AsyncTask;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
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;
|
||||
|
||||
/**
|
||||
* AsyncTask to import files from the external storage.
|
||||
*
|
||||
@@ -46,7 +44,6 @@ import java.util.List;
|
||||
public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
|
||||
private static final String TAG = ImportAsyncTask.class.getSimpleName();
|
||||
private final boolean importAll;
|
||||
private final TrackFileFormat trackFileFormat;
|
||||
private final String path;
|
||||
private final Context context;
|
||||
@@ -69,14 +66,11 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
* Creates an AsyncTask.
|
||||
*
|
||||
* @param importActivity the activity currently associated with this AsyncTask
|
||||
* @param importAll true to import all GPX files
|
||||
* @param trackFileFormat the track file format
|
||||
* @param path path to import GPX files
|
||||
*/
|
||||
public ImportAsyncTask(ImportActivity importActivity, boolean importAll,
|
||||
TrackFileFormat trackFileFormat, String path) {
|
||||
public ImportAsyncTask(ImportActivity importActivity, TrackFileFormat trackFileFormat, String path) {
|
||||
this.importActivity = importActivity;
|
||||
this.importAll = importAll;
|
||||
this.trackFileFormat = trackFileFormat;
|
||||
this.path = path;
|
||||
context = importActivity.getApplicationContext();
|
||||
@@ -95,7 +89,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
public void setActivity(ImportActivity importActivity) {
|
||||
this.importActivity = importActivity;
|
||||
if (completed && importActivity != null) {
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,11 +105,8 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
try {
|
||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||
// Get the wake lock if not recording or paused
|
||||
boolean isRecording =
|
||||
PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key)
|
||||
!= PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isPaused = PreferencesUtils.getBoolean(importActivity,
|
||||
R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
boolean isRecording = PreferencesUtils.getLong(importActivity, R.string.recording_track_id_key) != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
boolean isPaused = PreferencesUtils.getBoolean(importActivity, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
|
||||
if (!isRecording || isPaused) {
|
||||
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
|
||||
}
|
||||
@@ -155,7 +146,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
protected void onPostExecute(Boolean result) {
|
||||
completed = true;
|
||||
if (importActivity != null) {
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +154,7 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
protected void onCancelled() {
|
||||
completed = true;
|
||||
if (importActivity != null) {
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount, trackId);
|
||||
importActivity.onAsyncTaskCompleted(successCount, totalCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,37 +164,28 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
* @param file the file
|
||||
*/
|
||||
private boolean importFile(final File file) {
|
||||
FileInputStream fileInputStream = null;
|
||||
try {
|
||||
TrackImporter trackImporter;
|
||||
if (trackFileFormat == TrackFileFormat.KML) {
|
||||
String extension = FileUtils.getExtension(file.getName());
|
||||
if (TrackFileFormat.KML.getExtension().equals(extension)) {
|
||||
trackImporter = new KmlFileTrackImporter(context, -1L);
|
||||
} else {
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
Uri uri = contentProviderUtils.insertTrack(new Track());
|
||||
long newId = Long.parseLong(uri.getLastPathSegment());
|
||||
|
||||
trackImporter = new KmzTrackImporter(context, newId);
|
||||
}
|
||||
TrackImporter trackImporter;
|
||||
if (trackFileFormat == TrackFileFormat.GPX) {
|
||||
trackImporter = new GpxFileTrackImporter(context);
|
||||
} else { //KML or KMZ
|
||||
String extension = FileUtils.getExtension(file.getName());
|
||||
if (TrackFileFormat.KML_ONLY_TRACK.getExtension().equals(extension)) {
|
||||
trackImporter = new KmlFileTrackImporter(context, -1L);
|
||||
} else {
|
||||
trackImporter = new GpxFileTrackImporter(context);
|
||||
ContentProviderUtils contentProviderUtils = ContentProviderUtils.Factory.get(context);
|
||||
Uri uri = contentProviderUtils.insertTrack(new Track());
|
||||
long newId = Long.parseLong(uri.getLastPathSegment());
|
||||
|
||||
trackImporter = new KmzTrackImporter(context, newId);
|
||||
}
|
||||
fileInputStream = new FileInputStream(file);
|
||||
}
|
||||
|
||||
try (FileInputStream fileInputStream = new FileInputStream(file)) {
|
||||
trackId = trackImporter.importFile(fileInputStream);
|
||||
return trackId != -1L;
|
||||
} catch (FileNotFoundException e) {
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to import file", e);
|
||||
return false;
|
||||
} finally {
|
||||
if (fileInputStream != null) {
|
||||
try {
|
||||
fileInputStream.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Unable to close file input stream", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,26 +197,20 @@ public class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
||||
private List<File> getFiles() {
|
||||
List<File> files = new ArrayList<>();
|
||||
File file = new File(path);
|
||||
if (importAll) {
|
||||
File[] candidates = file.listFiles();
|
||||
if (candidates != null) {
|
||||
for (File candidate : candidates) {
|
||||
if (!FileUtils.isDirectory(candidate)) {
|
||||
String extension = FileUtils.getExtension(candidate.getName());
|
||||
if (trackFileFormat == TrackFileFormat.KML && (
|
||||
TrackFileFormat.KML.getExtension().equals(extension)
|
||||
|| KmzTrackExporter.KMZ_EXTENSION.equals(extension))) {
|
||||
files.add(candidate);
|
||||
} else if (trackFileFormat == TrackFileFormat.GPX
|
||||
&& TrackFileFormat.GPX.getExtension().equals(extension)) {
|
||||
files.add(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
files.add(file);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackDetailActivity;
|
||||
import de.dennisguse.opentracks.TrackListActivity;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
@@ -55,8 +58,8 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
|
||||
import de.dennisguse.opentracks.content.WaypointCreationRequest;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
import de.dennisguse.opentracks.services.sensors.SensorManager;
|
||||
import de.dennisguse.opentracks.services.sensors.SensorManagerFactory;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManager;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManagerFactory;
|
||||
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
||||
import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
|
||||
import de.dennisguse.opentracks.services.tasks.SplitPeriodicTaskFactory;
|
||||
@@ -69,10 +72,6 @@ import de.dennisguse.opentracks.util.SystemUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackNameUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* A background service that registers a location listener and records track
|
||||
@@ -182,7 +181,7 @@ public class TrackRecordingService extends Service {
|
||||
};
|
||||
private TripStatisticsUpdater markerTripStatisticsUpdater;
|
||||
private WakeLock wakeLock;
|
||||
private SensorManager sensorManager;
|
||||
private RemoteSensorManager remoteSensorManager;
|
||||
private Location lastLocation;
|
||||
private boolean currentSegmentHasLocation;
|
||||
private boolean isIdle; // true if idle
|
||||
@@ -241,7 +240,7 @@ public class TrackRecordingService extends Service {
|
||||
locationManagerConnector = new LocationManagerConnector(this, handler.getLooper());
|
||||
voiceExecutor = new PeriodicTaskExecutor(this, new AnnouncementPeriodicTaskFactory());
|
||||
splitExecutor = new PeriodicTaskExecutor(this, new SplitPeriodicTaskFactory());
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
// onSharedPreferenceChanged might not set recordingTrackId.
|
||||
@@ -281,9 +280,9 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (sensorManager != null) {
|
||||
SensorManagerFactory.releaseSystemSensorManager();
|
||||
sensorManager = null;
|
||||
if (remoteSensorManager != null) {
|
||||
RemoteSensorManagerFactory.releaseSystemSensorManager();
|
||||
remoteSensorManager = null;
|
||||
}
|
||||
|
||||
// Reverse order from onCreate
|
||||
@@ -618,7 +617,7 @@ public class TrackRecordingService extends Service {
|
||||
*/
|
||||
private void startRecording(boolean trackStarted) {
|
||||
// Update instance variables
|
||||
sensorManager = SensorManagerFactory.getSystemSensorManager(this);
|
||||
remoteSensorManager = RemoteSensorManagerFactory.getSystemSensorManager(this);
|
||||
lastLocation = null;
|
||||
currentSegmentHasLocation = false;
|
||||
isIdle = false;
|
||||
@@ -716,9 +715,9 @@ public class TrackRecordingService extends Service {
|
||||
splitExecutor.shutdown();
|
||||
|
||||
// Update instance variables
|
||||
if (sensorManager != null) {
|
||||
SensorManagerFactory.releaseSystemSensorManager();
|
||||
sensorManager = null;
|
||||
if (remoteSensorManager != null) {
|
||||
RemoteSensorManagerFactory.releaseSystemSensorManager();
|
||||
remoteSensorManager = null;
|
||||
}
|
||||
lastLocation = null;
|
||||
|
||||
@@ -932,10 +931,10 @@ public class TrackRecordingService extends Service {
|
||||
}
|
||||
|
||||
private SensorDataSet getSensorDataSet() {
|
||||
if (sensorManager == null || !sensorManager.isEnabled() || !sensorManager.isSensorDataSetValid()) {
|
||||
if (remoteSensorManager == null || !remoteSensorManager.isEnabled() || !remoteSensorManager.isSensorDataSetValid()) {
|
||||
return null;
|
||||
}
|
||||
return sensorManager.getSensorDataSet();
|
||||
return remoteSensorManager.getSensorDataSet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1013,8 +1012,7 @@ public class TrackRecordingService extends Service {
|
||||
* @param trackId the track id
|
||||
*/
|
||||
private void sendTrackBroadcast(int actionId, long trackId) {
|
||||
Intent intent = new Intent().setAction(getString(actionId))
|
||||
.putExtra(getString(R.string.track_id_broadcast_extra), trackId);
|
||||
Intent intent = new Intent().setAction(getString(actionId)).putExtra(getString(R.string.track_id_broadcast_extra), trackId);
|
||||
sendBroadcast(intent, getString(R.string.permission_notification_value));
|
||||
}
|
||||
|
||||
@@ -1106,24 +1104,24 @@ public class TrackRecordingService extends Service {
|
||||
|
||||
@Override
|
||||
public SensorDataSet getSensorData() {
|
||||
if (trackRecordingService.sensorManager == null) {
|
||||
Log.d(TAG, "sensorManager is null.");
|
||||
if (trackRecordingService.remoteSensorManager == null) {
|
||||
Log.d(TAG, "remoteSensorManager is null.");
|
||||
return null;
|
||||
}
|
||||
if (trackRecordingService.getSensorDataSet() == null) {
|
||||
Log.d(TAG, "Sensor data set is null.");
|
||||
return null;
|
||||
}
|
||||
return trackRecordingService.sensorManager.getSensorDataSet();
|
||||
return trackRecordingService.remoteSensorManager.getSensorDataSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SensorState getSensorState() {
|
||||
if (trackRecordingService.sensorManager == null) {
|
||||
Log.d(TAG, "sensorManager is null.");
|
||||
if (trackRecordingService.remoteSensorManager == null) {
|
||||
Log.d(TAG, "remoteSensorManager is null.");
|
||||
return SensorState.NONE;
|
||||
}
|
||||
return trackRecordingService.sensorManager.getSensorState();
|
||||
return trackRecordingService.remoteSensorManager.getSensorState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-5
@@ -24,13 +24,13 @@ import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Bluetooth sensor manager.
|
||||
@@ -39,7 +39,7 @@ import java.util.ArrayList;
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class BluetoothSensorManager extends SensorManager {
|
||||
public class BluetoothRemoteSensorManager extends RemoteSensorManager {
|
||||
|
||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
||||
private static final BluetoothAdapter bluetoothAdapter = getDefaultBluetoothAdapter();
|
||||
@@ -71,7 +71,7 @@ public class BluetoothSensorManager extends SensorManager {
|
||||
/**
|
||||
* @param context the context
|
||||
*/
|
||||
public BluetoothSensorManager(Context context) {
|
||||
public BluetoothRemoteSensorManager(Context context) {
|
||||
this.context = context;
|
||||
bluetoothConnectionManager = new BluetoothConnectionManager(context, messageHandler);
|
||||
}
|
||||
+7
-7
@@ -18,23 +18,23 @@ package de.dennisguse.opentracks.services.sensors;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorState;
|
||||
|
||||
/**
|
||||
* Manage the connection to a sensor.
|
||||
* Manage the connection to a remote sensor.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public abstract class SensorManager {
|
||||
public abstract class RemoteSensorManager {
|
||||
|
||||
public static final long MAX_SENSOR_DATE_SET_AGE = 5000;
|
||||
public static final long MAX_SENSOR_STATE_AGE = 20000;
|
||||
|
||||
private static final String TAG = SensorManager.class.getSimpleName();
|
||||
private static final String TAG = RemoteSensorManager.class.getSimpleName();
|
||||
private static final int RETRY_PERIOD = 20000;
|
||||
|
||||
private SensorState sensorState = SensorState.NONE;
|
||||
@@ -88,7 +88,7 @@ public abstract class SensorManager {
|
||||
}
|
||||
}
|
||||
};
|
||||
timer = new Timer(SensorManager.class.getSimpleName());
|
||||
timer = new Timer(RemoteSensorManager.class.getSimpleName());
|
||||
timer.schedule(timerTask, RETRY_PERIOD, RETRY_PERIOD);
|
||||
}
|
||||
|
||||
+22
-26
@@ -19,16 +19,16 @@ package de.dennisguse.opentracks.services.sensors;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* A factory of {@link SensorManager}.
|
||||
* A factory of {@link RemoteSensorManager}.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class SensorManagerFactory {
|
||||
public class RemoteSensorManagerFactory {
|
||||
|
||||
private static SensorManager systemSensorManager = null;
|
||||
private static SensorManager sensorManagerTemporary = null;
|
||||
private static RemoteSensorManager remoteSensorManagerSystem = null;
|
||||
private static RemoteSensorManager remoteSensorManagerTemporary = null;
|
||||
|
||||
private SensorManagerFactory() {
|
||||
private RemoteSensorManagerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,49 +36,45 @@ public class SensorManagerFactory {
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public static SensorManager getSystemSensorManager(Context context) {
|
||||
public static RemoteSensorManager getSystemSensorManager(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
releaseSystemSensorManager();
|
||||
systemSensorManager = getSensorManager(context);
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.startSensor();
|
||||
}
|
||||
return systemSensorManager;
|
||||
remoteSensorManagerSystem = getSensorManager(context);
|
||||
remoteSensorManagerSystem.startSensor();
|
||||
return remoteSensorManagerSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the system sensor manager.
|
||||
*/
|
||||
public static void releaseSystemSensorManager() {
|
||||
if (systemSensorManager != null) {
|
||||
systemSensorManager.stopSensor();
|
||||
if (remoteSensorManagerSystem != null) {
|
||||
remoteSensorManagerSystem.stopSensor();
|
||||
}
|
||||
systemSensorManager = null;
|
||||
remoteSensorManagerSystem = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the temp sensor manager.
|
||||
*/
|
||||
public static SensorManager getSensorManagerTemporary(Context context) {
|
||||
public static RemoteSensorManager getSensorManagerTemporary(Context context) {
|
||||
releaseSensorManagerTemporary();
|
||||
if (systemSensorManager != null) {
|
||||
if (remoteSensorManagerSystem != null) {
|
||||
return null;
|
||||
}
|
||||
sensorManagerTemporary = getSensorManager(context);
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.startSensor();
|
||||
}
|
||||
return sensorManagerTemporary;
|
||||
remoteSensorManagerTemporary = getSensorManager(context);
|
||||
remoteSensorManagerTemporary.startSensor();
|
||||
return remoteSensorManagerTemporary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the temp sensor manager.
|
||||
*/
|
||||
public static void releaseSensorManagerTemporary() {
|
||||
if (sensorManagerTemporary != null) {
|
||||
sensorManagerTemporary.stopSensor();
|
||||
if (remoteSensorManagerTemporary != null) {
|
||||
remoteSensorManagerTemporary.stopSensor();
|
||||
}
|
||||
sensorManagerTemporary = null;
|
||||
remoteSensorManagerTemporary = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +82,7 @@ public class SensorManagerFactory {
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
private static SensorManager getSensorManager(Context context) {
|
||||
return new BluetoothSensorManager(context);
|
||||
private static RemoteSensorManager getSensorManager(Context context) {
|
||||
return new BluetoothRemoteSensorManager(context);
|
||||
}
|
||||
}
|
||||
@@ -1,127 +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.settings;
|
||||
|
||||
import android.app.ActionBar;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* An abstract activity for all the settings activities.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AbstractSettingsActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
|
||||
|
||||
ActionBar actionBar = this.getActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
PreferenceManager preferenceManager = getPreferenceManager();
|
||||
preferenceManager.setSharedPreferencesName(Constants.SETTINGS_NAME);
|
||||
preferenceManager.setSharedPreferencesMode(Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() != android.R.id.home) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a list preference.
|
||||
*
|
||||
* @param listPreference the list preference
|
||||
* @param summary the summary array
|
||||
* @param options the options array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
* @param listener optional listener
|
||||
*/
|
||||
protected void configureListPreference(ListPreference listPreference, final String[] summary,
|
||||
final String[] options, final String[] values, String value,
|
||||
final OnPreferenceChangeListener listener) {
|
||||
listPreference.setEntryValues(values);
|
||||
listPreference.setEntries(options);
|
||||
listPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
updatePreferenceSummary(pref, summary, values, (String) newValue);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(pref, newValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
updatePreferenceSummary(listPreference, summary, values, value);
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(listPreference, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the preference summary.
|
||||
*
|
||||
* @param preference the preference
|
||||
* @param summary the summary array
|
||||
* @param values the values array
|
||||
* @param value the value
|
||||
*/
|
||||
private void updatePreferenceSummary(Preference preference, String[] summary, String[] values, String value) {
|
||||
int index = getIndex(values, value);
|
||||
if (index == -1) {
|
||||
preference.setSummary(R.string.value_unknown);
|
||||
} else {
|
||||
preference.setSummary(summary[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array index for a value.
|
||||
*
|
||||
* @param values the array
|
||||
* @param value the value
|
||||
*/
|
||||
private int getIndex(String[] values, String value) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (value.equals(values[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1,185 +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.settings;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.util.DialogUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* An activity for advanced settings.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class AdvancedSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
private static final String TAG = AdvancedSettingsActivity.class.getSimpleName();
|
||||
|
||||
private static final int DIALOG_CONFIRM_RESET_ID = 1;
|
||||
|
||||
private Preference resetPreference;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||
* Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final OnSharedPreferenceChangeListener
|
||||
sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key != null && key.equals(PreferencesUtils.getKey(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(
|
||||
AdvancedSettingsActivity.this, R.string.recording_track_id_key);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUi();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_advanced);
|
||||
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.photo_size_key));
|
||||
int value = PreferencesUtils.getInt(this, R.string.photo_size_key, PreferencesUtils.PHOTO_SIZE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.photo_size_values);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
setPhotoSizeSummaryAndOptions(summary, options, values);
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
|
||||
sharedPreferences = getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
|
||||
resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
resetPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference arg0) {
|
||||
showDialog(DIALOG_CONFIRM_RESET_ID);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||
updateUi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
switch (id) {
|
||||
case DIALOG_CONFIRM_RESET_ID:
|
||||
return DialogUtils.createConfirmationDialog(this, R.string.settings_reset_confirm_title,
|
||||
getString(R.string.settings_reset_confirm_message),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface d, int button) {
|
||||
onResetPreferencesConfirmed();
|
||||
}
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setPhotoSizeSummaryAndOptions(String[] summary, String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
if (value == -1) {
|
||||
options[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_original);
|
||||
} else if (value < 1024) {
|
||||
options[i] = getString(R.string.value_integer_kilobyte, value);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
} else {
|
||||
int megabyte = value / 1024;
|
||||
options[i] = getString(R.string.value_integer_megabyte, megabyte);
|
||||
summary[i] = getString(R.string.settings_advanced_photo_size_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the UI based on the recording state.
|
||||
*/
|
||||
private void updateUi() {
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when the user confirms resetting all settings.
|
||||
*/
|
||||
private void onResetPreferencesConfirmed() {
|
||||
// Change preferences in a separate thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i(TAG, "Resetting all settings");
|
||||
|
||||
// Actually wipe preferences and save synchronously
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Give UI feedback in the UI thread
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(
|
||||
AdvancedSettingsActivity.this, R.string.settings_reset_done, Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
// Restart the settings activity so all changes are loaded
|
||||
Intent intent = getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* An activity for accessing chart settings.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class ChartSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_chart);
|
||||
configXAxisListPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateUi();
|
||||
}
|
||||
|
||||
private void updateUi() {
|
||||
CheckBoxPreference speedCheckBoxPreference = (CheckBoxPreference) findPreference(
|
||||
getString(R.string.chart_show_speed_key));
|
||||
speedCheckBoxPreference.setTitle(
|
||||
PreferencesUtils.isReportSpeed(this) ? R.string.stats_speed : R.string.stats_pace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the x axis list preference.
|
||||
*/
|
||||
private void configXAxisListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.chart_x_axis_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.chart_x_axis_key, PreferencesUtils.CHART_X_AXIS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.chart_x_axis_values);
|
||||
String[] options = getResources().getStringArray(R.array.chart_x_axis_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller;
|
||||
@@ -33,7 +34,7 @@ import de.dennisguse.opentracks.R;
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
public class RecordingSettingsActivity extends PreferenceActivity
|
||||
implements ChooseActivityTypeCaller {
|
||||
|
||||
private static final int DIALOG_CHOOSE_ACTIVITY = 0;
|
||||
@@ -47,27 +48,17 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(this);
|
||||
|
||||
configFrequencyPreference(R.string.voice_frequency_key,
|
||||
PreferencesUtils.VOICE_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configFrequencyPreference(R.string.split_frequency_key,
|
||||
PreferencesUtils.SPLIT_FREQUENCY_DEFAULT, R.array.frequency_values, metricUnits);
|
||||
configTrackName();
|
||||
configFrequencyPreference(R.string.voice_frequency_key, metricUnits);
|
||||
configFrequencyPreference(R.string.split_frequency_key, metricUnits);
|
||||
configDefaultActivity();
|
||||
configListPreference(R.string.min_recording_interval_key,
|
||||
PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT, R.array.min_recording_interval_values,
|
||||
configListPreference(R.string.min_recording_interval_key, R.array.min_recording_interval_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_distance_interval_key,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT,
|
||||
R.array.recording_distance_interval_values, metricUnits);
|
||||
configListPreference(R.string.max_recording_distance_key,
|
||||
PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT, R.array.max_recording_distance_values,
|
||||
configListPreference(R.string.recording_distance_interval_key, R.array.recording_distance_interval_values, metricUnits);
|
||||
configListPreference(R.string.max_recording_distance_key, R.array.max_recording_distance_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.recording_gps_accuracy_key,
|
||||
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT, R.array.recording_gps_accuracy_values,
|
||||
configListPreference(R.string.recording_gps_accuracy_key, R.array.recording_gps_accuracy_values,
|
||||
metricUnits);
|
||||
configListPreference(R.string.auto_resume_track_timeout_key,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT,
|
||||
R.array.auto_resume_track_timeout_values, metricUnits);
|
||||
configListPreference(R.string.auto_resume_track_timeout_key, R.array.auto_resume_track_timeout_values, metricUnits);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,21 +72,11 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
return ChooseActivityTypeDialogFragment.getDialog(this, category, this);
|
||||
}
|
||||
|
||||
private void configFrequencyPreference(int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
private void configFrequencyPreference(int key, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = StringUtils.getFrequencyOptions(this, metricUnits);
|
||||
configureListPreference(preference, options, options, values, String.valueOf(value), null);
|
||||
}
|
||||
|
||||
private void configTrackName() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.track_name_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.track_name_key, PreferencesUtils.TRACK_NAME_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.track_name_values);
|
||||
String[] options = getResources().getStringArray(R.array.track_name_options);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
String[] options = StringUtils.getFrequencyOptions(this, metricUnits);
|
||||
preference.setEntries(options);
|
||||
}
|
||||
|
||||
private void configDefaultActivity() {
|
||||
@@ -120,78 +101,69 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
activityTypePreference.setRecordingSettingsActivity(this);
|
||||
}
|
||||
|
||||
private void configListPreference(
|
||||
int key, int defaultValue, int valueArray, boolean metricUnits) {
|
||||
private void configListPreference(int key, int valueArray, boolean metricUnits) {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(key));
|
||||
int value = PreferencesUtils.getInt(this, key, defaultValue);
|
||||
|
||||
@Deprecated
|
||||
String[] values = getResources().getStringArray(valueArray);
|
||||
String[] options = new String[values.length];
|
||||
String[] summary = new String[values.length];
|
||||
switch (key) {
|
||||
case R.string.min_recording_interval_key:
|
||||
setMinRecordingIntervalSummaryAndOptions(summary, options, values);
|
||||
setMinRecordingIntervalOptions(options, values);
|
||||
break;
|
||||
case R.string.recording_distance_interval_key:
|
||||
setRecordingDistanceIntervalSummaryAndOptions(summary, options, values, metricUnits);
|
||||
setRecordingDistanceIntervalOptions(options, values, metricUnits);
|
||||
break;
|
||||
case R.string.max_recording_distance_key:
|
||||
setMaxRecordingDistanceSummaryAndOptions(summary, options, values, metricUnits);
|
||||
setMaxRecordingDistanceOptions(options, values, metricUnits);
|
||||
break;
|
||||
case R.string.recording_gps_accuracy_key:
|
||||
setRecordingGpsAccuracySummaryAndOptions(summary, options, values, metricUnits);
|
||||
setRecordingGpsAccuracyOptions(options, values, metricUnits);
|
||||
break;
|
||||
case R.string.auto_resume_track_timeout_key:
|
||||
setAutoResumeTrackTimeoutSummaryAndOptions(summary, options, values);
|
||||
setAutoResumeTrackTimeoutOptions(options, values);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
configureListPreference(preference, summary, options, values, String.valueOf(value), null);
|
||||
|
||||
preference.setEntries(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the min recording interval summary and options.
|
||||
* Sets the min recording interval options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setMinRecordingIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
private void setMinRecordingIntervalOptions(String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE:
|
||||
options[i] = getString(R.string.value_adapt_battery_life);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_ADAPT_ACCURACY:
|
||||
options[i] = getString(R.string.value_adapt_accuracy);
|
||||
summary[i] = options[i];
|
||||
break;
|
||||
case PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT:
|
||||
options[i] = getString(R.string.value_smallest_recommended);
|
||||
summary[i] = getString(R.string.value_smallest);
|
||||
break;
|
||||
default:
|
||||
options[i] = value < 60 ? getString(R.string.value_integer_second, value)
|
||||
: getString(R.string.value_integer_minute, value / 60);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_location_frequency_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording distance interval summary and options.
|
||||
* Sets the recording distance interval options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingDistanceIntervalSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
private void setRecordingDistanceIntervalOptions(String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
@@ -211,20 +183,17 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(R.string.settings_recording_location_frequency_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max recording distance summary and options.
|
||||
* Sets the max recording distance options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setMaxRecordingDistanceSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
private void setMaxRecordingDistanceOptions(String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
@@ -250,21 +219,17 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
options[i] = displayValue;
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_max_recording_distance_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording gps accuracy summary and options.
|
||||
* Sets the recording gps accuracy options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
* @param metricUnits true for metric units
|
||||
*/
|
||||
private void setRecordingGpsAccuracySummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values, boolean metricUnits) {
|
||||
private void setRecordingGpsAccuracyOptions(String[] options, String[] values, boolean metricUnits) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
String displayValue;
|
||||
@@ -307,37 +272,27 @@ public class RecordingSettingsActivity extends AbstractSettingsActivity
|
||||
}
|
||||
}
|
||||
}
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_min_required_accuracy_summary, displayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the auto resume track timeout summary and options.
|
||||
* Sets the auto resume track timeout options.
|
||||
*
|
||||
* @param summary the summary
|
||||
* @param options the options
|
||||
* @param values the values
|
||||
*/
|
||||
private void setAutoResumeTrackTimeoutSummaryAndOptions(
|
||||
String[] summary, String[] options, String[] values) {
|
||||
private void setAutoResumeTrackTimeoutOptions(String[] options, String[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
switch (value) {
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_NEVER:
|
||||
options[i] = getString(R.string.value_never);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_never_summary);
|
||||
break;
|
||||
case PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_ALWAYS:
|
||||
options[i] = getString(R.string.value_always);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_always_summary);
|
||||
break;
|
||||
default:
|
||||
options[i] = getString(R.string.value_integer_minute, value);
|
||||
summary[i] = getString(
|
||||
R.string.settings_recording_auto_resume_track_timeout_summary, options[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.preference.DialogPreference;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
public class ResetDialogPreference extends DialogPreference {
|
||||
|
||||
public ResetDialogPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
super.onClick(dialog, which);
|
||||
if (which == DialogInterface.BUTTON_POSITIVE) {
|
||||
PreferencesUtils.resetPreferences(getContext(), true);
|
||||
|
||||
Toast.makeText(getContext(), R.string.settings_reset_done, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +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.settings;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.provider.Settings;
|
||||
|
||||
import de.dennisguse.opentracks.util.BluetoothDeviceUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An activity for accessing sensor settings.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SensorSettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_sensors);
|
||||
|
||||
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Update each time in case the list of bluetooth sensors has changed
|
||||
configBluetoothSensor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bluetooth sensor.
|
||||
*/
|
||||
private void configBluetoothSensor() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
String value = PreferencesUtils.getString(this, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
List<String> devicesNameList = new ArrayList<>();
|
||||
List<String> devicesAddressList = new ArrayList<>();
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter != null) {
|
||||
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
|
||||
}
|
||||
|
||||
// Was the previously configured device unpaired? Then forget it.
|
||||
if (!devicesAddressList.contains(value)) {
|
||||
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
|
||||
PreferencesUtils.setString(this, R.string.bluetooth_sensor_key, value);
|
||||
}
|
||||
|
||||
devicesNameList.add(0, getString(R.string.value_none));
|
||||
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
|
||||
String[] options = devicesNameList.toArray(new String[0]);
|
||||
String[] values = devicesAddressList.toArray(new String[0]);
|
||||
configureListPreference(preference, options, options, values, value, null);
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,53 @@
|
||||
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.provider.Settings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.BluetoothDeviceUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* An activity for accessing settings.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class SettingsActivity extends AbstractSettingsActivity {
|
||||
public class SettingsActivity extends PreferenceActivity {
|
||||
|
||||
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
|
||||
/*
|
||||
* Note that sharedPreferenceChangeListenr cannot be an anonymous inner class.
|
||||
* Anonymous inner class will get garbage collected.
|
||||
*/
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
||||
if (key != null && key.equals(PreferencesUtils.getKey(SettingsActivity.this, R.string.recording_track_id_key))) {
|
||||
recordingTrackId = PreferencesUtils.getLong(SettingsActivity.this, R.string.recording_track_id_key);
|
||||
}
|
||||
|
||||
//TODO Should only be called if something meaningful happens.
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateUI();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
@@ -37,11 +70,33 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
configPreference(R.string.settings_chart_key, ChartSettingsActivity.class);
|
||||
configPreference(R.string.settings_stats_key, StatsSettingsActivity.class);
|
||||
configPreference(R.string.settings_recording_key, RecordingSettingsActivity.class);
|
||||
configPreference(R.string.settings_sensor_key, SensorSettingsActivity.class);
|
||||
configPreference(R.string.settings_advanced_key, AdvancedSettingsActivity.class);
|
||||
|
||||
findPreference(getString(R.string.settings_sensor_bluetooth_pairing_key))
|
||||
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
Intent settingsIntent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
|
||||
startActivity(settingsIntent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
PreferencesUtils.getSharedPreferences(this).registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
recordingTrackId = PreferencesUtils.getLong(this, R.string.recording_track_id_key);
|
||||
updateUI();
|
||||
|
||||
configBluetoothSensor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
PreferencesUtils.getSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,4 +116,44 @@ public class SettingsActivity extends AbstractSettingsActivity {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the bluetooth sensor.
|
||||
*/
|
||||
private void configBluetoothSensor() {
|
||||
ListPreference preference = (ListPreference) findPreference(getString(R.string.bluetooth_sensor_key));
|
||||
String value = PreferencesUtils.getString(this, R.string.bluetooth_sensor_key, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
List<String> devicesNameList = new ArrayList<>();
|
||||
List<String> devicesAddressList = new ArrayList<>();
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter != null) {
|
||||
BluetoothDeviceUtils.populateDeviceLists(bluetoothAdapter, devicesNameList, devicesAddressList);
|
||||
}
|
||||
|
||||
// Was the previously configured device unpaired? Then forget it.
|
||||
if (!devicesAddressList.contains(value)) {
|
||||
value = PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT;
|
||||
PreferencesUtils.setString(this, R.string.bluetooth_sensor_key, value);
|
||||
}
|
||||
|
||||
devicesNameList.add(0, getString(R.string.value_none));
|
||||
devicesAddressList.add(0, PreferencesUtils.BLUETOOTH_SENSOR_DEFAULT);
|
||||
|
||||
String[] values = devicesAddressList.toArray(new String[0]);
|
||||
preference.setEntryValues(values);
|
||||
|
||||
String[] options = devicesNameList.toArray(new String[0]);
|
||||
preference.setEntries(options);
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
//TODO Remove the following if recordingTrackId is replaced by direct communication rather than via preferences.
|
||||
boolean isRecording = recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
|
||||
Preference speedCheckBoxPreference = findPreference(getString(R.string.chart_show_speed_key));
|
||||
speedCheckBoxPreference.setTitle(PreferencesUtils.isReportSpeed(this) ? R.string.stats_speed : R.string.stats_pace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,36 +20,34 @@ import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* An activity for accessing stats settings.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
public class StatsSettingsActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.settings_statistics);
|
||||
|
||||
/*
|
||||
* Note configureUnitsListPreference will trigger
|
||||
* configureRateListPreference
|
||||
*/
|
||||
configUnitsListPreference();
|
||||
boolean metricUnits = PreferencesUtils.STATS_UNITS_DEFAULT.equals(PreferencesUtils.getString(this, R.string.stats_units_key, ""));
|
||||
configRateListPreference(metricUnits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the preferred units list preference.
|
||||
*/
|
||||
private void configUnitsListPreference() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_units_key));
|
||||
OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_units_key));
|
||||
final OnPreferenceChangeListener listener = new OnPreferenceChangeListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
@@ -58,11 +56,15 @@ public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_units_key, PreferencesUtils.STATS_UNITS_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_units_values);
|
||||
String[] options = getResources().getStringArray(R.array.stats_units_options);
|
||||
configureListPreference(listPreference, options, options, values, value, listener);
|
||||
listPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference pref, Object newValue) {
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(pref, newValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,13 +73,9 @@ public class StatsSettingsActivity extends AbstractSettingsActivity {
|
||||
* @param metricUnits true if metric units
|
||||
*/
|
||||
private void configRateListPreference(boolean metricUnits) {
|
||||
ListPreference listPreference = (ListPreference) findPreference(
|
||||
getString(R.string.stats_rate_key));
|
||||
String value = PreferencesUtils.getString(
|
||||
this, R.string.stats_rate_key, PreferencesUtils.STATS_RATE_DEFAULT);
|
||||
String[] values = getResources().getStringArray(R.array.stats_rate_values);
|
||||
String[] options = getResources().getStringArray(
|
||||
metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
configureListPreference(listPreference, options, options, values, value, null);
|
||||
ListPreference listPreference = (ListPreference) findPreference(getString(R.string.stats_rate_key));
|
||||
String[] options = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
|
||||
listPreference.setEntries(options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,30 +77,6 @@ public class DoubleBuffer {
|
||||
return sum / numberOfEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average and the variance of the buffer.
|
||||
*
|
||||
* @return an array of two elements - the first is the average, the second is
|
||||
* the variance
|
||||
*/
|
||||
public double[] getAverageAndVariance() {
|
||||
int numberOfEntries = isFull ? buffer.length : index;
|
||||
if (numberOfEntries == 0) {
|
||||
return new double[]{0, 0};
|
||||
}
|
||||
|
||||
double sum = 0;
|
||||
double sumSquares = 0;
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
sum += buffer[i];
|
||||
sumSquares += Math.pow(buffer[i], 2);
|
||||
}
|
||||
|
||||
double average = sum / numberOfEntries;
|
||||
double variance = sumSquares / numberOfEntries - Math.pow(average, 2);
|
||||
return new double[]{average, variance};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a double to the buffer. If the buffer is full the oldest element is
|
||||
* overwritten.
|
||||
|
||||
@@ -35,10 +35,7 @@ public class FileUtils {
|
||||
* Used to transfer picture from the camera.
|
||||
*/
|
||||
public static final String FILEPROVIDER = BuildConfig.APPLICATION_ID + ".fileprovider";
|
||||
/**
|
||||
* The play tracks directory in the app's cache directory.
|
||||
*/
|
||||
public static final String PLAY_TRACKS_DIR = "temp";
|
||||
|
||||
/**
|
||||
* Our external storage directory.
|
||||
*/
|
||||
|
||||
@@ -16,18 +16,28 @@
|
||||
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.DescriptionGeneratorImpl;
|
||||
import de.dennisguse.opentracks.content.ShareContentProvider;
|
||||
import de.dennisguse.opentracks.content.Track;
|
||||
import de.dennisguse.opentracks.content.Waypoint;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Utilities for creating intents.
|
||||
@@ -36,6 +46,8 @@ import java.io.File;
|
||||
*/
|
||||
public class IntentUtils {
|
||||
|
||||
private final static String TAG = IntentUtils.class.getSimpleName();
|
||||
|
||||
private IntentUtils() {
|
||||
}
|
||||
|
||||
@@ -50,38 +62,91 @@ public class IntentUtils {
|
||||
return new Intent(context, cls).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
private static final String JPEG_EXTENSION = "jpeg";
|
||||
|
||||
/**
|
||||
* Creates an intent to share a track file with an app.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackId the track id
|
||||
* @param filePath the file path
|
||||
* @param trackFileFormat the track file format
|
||||
* @param context the context
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
public static Intent newShareFileIntent(Context context, long trackId, String filePath, TrackFileFormat trackFileFormat) {
|
||||
Track track = ContentProviderUtils.Factory.get(context).getTrack(trackId);
|
||||
String trackDescription = track == null ? "" : new DescriptionGeneratorImpl(context).generateTrackDescription(track, null, null, false);
|
||||
public static Intent newShareFileIntent(Context context, long[] trackIds) {
|
||||
String trackDescription = "";
|
||||
if (trackIds.length == 1) {
|
||||
Track track = ContentProviderUtils.Factory.get(context).getTrack(trackIds[0]);
|
||||
trackDescription = track == null ? "" : new DescriptionGeneratorImpl(context).generateTrackDescription(track, false);
|
||||
}
|
||||
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, TrackFileFormat.KMZ_WITH_SENSORDATA_AND_PICTURES);
|
||||
|
||||
return new Intent(Intent.ACTION_SEND)
|
||||
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filePath)))
|
||||
.putExtra(Intent.EXTRA_STREAM, uriAndMime.first)
|
||||
.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.share_track_subject))
|
||||
.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.share_track_share_file_body, trackDescription))
|
||||
.putExtra(context.getString(R.string.track_id_broadcast_extra), trackId)
|
||||
.setType(trackFileFormat.getMimeType());
|
||||
.setType(uriAndMime.second)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
public static Intent newShowOnMapIntent(Waypoint waypoint) {
|
||||
return newShowOnMapIntent(waypoint.getLocation().getLatitude(), waypoint.getLocation().getLongitude(), waypoint.getName());
|
||||
public static void showCoordinateOnMap(Context context, Waypoint waypoint) {
|
||||
showCoordinateOnMap(context, waypoint.getLocation().getLatitude(), waypoint.getLocation().getLongitude(), waypoint.getName());
|
||||
}
|
||||
|
||||
public static Intent newShowOnMapIntent(double latitude, double longitude, String label) {
|
||||
public static void showCoordinateOnMap(Context context, double latitude, double longitude, String label) {
|
||||
//SEE https://developer.android.com/guide/components/intents-common.html#Maps
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
String uri = "geo:0,0?q=" + latitude + "," + longitude;
|
||||
if (label != null && label.length() > 0) {
|
||||
uri += "(" + label + ")";
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(uri));
|
||||
return intent;
|
||||
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(context, context.getString(R.string.app_not_installed_show_on_map), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send intent to show tracks on a map (needs an another app).
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
public static void showTrackOnMap(Context context, long[] trackIds) {
|
||||
if (trackIds.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, TrackFileFormat.KMZ_ONLY_TRACK);
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(android.content.Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(uriAndMime.first, uriAndMime.second);
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(context, context.getString(R.string.app_not_installed_show_on_map), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a take picture request to the camera app.
|
||||
* The picture is then stored in the track's folder.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public static Pair<Intent, Uri> createTakePictureIntent(Context context, long trackId) {
|
||||
File dir = FileUtils.getPhotoDir(trackId);
|
||||
FileUtils.ensureDirectoryExists(dir);
|
||||
|
||||
String fileName = SimpleDateFormat.getDateTimeInstance().format(new Date());
|
||||
File file = new File(dir, FileUtils.buildUniqueFileName(dir, fileName, JPEG_EXTENSION));
|
||||
|
||||
Uri photoUri = FileProvider.getUriForFile(context, FileUtils.FILEPROVIDER, file);
|
||||
Log.d(TAG, "Taking photo to URI: " + photoUri);
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
|
||||
.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
|
||||
return new Pair<>(intent, photoUri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package de.dennisguse.opentracks.util;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import de.dennisguse.opentracks.Constants;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
@@ -55,7 +55,6 @@ public class PreferencesUtils {
|
||||
// Value for split_frequency_key and voice_frequency_key
|
||||
public static final int FREQUENCY_OFF = 0;
|
||||
|
||||
public static final int MAP_TYPE_DEFAULT = 1;
|
||||
public static final int MAX_RECORDING_DISTANCE_DEFAULT = 200;
|
||||
|
||||
// Values for min_recording_interval_key
|
||||
@@ -63,7 +62,6 @@ public class PreferencesUtils {
|
||||
public static final int MIN_RECORDING_INTERVAL_ADAPT_BATTERY_LIFE = -2;
|
||||
public static final int MIN_RECORDING_INTERVAL_DEFAULT = 0;
|
||||
|
||||
public static final int PHOTO_SIZE_DEFAULT = 1024; // 1024 kB
|
||||
public static final int RECORDING_DISTANCE_INTERVAL_DEFAULT = 10;
|
||||
|
||||
// Values for recording_gps_accuracy
|
||||
@@ -71,6 +69,7 @@ public class PreferencesUtils {
|
||||
public static final int RECORDING_GPS_ACCURACY_EXCELLENT = 10;
|
||||
public static final int RECORDING_GPS_ACCURACY_POOR = 2000;
|
||||
|
||||
@Deprecated //NOTE: is at the moment still used to determine if a track is currently recorded; better ask the service directly.
|
||||
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
|
||||
public static final boolean RECORDING_TRACK_PAUSED_DEFAULT = true;
|
||||
|
||||
@@ -84,6 +83,8 @@ public class PreferencesUtils {
|
||||
|
||||
public static final String TRACK_NAME_DEFAULT = "DATE_ISO_8601";
|
||||
|
||||
public static final boolean SHOW_TRACKDETAIL_WHILE_RECORDING_ON_LOCKSCREEN = false;
|
||||
|
||||
// Track widget
|
||||
public static final int TRACK_WIDGET_ITEM1_DEFAULT = 3; // moving time
|
||||
public static final int TRACK_WIDGET_ITEM2_DEFAULT = 0; // distance
|
||||
@@ -94,6 +95,10 @@ public class PreferencesUtils {
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
public static SharedPreferences getSharedPreferences(Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a preference key
|
||||
*
|
||||
@@ -112,7 +117,7 @@ public class PreferencesUtils {
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public static boolean getBoolean(Context context, int keyId, boolean defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
@@ -124,7 +129,7 @@ public class PreferencesUtils {
|
||||
* @param value the value
|
||||
*/
|
||||
public static void setBoolean(Context context, int keyId, boolean value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putBoolean(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
@@ -138,16 +143,21 @@ public class PreferencesUtils {
|
||||
* @param defaultValue the default value
|
||||
*/
|
||||
public static int getInt(Context context, int keyId, int defaultValue) {
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
String stringValue = sharedPreferences.getString(getKey(context, keyId), null);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
|
||||
int intValue = defaultValue;
|
||||
try {
|
||||
intValue = Integer.parseInt(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
try {
|
||||
String stringValue = sharedPreferences.getString(getKey(context, keyId), null);
|
||||
return Integer.parseInt(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
return intValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +168,7 @@ public class PreferencesUtils {
|
||||
* @param value the value
|
||||
*/
|
||||
public static void setInt(Context context, int keyId, int value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
@@ -171,7 +181,7 @@ public class PreferencesUtils {
|
||||
* @param keyId the key id
|
||||
*/
|
||||
public static long getLong(Context context, int keyId) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
return sharedPreferences.getLong(getKey(context, keyId), -1L);
|
||||
}
|
||||
|
||||
@@ -183,7 +193,7 @@ public class PreferencesUtils {
|
||||
* @param value the value
|
||||
*/
|
||||
public static void setLong(Context context, int keyId, long value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putLong(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
@@ -197,7 +207,7 @@ public class PreferencesUtils {
|
||||
* @param defaultValue default value
|
||||
*/
|
||||
public static String getString(Context context, int keyId, String defaultValue) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
@@ -209,7 +219,7 @@ public class PreferencesUtils {
|
||||
* @param value the value
|
||||
*/
|
||||
public static void setString(Context context, int keyId, String value) {
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
@@ -242,4 +252,10 @@ public class PreferencesUtils {
|
||||
public static boolean isChartByDistance(Context context) {
|
||||
return CHART_X_AXIS_DEFAULT.equals(getString(context, R.string.chart_x_axis_key, CHART_X_AXIS_DEFAULT));
|
||||
}
|
||||
|
||||
public static void resetPreferences(Context context, boolean readAgain) {
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings_recording, readAgain);
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings_statistics, readAgain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import android.view.View;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.sensors.SensorManager;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.sensors.RemoteSensorManager;
|
||||
import de.dennisguse.opentracks.stats.TripStatistics;
|
||||
|
||||
/**
|
||||
* Utilities for updating the statistics UI labels and values.
|
||||
*
|
||||
@@ -143,6 +143,8 @@ public class StatsUtils {
|
||||
setDistanceValue(context, getView(activity, view, R.id.stats_distance), totalDistance, metricUnits);
|
||||
|
||||
// Set activity type
|
||||
getView(activity, null, R.id.stats_activity_type_label).setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
|
||||
Spinner spinner = (Spinner) getView(activity, view, R.id.stats_activity_type_icon);
|
||||
spinner.setVisibility(trackIconValue != null ? View.VISIBLE : View.GONE);
|
||||
if (trackIconValue != null) {
|
||||
@@ -292,7 +294,7 @@ public class StatsUtils {
|
||||
private static void setHeartRateData(Context context, View viewValue, View viewSensor, SensorDataSet sensorDataSet) {
|
||||
String heartRate = context.getString(R.string.value_unknown);
|
||||
String sensor = context.getString(R.string.value_unknown);
|
||||
if (sensorDataSet != null && sensorDataSet.hasHeartRate() && sensorDataSet.isRecent(SensorManager.MAX_SENSOR_DATE_SET_AGE)) {
|
||||
if (sensorDataSet != null && sensorDataSet.hasHeartRate() && sensorDataSet.isRecent(RemoteSensorManager.MAX_SENSOR_DATE_SET_AGE)) {
|
||||
heartRate = StringUtils.formatDecimal(sensorDataSet.getHeartRate(), 0);
|
||||
sensor = sensorDataSet.getSensorName();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.pm.PackageInfoCompat;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
|
||||
/**
|
||||
@@ -47,7 +49,7 @@ public class SystemUtils {
|
||||
public static String getAppVersionName(Context context) {
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA);
|
||||
return pi.versionName;
|
||||
return pi.versionName + "/" + BuildConfig.VERSION_NAME_FULL;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Failed to get version info.", e);
|
||||
return "";
|
||||
@@ -57,7 +59,7 @@ public class SystemUtils {
|
||||
public static Long getAppVersionCode(Context context) {
|
||||
try {
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, PackageManager.GET_META_DATA);
|
||||
return pi.getLongVersionCode();
|
||||
return PackageInfoCompat.getLongVersionCode(pi);
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Failed to get version info.", e);
|
||||
return -1L;
|
||||
|
||||
@@ -17,19 +17,14 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Address;
|
||||
import android.location.Geocoder;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
/**
|
||||
* Utilities for track name.
|
||||
*
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Distance, calorie, total time, and moving time -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_distance"
|
||||
layout="@layout/stats_large_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_calorie"
|
||||
layout="@layout/stats_activity_type" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_total_time"
|
||||
layout="@layout/stats_large_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_moving_time"
|
||||
layout="@layout/stats_large_item" />
|
||||
</LinearLayout>
|
||||
<!-- Horizontal line -->
|
||||
<View style="@style/StatsHorizontalLine" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
<!-- Speed and average speed -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_average_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Max speed and average moving speed -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_max_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_average_moving_speed"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
<!-- Latitude and longitude -->
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_coordinate_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_latitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_longitude"
|
||||
layout="@layout/stats_small_item" />
|
||||
</LinearLayout>
|
||||
</TableLayout>
|
||||
<!-- Vertical line -->
|
||||
<View
|
||||
android:id="@+id/stats_elevation_separator"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/stats_separator" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/stats_elevation_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer">
|
||||
<!-- This is a placeholder -->
|
||||
</LinearLayout>
|
||||
<!-- Elevation -->
|
||||
<LinearLayout style="@style/StatsSmallGroupContainer">
|
||||
<!-- Elevation label -->
|
||||
<TextView
|
||||
style="@style/StatsSmallLabel"
|
||||
android:text="@string/stats_elevation" />
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center">
|
||||
<!-- Elevation -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- TODO DISABLED AS computation from GPS is not precise enough, go for getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure) -->
|
||||
<!-- Elevation gain -->
|
||||
<!--<include-->
|
||||
<!--android:id="@+id/stats_elevation_gain"-->
|
||||
<!--layout="@layout/stats_small_group_item" />-->
|
||||
<!-- Elevation min -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_min"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation max -->
|
||||
<include
|
||||
android:id="@+id/stats_elevation_max"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
</TableLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</merge>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Copyright 2013 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false">
|
||||
|
||||
<include layout="@layout/track_controller" />
|
||||
|
||||
<TabHost
|
||||
android:id="@android:id/tabhost"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<include layout="@layout/track_details_tab" />
|
||||
</TabHost>
|
||||
</LinearLayout>
|
||||
@@ -8,7 +8,6 @@
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="239dp"
|
||||
android:contentDescription="@string/image_my_tracks_logo"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_logo" />
|
||||
|
||||
|
||||
@@ -25,5 +25,5 @@ limitations under the License.
|
||||
<TextView
|
||||
android:id="@+id/marker_list_empty"
|
||||
style="@style/ListEmptyTextView"
|
||||
android:text="@string/marker_list_empty_message"></TextView>
|
||||
android:text="@string/marker_list_empty_message" />
|
||||
</merge>
|
||||
@@ -25,5 +25,5 @@ limitations under the License.
|
||||
<TextView
|
||||
android:id="@+id/search_list_empty"
|
||||
style="@style/ListEmptyTextView"
|
||||
android:text="@string/search_list_empty_message"></TextView>
|
||||
android:text="@string/search_list_empty_message" />
|
||||
</merge>
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
style="@style/StatsLargeItemContainer">
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/stats_label"
|
||||
android:id="@+id/stats_activity_type_label"
|
||||
style="@style/StatsLargeLabel"
|
||||
android:text="@string/track_edit_activity_type_hint" />
|
||||
<!-- Value container -->
|
||||
|
||||
@@ -24,7 +24,7 @@ limitations under the License.
|
||||
layout="@layout/stats_large_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/stats_calorie"
|
||||
android:id="@+id/stats_activity_type"
|
||||
layout="@layout/stats_activity_type" />
|
||||
</LinearLayout>
|
||||
<!-- Total time and moving time -->
|
||||
@@ -96,7 +96,7 @@ limitations under the License.
|
||||
android:id="@+id/stats_elevation"
|
||||
layout="@layout/stats_small_group_item" />
|
||||
<!-- Elevation gain -->
|
||||
<!-- TODO DISABLED AS computation from GPS is not precise enough, go for getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure) -->
|
||||
<!-- TODO DISABLED AS computation from GPS is not precise enough, go for getAltitude(RemoteSensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure) -->
|
||||
<!--<include-->
|
||||
<!--android:id="@+id/stats_elevation_gain"-->
|
||||
<!--layout="@layout/stats_small_group_item" />-->
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user