Compare commits

..

33 Commits

Author SHA1 Message Date
Dennis Guse c159e1eba4 Version v3.0.1 2019-08-16 19:53:38 +02:00
Dennis Guse 420d538a73 Preferences: Import all/Export all has now transparent background and back button works as desired. 2019-08-16 18:53:26 +02:00
Dennis Guse 36672d6857 Fixed ChartFragmentTest. 2019-08-16 07:49:47 +02:00
Dennis Guse 51dc6f8e8d Tests: accept permission automatically. 2019-08-16 07:13:58 +02:00
Dennis Guse 9a6c9eeace Set android:allowBackup=false to prevent backups as this also includes cloud-based storages (e.g., Google Drive). 2019-08-15 23:14:52 +02:00
Dennis Guse bfb7f781ec Enabled TravisCI (just building, no tests). 2019-08-15 23:03:30 +02:00
Dennis Guse 8ca24b877c Moved Import all/Export all functionality to Settings. 2019-08-15 22:38:51 +02:00
Dennis Guse 01ef5cfa6f All test are now executable (but all are not working). 2019-08-14 18:03:48 +02:00
Dennis Guse 08825fcf04 Cleanup: typo. 2019-08-14 08:05:37 +02:00
Dennis Guse 4f5e87f87d Enabled some TrackDataHub tests. 2019-08-14 08:05:00 +02:00
Dennis Guse 45b1fafdd5 Fixed test and implementation of DescriptionGenerator. 2019-08-13 23:04:09 +02:00
Dennis Guse e89258c173 Fixed some tests by using the application context.
Fixed some tests by using the application context.

Fixed some tests by using the application context.

Fixed some tests by using the application context.
2019-08-13 22:49:41 +02:00
Dennis Guse d47d37afd2 Import/Exporter tests now compile (do not work). 2019-08-13 22:09:56 +02:00
Dennis Guse d9effc80a7 Enabled TrackRecordingServiceTest (mainly failing). 2019-08-13 20:59:29 +02:00
Dennis Guse 03d2b05ffd Allows SharedPreferences to contain int as well as String. 2019-08-13 20:56:07 +02:00
Dennis Guse e2cac6ee60 More tests executable (but not passing). 2019-08-12 18:47:48 +02:00
Dennis Guse f749ddb5ae Cleanup. 2019-08-12 18:47:48 +02:00
DanielJorn 6bb409fed0 Fixed typo in Readme.md 2019-08-11 09:25:53 +02:00
Dennis Guse 6da2d9d104 Export to KML and KMZ as requested by user. 2019-08-11 09:07:31 +02:00
Dennis Guse cd267123e7 KML export: write close tag for last segment.
Closes #21.
2019-08-10 14:00:38 +02:00
Dennis Guse 617ee719c8 Add heart rate sensor UI for landscape (BUG: still not visible). 2019-08-07 07:26:39 +02:00
Dennis Guse 4edad610ee Moved SensorSettingsActivity into SettingsActivity. 2019-08-06 20:07:22 +02:00
Dennis Guse fda95bbec9 Integrated ChartSettingsActivity in SettingsActivity. 2019-08-06 19:52:57 +02:00
Dennis Guse 1028e09888 Removed actionbar back button.
It was complicating the code base too much.
2019-08-06 19:46:01 +02:00
Dennis Guse 4abd07694c Use a custom dialog to reset preferences. 2019-08-06 19:08:11 +02:00
Dennis Guse a53b3288f7 BREAKING CHANGE: Moved to getDefaultSharedPreferences(). 2019-08-06 18:34:07 +02:00
Dennis Guse 7ba771c417 Moved sharedPreferences accessor to PreferenceUtils. 2019-08-06 18:18:25 +02:00
Dennis Guse 2a145b4791 Cleanup list preferences. 2019-08-06 07:47:37 +02:00
Dennis Guse cf90462772 PreferenceList: let summary be set by Android.
Some preferences used customized summaries - these were also removed.
2019-08-06 07:41:17 +02:00
Dennis Guse d9a6701260 Initialize preferences with default values. 2019-08-06 07:41:17 +02:00
Dennis Guse 1dc9558541 Use Androidx to get version code from manifest. 2019-08-05 22:21:44 +02:00
Dennis Guse 972de5f92e Photo size for KMZ exports is not configurable anymore. 2019-08-05 22:12:40 +02:00
Dennis Guse 28270b202e Hide activityTypeLabel in statistic summary. 2019-08-05 22:05:49 +02:00
153 changed files with 1863 additions and 2936 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
+12
View File
@@ -0,0 +1,12 @@
language: android
dist: trusty
android:
components:
- build-tools-28.0.3
- android-28
script:
- ./gradlew build
#TODO: When all tests are passing, CI should also execute them.
#- ./gradlew connectedCheck
+2 -2
View File
@@ -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).
+17 -5
View File
@@ -72,16 +72,28 @@ android {
}
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'
}
@@ -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);
}
@@ -16,41 +16,42 @@
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();
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 +61,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 +118,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 +138,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);
}
}
@@ -20,65 +20,65 @@ 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 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 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.Vector;
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.stats.TripStatistics;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* A unit test for {@link ContentProviderUtilsImpl}.
*
* @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 +93,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 +176,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 +197,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 +234,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 +295,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 +308,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 +318,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 +328,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 +345,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 +381,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 +396,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 +433,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);
@@ -473,6 +470,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);
@@ -528,6 +526,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 +546,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 +577,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 +606,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 +623,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 +642,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 +660,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 +735,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 +748,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 +761,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 +777,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 +793,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 +805,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 +816,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 +831,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 +840,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 +876,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 +892,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 +905,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());
}
}
@@ -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>";
+ "Recorded: " + StringUtils.formatDateTime(context, START_TIME) + "<br>";
Assert.assertEquals(expected, descriptionGenerator.generateTrackDescription(track, null, null, 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_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_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_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_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;
// }
}
/**
@@ -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;
}
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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;
}
}
@@ -16,28 +16,31 @@
package de.dennisguse.opentracks.services;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
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 +49,99 @@ 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;
}
};
mockContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
// Set the context
setContext(context);
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 +153,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 +174,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 +195,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 +250,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 +302,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 +334,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 +547,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 +589,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 +605,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 +617,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;
@@ -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);
}
}
@@ -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));
}
}
+11 -15
View File
@@ -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="2947"
android:versionName="v3.0.1"> <!-- 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=".InitPreferences"
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" />
@@ -116,13 +119,7 @@ limitations under the License.
android:name="android.app.searchable"
android:resource="@xml/search" />
</activity>
<activity
android:name=".TrackDetailActivity"
android:parentActivityName=".TrackListActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".TrackListActivity" />
</activity>
<activity android:name=".TrackDetailActivity" />
<activity android:name=".TrackEditActivity" />
<activity android:name=".TrackListActivity">
<intent-filter>
@@ -133,11 +130,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
@@ -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();
}
}
@@ -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() {
}
@@ -0,0 +1,15 @@
package de.dennisguse.opentracks;
import android.app.Application;
import de.dennisguse.opentracks.util.PreferencesUtils;
public class InitPreferences extends Application {
@Override
public void onCreate() {
super.onCreate();
//Set default values of preferences on first start.
PreferencesUtils.resetPreferences(this, false);
}
}
@@ -129,7 +129,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;
@@ -168,7 +168,7 @@ 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);
@@ -41,6 +41,10 @@ import androidx.core.app.TaskStackBuilder;
import androidx.core.content.FileProvider;
import androidx.viewpager.widget.ViewPager;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import de.dennisguse.opentracks.content.ContentProviderUtils;
import de.dennisguse.opentracks.content.Track;
import de.dennisguse.opentracks.content.TrackDataHub;
@@ -63,10 +67,6 @@ 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.
*
@@ -193,7 +193,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);
@@ -292,20 +292,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);
@@ -392,13 +378,11 @@ public class TrackDetailActivity extends AbstractTrackActivity implements Choose
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);
}
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
@@ -26,7 +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;
@@ -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 AbstractTrackActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller {
private static final String TAG = TrackListActivity.class.getSimpleName();
private static final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
@@ -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
@@ -269,7 +261,7 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
}
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);
@@ -382,11 +374,6 @@ 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);
@@ -396,8 +383,6 @@ public class TrackListActivity extends AbstractTrackActivity implements FileType
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 +442,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;
@@ -522,26 +497,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 +535,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);
}
@@ -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.
*
@@ -354,8 +354,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,6 +24,7 @@ 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 +38,7 @@ public class DataSource {
public DataSource(Context context) {
contentResolver = context.getContentResolver();
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
}
/**
@@ -20,12 +20,12 @@ import android.content.Context;
import androidx.annotation.VisibleForTesting;
import java.util.Vector;
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
@@ -51,7 +51,7 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
// 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);
@@ -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());
@@ -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,7 +63,6 @@ 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();
String[] choices = new String[trackFileFormats.length];
for (int i = 0; i < choices.length; i++) {
@@ -73,14 +70,35 @@ public class FileTypeDialogFragment extends AbstractDialogFragment {
choices[i] = getString(optionId, trackFileFormat.name(),
FileUtils.getPathDisplayName(trackFileFormat.getExtension()));
}
return new AlertDialog.Builder(getActivity()).setNegativeButton(R.string.generic_cancel, null)
return new AlertDialog.Builder(getActivity())
.setCancelable(true)
.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(TrackFileFormat.values()[position]);
}
}).setSingleChoiceItems(choices, 0, null).setTitle(titleId).create();
})
.setSingleChoiceItems(choices, 0, null)
.setTitle(titleId)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
onDismissed();
}
})
.create();
}
protected void onDismissed() {
dismiss();
caller.onDismissed();
}
/**
@@ -93,6 +111,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();
}
}
@@ -4,12 +4,12 @@ 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.io.file.exporter.GpxTrackWriter;
import de.dennisguse.opentracks.io.file.exporter.KmlTrackWriter;
import de.dennisguse.opentracks.io.file.exporter.TrackWriter;
import de.dennisguse.opentracks.R;
import java.util.Locale;
/**
* Definition of all possible track formats.
@@ -17,13 +17,19 @@ import java.util.Locale;
public enum TrackFileFormat implements Parcelable {
KML {
@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);
}
},
KMZ {
@Override
public TrackWriter newTrackWriter(Context context, boolean multiple) {
return new KmlTrackWriter(context, multiple, true);
}
},
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));
}
};
@@ -55,9 +61,8 @@ public enum TrackFileFormat implements Parcelable {
*
* @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);
public abstract TrackWriter newTrackWriter(Context context, boolean multiple);
/**
* Returns the mime type for each format.
@@ -16,22 +16,22 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
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,20 +39,11 @@ import java.io.File;
*
* @author Rodrigo Damazio
*/
public class SaveActivity extends Activity {
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";
public class ExportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
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;
@@ -64,55 +55,46 @@ 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);
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 SaveAsyncTask before
// Object retained = getLastNonConfigurationInstance();
// if (retained instanceof SaveAsyncTask) {
// saveAsyncTask = (SaveAsyncTask) retained;
// saveAsyncTask.setActivity(this);
// } else {
saveAsyncTask = new SaveAsyncTask(this, trackFileFormat, directory);
saveAsyncTask.execute();
// }
}
@Override
public Object onRetainNonConfigurationInstance() {
saveAsyncTask.setActivity(null);
return saveAsyncTask;
public void onDismissed() {
finish();
}
@Override
@@ -126,6 +108,7 @@ public class SaveActivity extends Activity {
public void onCancel(DialogInterface dialog) {
saveAsyncTask.cancel(true);
dialog.dismiss();
onDismissed();
}
}, directoryDisplayName);
return progressDialog;
@@ -152,35 +135,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 +163,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);
}
@@ -53,8 +53,7 @@ public class FileTrackExporter implements TrackExporter {
* @param trackWriter the track writer
* @param trackExporterListener the track export listener
*/
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks,
TrackWriter trackWriter, TrackExporterListener trackExporterListener) {
public FileTrackExporter(ContentProviderUtils contentProviderUtils, Track[] tracks, TrackWriter trackWriter, 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);
@@ -22,6 +22,12 @@ import android.net.Uri;
import androidx.annotation.VisibleForTesting;
import java.io.File;
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;
@@ -32,12 +38,6 @@ 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 +56,15 @@ 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 isKMZ;
private final DescriptionGenerator descriptionGenerator;
private final ContentProviderUtils contentProviderUtils;
@@ -81,15 +76,15 @@ 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));
public KmlTrackWriter(Context context, boolean multiple, boolean isKMZ) {
this(context, multiple, isKMZ, new DescriptionGeneratorImpl(context));
}
@VisibleForTesting
KmlTrackWriter(Context context, boolean multiple, boolean playTrack, DescriptionGenerator descriptionGenerator) {
KmlTrackWriter(Context context, boolean multiple, boolean isKMZ, DescriptionGenerator descriptionGenerator) {
this.context = context;
this.multiple = multiple;
this.playTrack = playTrack;
this.isKMZ = isKMZ;
this.descriptionGenerator = descriptionGenerator;
this.contentProviderUtils = ContentProviderUtils.Factory.get(context);
}
@@ -124,9 +119,7 @@ public class KmlTrackWriter implements TrackWriter {
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>");
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);
@@ -151,9 +144,7 @@ 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><name>" + StringUtils.formatCData(context.getString(R.string.track_markers, track.getName())) + "</name>");
printWriter.println("<open>1</open>");
}
}
@@ -258,8 +249,7 @@ public class KmlTrackWriter implements TrackWriter {
@Override
public void writeLocation(Location location) {
if (printWriter != null) {
printWriter.println(
"<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
printWriter.println("<when>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when>");
printWriter.println("<gx:coord>" + getCoordinates(location, " ") + "</gx:coord>");
if (location instanceof SensorDataSetLocation) {
SensorDataSet sensorDataSet = ((SensorDataSetLocation) location).getSensorDataSet();
@@ -306,10 +296,8 @@ public class KmlTrackWriter implements TrackWriter {
if (location != null) {
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>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
writeCategory(category);
printWriter.println("<Point>");
@@ -330,13 +318,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) {
private void writePhotoOverlay(String name, String category, String description, String styleName, Location location, String photoUrl, float heading) {
if (location != null) {
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 +330,15 @@ 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>" + StringUtils.formatDateTimeIso8601(location.getTime()) + "</when></TimeStamp>");
printWriter.println("<styleUrl>#" + styleName + "</styleUrl>");
writeCategory(category);
if (playTrack) {
if (isKMZ) {
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.println("<Icon><href>" + KmzTrackExporter.KMZ_IMAGES_DIR + File.separatorChar + uri.getLastPathSegment() + "</href></Icon>");
}
printWriter.print("<ViewVolume>");
@@ -411,8 +396,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 +425,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>");
}
@@ -18,17 +18,9 @@ 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,6 +28,10 @@ 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.
*
@@ -53,7 +49,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 +56,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 +130,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];
@@ -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,12 +35,6 @@ 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.
@@ -44,14 +44,11 @@ import java.io.IOException;
public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = SaveAsyncTask.class.getSimpleName();
private final long[] trackIds;
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 +66,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 SaveAsyncTask(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 +86,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 +106,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,24 +122,24 @@ 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);
}
}
@@ -180,31 +154,25 @@ public class SaveAsyncTask extends AsyncTask<Void, Integer, Boolean> {
}
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),
trackFileFormat.newTrackWriter(context, tracks.length > 1),
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) {
//Update the progress dialog once every 500 points.
if (number % 500 == 0) {
publishProgress(number, max);
}
}
});
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks, context) : fileTrackExporter;
boolean useKmz = trackFileFormat == TrackFileFormat.KMZ;
String extension = useKmz ? KmzTrackExporter.KMZ_EXTENSION : trackFileFormat.getExtension();
TrackExporter trackExporter = useKmz ? new KmzTrackExporter(contentProviderUtils, fileTrackExporter, tracks) : fileTrackExporter;
String fileName = FileUtils.buildUniqueFileName(directory, track.getName(), extension);
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 +186,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;
}
}
@@ -16,27 +16,23 @@
package de.dennisguse.opentracks.io.file.importer;
import android.app.Activity;
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.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.core.app.TaskStackBuilder;
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,10 +40,7 @@ import java.io.File;
*
* @author Rodrigo Damazio
*/
public class ImportActivity extends Activity {
public static final String EXTRA_IMPORT_ALL = "import_all";
public static final String EXTRA_TRACK_FILE_FORMAT = "track_file_format";
public class ImportActivity extends FragmentActivity implements FileTypeDialogFragment.FileTypeCaller {
private static final String TAG = ImportActivity.class.getSimpleName();
private static final int DIALOG_PROGRESS_ID = 0;
@@ -56,8 +49,6 @@ public class ImportActivity extends Activity {
private ImportAsyncTask importAsyncTask;
private ProgressDialog progressDialog;
private boolean importAll;
// the path on the external storage to import
private String directoryDisplayName;
@@ -67,16 +58,19 @@ 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);
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) {
Intent intent = getIntent();
importAll = intent.getBooleanExtra(EXTRA_IMPORT_ALL, false);
TrackFileFormat trackFileFormat = intent.getParcelableExtra(EXTRA_TRACK_FILE_FORMAT);
if (trackFileFormat == null) {
trackFileFormat = TrackFileFormat.GPX;
}
@@ -86,49 +80,31 @@ public class ImportActivity extends Activity {
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 SaveAsyncTask 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 +117,7 @@ public class ImportActivity extends Activity {
public void onCancel(DialogInterface dialog) {
importAsyncTask.cancel(true);
dialog.dismiss();
onDismissed();
}
}, directoryDisplayName);
return progressDialog;
@@ -171,21 +148,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 +175,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,6 +22,14 @@ import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
import android.util.Log;
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;
@@ -29,14 +37,6 @@ 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;
/**
* AsyncTask to import files from the external storage.
@@ -46,7 +46,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 +68,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 +91,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);
}
}
@@ -155,7 +151,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 +159,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);
}
}
@@ -215,26 +211,24 @@ 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) {
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);
}
}
}
} 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;
@@ -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
@@ -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.
@@ -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));
}
@@ -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,33 @@
package de.dennisguse.opentracks.settings;
import android.content.Context;
import android.content.DialogInterface;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
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
protected View onCreateDialogView() {
return super.onCreateDialogView();
}
@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,35 @@ 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.StringUtils;
/**
* 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 +57,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 +74,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);
}
}
@@ -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;
@@ -94,6 +93,10 @@ public class PreferencesUtils {
private PreferencesUtils() {
}
public static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
/**
* Gets a preference key
*
@@ -112,7 +115,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 +127,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 +141,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 +166,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 +179,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 +191,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 +205,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 +217,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 +250,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);
}
}
@@ -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) {
@@ -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;
/**
@@ -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;
+30 -2
View File
@@ -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" />
<View
@@ -48,7 +48,7 @@ limitations under the License.
android:layout_height="wrap_content">
<TableLayout
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<!-- Speed and average speed -->
@@ -138,5 +138,33 @@ limitations under the License.
</TableLayout>
</LinearLayout>
</LinearLayout>
<!-- Sensor data: heart rate-->
<View
android:id="@+id/stats_sensor_separator"
style="@style/StatsHorizontalLine" />
<LinearLayout
android:id="@+id/stats_sensor_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include
android:id="@+id/stats_sensor_heart_rate_value"
layout="@layout/stats_large_item" />
<LinearLayout style="@style/StatsSmallGroupContainer">
<TableLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<include
android:id="@+id/stats_sensor_heart_rate_sensor"
layout="@layout/stats_tiny_item" />
</TableLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</merge>
+1 -1
View File
@@ -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 -->
+1 -1
View File
@@ -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 -->
-8
View File
@@ -34,14 +34,6 @@ limitations under the License.
android:id="@+id/track_list_aggregated_statistics"
android:icon="@drawable/ic_menu_aggregated_statistics"
android:title="@string/menu_aggregated_statistics" />
<item
android:id="@+id/track_list_export_all"
android:icon="@drawable/ic_menu_export"
android:title="@string/menu_export_all" />
<item
android:id="@+id/track_list_import_all"
android:icon="@drawable/ic_menu_import"
android:title="@string/menu_import_all" />
<item
android:id="@+id/track_list_delete_all"
android:icon="@drawable/ic_menu_delete"
-3
View File
@@ -231,9 +231,6 @@
<string name="settings_not_while_recording">‏‮Not‬‏ ‏‮available‬‏ ‏‮while‬‏ ‏‮recording‬‏ ‏‮a‬‏ ‏‮track‬‏</string>
<string name="settings_show">‏‮Show‬‏</string>
<string name="settings_advanced">‏‮Advanced‬‏</string>
<string name="settings_advanced_photo_size_original">‏‮Original‬‏ ‏‮size‬‏</string>
<string name="settings_advanced_photo_size_summary">‏‮Scale‬‏ ‏‮photo‬‏ ‏‮to‬‏ %1$s ‏‮for‬‏ ‏‮export‬‏</string>
<string name="settings_advanced_photo_size_title">‏‮Photo‬‏ ‏‮size‬‏</string>
<string name="settings_chart_by_distance">‏‮By‬‏ ‏‮distance‬‏</string>
<string name="settings_chart_by_time">‏‮By‬‏ ‏‮time‬‏</string>
<string name="settings_chart_x_axis">‏‮X‬‏ ‏‮axis‬‏</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">غير متاح أثناء تسجيل مسار</string>
<string name="settings_show">إظهار</string>
<string name="settings_advanced">خيارات متقدمة</string>
<string name="settings_advanced_photo_size_original">الحجم الأصلي</string>
<string name="settings_advanced_photo_size_summary">تغيير حجم الصورة إلى‏ %1$s‏ لتصديرها</string>
<string name="settings_advanced_photo_size_title">حجم الصورة</string>
<string name="settings_chart_by_distance">بحسب المسافة</string>
<string name="settings_chart_by_time">بحسب الوقت</string>
<string name="settings_chart_x_axis">المحور س</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">Не е налице при записване на маршрут.</string>
<string name="settings_show">Показване</string>
<string name="settings_advanced">Разширени</string>
<string name="settings_advanced_photo_size_original">Оригинален размер</string>
<string name="settings_advanced_photo_size_summary">Мащабиране на снимката до %1$s за експортиране</string>
<string name="settings_advanced_photo_size_title">Размер на снимката</string>
<string name="settings_chart_by_distance">По разстояние</string>
<string name="settings_chart_by_time">По време</string>
<string name="settings_chart_x_axis">Ос X</string>
-3
View File
@@ -231,9 +231,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -231,9 +231,6 @@ es vegi el cel. Torneu-ho a provar més tard.</string>
<string name="settings_not_while_recording">No està disponible durant l\'enregistrament d\'una ruta.</string>
<string name="settings_show">Mostra\'n més</string>
<string name="settings_advanced">Avançades</string>
<string name="settings_advanced_photo_size_original">Mida original</string>
<string name="settings_advanced_photo_size_summary">Modifica la mida de la foto a %1$s per exportar-la.</string>
<string name="settings_advanced_photo_size_title">Mida de la foto</string>
<string name="settings_chart_by_distance">Per distància</string>
<string name="settings_chart_by_time">Per temps</string>
<string name="settings_chart_x_axis">Eix X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Při záznamu trasy není tato funkce dostupná.</string>
<string name="settings_show">Zobrazit</string>
<string name="settings_advanced">Pokročilé</string>
<string name="settings_advanced_photo_size_original">Původní velikost</string>
<string name="settings_advanced_photo_size_summary">Změnit velikost fotografie na %1$s za účelem exportu</string>
<string name="settings_advanced_photo_size_title">Velikost fotografie</string>
<string name="settings_chart_by_distance">Podle vzdálenosti</string>
<string name="settings_chart_by_time">Podle doby</string>
<string name="settings_chart_x_axis">Osa X</string>
-3
View File
@@ -231,9 +231,6 @@
<string name="settings_not_while_recording">Ikke tilgængelig ved registrering af rute</string>
<string name="settings_show">Vis</string>
<string name="settings_advanced">Avanceret</string>
<string name="settings_advanced_photo_size_original">Oprindelig størrelse</string>
<string name="settings_advanced_photo_size_summary">Skaler billede til %1$s til eksport</string>
<string name="settings_advanced_photo_size_title">Billedstørrelse</string>
<string name="settings_chart_by_distance">Efter afstand</string>
<string name="settings_chart_by_time">Efter tid</string>
<string name="settings_chart_x_axis">X-akse</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">Während der Aufzeichnung eines Tracks nicht verfügbar</string>
<string name="settings_show">Anzeigen</string>
<string name="settings_advanced">Erweitert</string>
<string name="settings_advanced_photo_size_original">Originalgröße</string>
<string name="settings_advanced_photo_size_summary">Foto für Export auf %1$s skalieren</string>
<string name="settings_advanced_photo_size_title">Fotogröße</string>
<string name="settings_chart_by_distance">Nach Strecke</string>
<string name="settings_chart_by_time">Nach Zeit</string>
<string name="settings_chart_x_axis">x-Achse</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">Während der Aufzeichnung eines Tracks nicht verfügbar</string>
<string name="settings_show">Anzeigen</string>
<string name="settings_advanced">Erweitert</string>
<string name="settings_advanced_photo_size_original">Originalgröße</string>
<string name="settings_advanced_photo_size_summary">Foto für Export auf %1$s skalieren</string>
<string name="settings_advanced_photo_size_title">Fotogröße</string>
<string name="settings_chart_by_distance">Nach Strecke</string>
<string name="settings_chart_by_time">Nach Zeit</string>
<string name="settings_chart_x_axis">x-Achse</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">Während der Aufzeichnung eines Tracks nicht verfügbar</string>
<string name="settings_show">Anzeigen</string>
<string name="settings_advanced">Erweitert</string>
<string name="settings_advanced_photo_size_original">Originalgröße</string>
<string name="settings_advanced_photo_size_summary">Foto für Export auf %1$s skalieren</string>
<string name="settings_advanced_photo_size_title">Fotogröße</string>
<string name="settings_chart_by_distance">Nach Strecke</string>
<string name="settings_chart_by_time">Nach Zeit</string>
<string name="settings_chart_x_axis">x-Achse</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">Η επιλογή δεν είναι διαθέσιμη κατά την εγγραφή ίχνους</string>
<string name="settings_show">Εμφάνιση</string>
<string name="settings_advanced">Προηγμένες</string>
<string name="settings_advanced_photo_size_original">Αρχικό μέγεθος</string>
<string name="settings_advanced_photo_size_summary">Αλλαγή μεγέθους φωτογραφίας σε %1$s για εξαγωγή</string>
<string name="settings_advanced_photo_size_title">Μέγεθος φωτογραφίας</string>
<string name="settings_chart_by_distance">Κατά απόσταση</string>
<string name="settings_chart_by_time">Κατά χρόνο</string>
<string name="settings_chart_x_axis">Άξονας Χ</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -231,9 +231,6 @@
<string name="settings_not_while_recording">[Ñöţ åvåîļåɓļé ŵĥîļé ŕéçöŕðîñĝ å ţŕåçķ one two three four five six seven eight]</string>
<string name="settings_show">[Šĥöŵ one]</string>
<string name="settings_advanced">[Åðvåñçéð one]</string>
<string name="settings_advanced_photo_size_original">[Öŕîĝîñåļ šîžé one two]</string>
<string name="settings_advanced_photo_size_summary">[Šçåļé þĥöţö ţö ᐅ%1$sᐊ ƒöŕ éxþöŕţ one two three four five six seven]</string>
<string name="settings_advanced_photo_size_title">[Þĥöţö šîžé one two]</string>
<string name="settings_chart_by_distance">[Bý ðîšţåñçé one two]</string>
<string name="settings_chart_by_time">[Bý ţîmé one]</string>
<string name="settings_chart_x_axis">[X åxîš one]</string>
-3
View File
@@ -231,9 +231,6 @@
<string name="settings_not_while_recording">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎Not available while recording a track‎‏‎‎‏‎</string>
<string name="settings_show">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎Show‎‏‎‎‏‎</string>
<string name="settings_advanced">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎Advanced‎‏‎‎‏‎</string>
<string name="settings_advanced_photo_size_original">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎Original size‎‏‎‎‏‎</string>
<string name="settings_advanced_photo_size_summary">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‎Scale photo to ‎‏‎‎‏‏‎%1$s‎‏‎‎‏‏‏‎ for export‎‏‎‎‏‎</string>
<string name="settings_advanced_photo_size_title">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎Photo size‎‏‎‎‏‎</string>
<string name="settings_chart_by_distance">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎By distance‎‏‎‎‏‎</string>
<string name="settings_chart_by_time">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎By time‎‏‎‎‏‎</string>
<string name="settings_chart_x_axis">‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎X axis‎‏‎‎‏‎</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Not available while recording a track</string>
<string name="settings_show">Show</string>
<string name="settings_advanced">Advanced</string>
<string name="settings_advanced_photo_size_original">Original size</string>
<string name="settings_advanced_photo_size_summary">Scale photo to %1$s for export</string>
<string name="settings_advanced_photo_size_title">Photo size</string>
<string name="settings_chart_by_distance">By distance</string>
<string name="settings_chart_by_time">By time</string>
<string name="settings_chart_x_axis">X axis</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">No está disponible durante la grabación de un recorrido.</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Opciones avanzadas</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla.</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -229,9 +229,6 @@
<string name="settings_not_while_recording">No disponible mientras se graba una ruta</string>
<string name="settings_show">Mostrar</string>
<string name="settings_advanced">Avanzado</string>
<string name="settings_advanced_photo_size_original">Tamaño original</string>
<string name="settings_advanced_photo_size_summary">Cambia el tamaño de la foto a %1$s para exportarla</string>
<string name="settings_advanced_photo_size_title">Tamaño de foto</string>
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
-3
View File
@@ -228,9 +228,6 @@
<string name="settings_not_while_recording">Raja salvestamise ajal ei ole kättesaadav</string>
<string name="settings_show">Kuva</string>
<string name="settings_advanced">Täpsemad seaded</string>
<string name="settings_advanced_photo_size_original">Originaalsuurus</string>
<string name="settings_advanced_photo_size_summary">Eksportimiseks skaleerige foto suurusele %1$s</string>
<string name="settings_advanced_photo_size_title">Foto suurus</string>
<string name="settings_chart_by_distance">Vahemaa järgi</string>
<string name="settings_chart_by_time">Aja järgi</string>
<string name="settings_chart_x_axis">X-telg</string>

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