forked from upstream-mirrors/OpenTracks
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 928781391d | |||
| 05cd672464 | |||
| d3109bbc37 | |||
| eabdb7c721 | |||
| ad990e8cd7 | |||
| db3eef0104 | |||
| c4f1ba7962 | |||
| 7451fbd413 | |||
| b6c55de55a | |||
| dd3ee6d99e | |||
| bbb6a7c22c | |||
| dc1b50a7bf | |||
| 21bfb88e0b | |||
| aaab8adce9 |
@@ -0,0 +1,21 @@
|
|||||||
|
#https://github.com/marketplace/actions/android-emulator-runner
|
||||||
|
name: Test
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
api-level: [21, 29]
|
||||||
|
target: [default]
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: run tests
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
arch: x86_64
|
||||||
|
script: ./gradlew connectedCheck
|
||||||
+10
-10
@@ -22,18 +22,18 @@ android:
|
|||||||
- android-29
|
- android-29
|
||||||
|
|
||||||
# Emulator Management: Create, Start and Wait
|
# Emulator Management: Create, Start and Wait
|
||||||
#install:
|
install:
|
||||||
# - echo y | sdkmanager "platform-tools"
|
- echo y | sdkmanager "platform-tools"
|
||||||
# - echo y | sdkmanager --channel=4 "emulator"
|
- echo y | sdkmanager --channel=4 "emulator"
|
||||||
# - echo y | sdkmanager "system-images;android-$API;$EMU_FLAVOR;$ABI"
|
- echo y | sdkmanager "system-images;android-$API;$EMU_FLAVOR;$ABI"
|
||||||
# - echo no | avdmanager create avd --force -n test -k "system-images;android-$API;$EMU_FLAVOR;$ABI" -c 10M
|
- echo no | avdmanager create avd --force -n test -k "system-images;android-$API;$EMU_FLAVOR;$ABI" -c 10M
|
||||||
# - emulator -verbose -avd test -no-accel -no-snapshot -no-window -camera-back none -camera-front none -selinux permissive -qemu -m 2048 &
|
- emulator -verbose -avd test -no-accel -no-snapshot -no-window -camera-back none -camera-front none -selinux permissive -qemu -m 2048 &
|
||||||
# - android-wait-for-emulator
|
- android-wait-for-emulator
|
||||||
# - adb shell input keyevent 82 &
|
- adb shell input keyevent 82 &
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./gradlew clean build
|
- ./gradlew build
|
||||||
# - ./gradlew connectedCheck
|
- ./gradlew connectedCheck
|
||||||
|
|
||||||
sbefore_cache:
|
sbefore_cache:
|
||||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||||
|
|||||||
+3
-3
@@ -14,15 +14,15 @@ For the time being, the OpenTracks Data API can be used to implement mainly cust
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
OpenTracks by default does not expose any data to 3rd-party applications.
|
OpenTracks by default does not expose any data to 3rd-party applications.
|
||||||
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s.
|
On the request of the user (i.e., pressing a button), an Intent is created that exposes the `Track` data (it's summary) as well as the `TrackPoint`s of one or more selected tracks.
|
||||||
For each, one URI is provided.
|
For each, one URI is provided.
|
||||||
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
|
These URIs notify observers if on content changes (e.g., a new `TrackPoint` was added).
|
||||||
|
|
||||||
Limitations:
|
Limitations:
|
||||||
* only tracks selected by the user is exposed,
|
* only tracks selected by the user are exposed,
|
||||||
* `Waypoints` are not exposed,
|
* `Waypoints` are not exposed,
|
||||||
* access is only granted temporarily and automatically revoked (see `FLAG_GRANT_READ_URI_PERMISSION`),
|
* access is only granted temporarily and automatically revoked (see `FLAG_GRANT_READ_URI_PERMISSION`),
|
||||||
* no write access is impossible, and
|
* no write access is possible, and
|
||||||
* neither track recordings can be started, paused, or stopped.
|
* neither track recordings can be started, paused, or stopped.
|
||||||
|
|
||||||
All required information can be found in `IntentDashboardUtils.java`.
|
All required information can be found in `IntentDashboardUtils.java`.
|
||||||
|
|||||||
+2
-1
@@ -98,6 +98,7 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.2.0'
|
androidTestImplementation 'androidx.test:core:1.2.0'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
@@ -109,5 +110,5 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation 'androidx.test:core:1.2.0'
|
testImplementation 'androidx.test:core:1.2.0'
|
||||||
testImplementation 'org.mockito:mockito-core:3.0.0'
|
testImplementation 'org.mockito:mockito-core:3.0.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
v3.4.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Dashboard API: multiple tracks
|
||||||
|
- Updated help
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Fixed crash on fragment re-creation
|
||||||
|
- When paused: prevent stopping app
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Enable testing via CI (Github Actions and TravisCI)
|
||||||
+11
-6
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.content.provider;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.content.ContentUris;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -29,7 +30,6 @@ import org.junit.Test;
|
|||||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider.DatabaseHelper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link CustomContentProvider}.
|
* Tests {@link CustomContentProvider}.
|
||||||
@@ -47,13 +47,13 @@ public class CustomContentProviderTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
context.deleteDatabase(DATABASE_NAME);
|
context.deleteDatabase(DATABASE_NAME);
|
||||||
db = (new DatabaseHelper(context, DATABASE_NAME)).getWritableDatabase();
|
db = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getWritableDatabase();
|
||||||
customContentProvider = new CustomContentProvider() {
|
customContentProvider = new CustomContentProvider() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link CustomContentProvider.DatabaseHelper#onCreate(SQLiteDatabase)}.
|
* Tests {@link CustomSQLiteOpenHelper#onCreate(SQLiteDatabase)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testDatabaseHelper_OnCreate() {
|
public void testDatabaseHelper_OnCreate() {
|
||||||
@@ -76,8 +76,13 @@ public class CustomContentProviderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetType() {
|
public void testGetType() {
|
||||||
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
Assert.assertEquals(TracksColumns.CONTENT_TYPE, customContentProvider.getType(TracksColumns.CONTENT_URI));
|
||||||
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI));
|
Assert.assertEquals(TracksColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TracksColumns.CONTENT_URI.buildUpon(), 1).build()));
|
||||||
|
|
||||||
|
Assert.assertEquals(TrackPointsColumns.CONTENT_TYPE, customContentProvider.getType(TrackPointsColumns.CONTENT_URI_BY_ID));
|
||||||
|
Assert.assertEquals(TrackPointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_TRACKID.buildUpon(), 1).build()));
|
||||||
|
|
||||||
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
Assert.assertEquals(WaypointsColumns.CONTENT_TYPE, customContentProvider.getType(WaypointsColumns.CONTENT_URI));
|
||||||
|
Assert.assertEquals(WaypointsColumns.CONTENT_ITEMTYPE, customContentProvider.getType(ContentUris.appendId(WaypointsColumns.CONTENT_URI.buildUpon(), 1).build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,7 +147,7 @@ public class CustomContentProviderTest {
|
|||||||
createTable(TrackPointsColumns.TABLE_NAME);
|
createTable(TrackPointsColumns.TABLE_NAME);
|
||||||
createTable(WaypointsColumns.TABLE_NAME);
|
createTable(WaypointsColumns.TABLE_NAME);
|
||||||
|
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
CustomSQLiteOpenHelper databaseHelper = new CustomSQLiteOpenHelper(context);
|
||||||
databaseHelper.onUpgrade(db, oldVersion, CustomContentProvider.DATABASE_VERSION);
|
databaseHelper.onUpgrade(db, oldVersion, CustomSQLiteOpenHelper.DATABASE_VERSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+12
-4
@@ -230,7 +230,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(1, tracksCursor.getCount());
|
Assert.assertEquals(1, tracksCursor.getCount());
|
||||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
Assert.assertEquals(10, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(1, waypointCursor.getCount());
|
Assert.assertEquals(1, waypointCursor.getCount());
|
||||||
@@ -239,7 +239,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// Check whether all have been deleted.
|
// Check whether all have been deleted.
|
||||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(0, tracksCursor.getCount());
|
Assert.assertEquals(0, tracksCursor.getCount());
|
||||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
Assert.assertEquals(0, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
@@ -265,7 +265,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(3, tracksCursor.getCount());
|
Assert.assertEquals(3, tracksCursor.getCount());
|
||||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
Cursor waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(1, waypointCursor.getCount());
|
Assert.assertEquals(1, waypointCursor.getCount());
|
||||||
@@ -274,7 +274,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// Check whether all data of a track has been deleted.
|
// Check whether all data of a track has been deleted.
|
||||||
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
Assert.assertEquals(2, tracksCursor.getCount());
|
Assert.assertEquals(2, tracksCursor.getCount());
|
||||||
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI, null, null, null, TrackPointsColumns._ID);
|
tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
Assert.assertEquals(20, tracksPointsCursor.getCount());
|
||||||
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
waypointCursor = contentResolver.query(WaypointsColumns.CONTENT_URI, null, null, null, WaypointsColumns._ID);
|
||||||
Assert.assertEquals(0, waypointCursor.getCount());
|
Assert.assertEquals(0, waypointCursor.getCount());
|
||||||
@@ -766,4 +766,12 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
contentProviderUtils.bulkInsertTrackPoint(track.getLocations().toArray(new Location[0]), track.getLocations().size(), track.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatIdListForUri() {
|
||||||
|
Assert.assertEquals("", ContentProviderUtils.formatIdListForUri(new long[]{}));
|
||||||
|
Assert.assertEquals("12", ContentProviderUtils.formatIdListForUri(new long[]{12}));
|
||||||
|
Assert.assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new long[]{42, 43, 44}));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ public class ChartFragmentTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
boolean chartByDistance = false;
|
boolean chartByDistance = false;
|
||||||
chartFragment = new ChartFragment(chartByDistance) {
|
chartFragment = (ChartFragment) ChartFragment.newInstance(chartByDistance);
|
||||||
};
|
|
||||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
||||||
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
chartFragment.setTripStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,10 @@ public class StringUtilsTest {
|
|||||||
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||||
calendar.set(year, month - 1, day, hour, minute, second);
|
calendar.set(year, month - 1, day, hour, minute, second);
|
||||||
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
calendar.set(GregorianCalendar.MILLISECOND, millisecond);
|
||||||
Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
|
||||||
|
// This comparision tends to be flaky (difference of 1ms)
|
||||||
|
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
||||||
|
Assert.assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ limitations under the License.
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="de.dennisguse.opentracks"
|
package="de.dennisguse.opentracks"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="3272"
|
android:versionCode="3287"
|
||||||
android:versionName="v3.3.2"> <!-- For FDroid. -->
|
android:versionName="v3.4.0"> <!-- For FDroid. -->
|
||||||
<!-- Permission to initialize services -->
|
<!-- Permission to initialize services -->
|
||||||
<application
|
<application
|
||||||
android:name=".Startup"
|
android:name=".Startup"
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import android.view.WindowManager;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentPagerAdapter;
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
@@ -46,8 +45,7 @@ import de.dennisguse.opentracks.content.TrackDataHub;
|
|||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.fragments.ChartDistanceFragment;
|
import de.dennisguse.opentracks.fragments.ChartFragment;
|
||||||
import de.dennisguse.opentracks.fragments.ChartTimeFragment;
|
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.StatsFragment;
|
import de.dennisguse.opentracks.fragments.StatsFragment;
|
||||||
@@ -206,17 +204,15 @@ public class TrackDetailActivity extends AbstractListActivity implements ChooseA
|
|||||||
@Override
|
@Override
|
||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 0:
|
|
||||||
return new StatsFragment();
|
|
||||||
case 1:
|
case 1:
|
||||||
return new ChartTimeFragment();
|
return ChartFragment.newInstance(false);
|
||||||
case 2:
|
case 2:
|
||||||
return new ChartDistanceFragment();
|
return ChartFragment.newInstance(true);
|
||||||
|
default: //0
|
||||||
|
return new StatsFragment();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
|
|||||||
@@ -111,8 +111,7 @@ class DataSourceManager {
|
|||||||
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
dataSource.registerContentObserver(WaypointsColumns.CONTENT_URI, waypointsTableObserver);
|
||||||
break;
|
break;
|
||||||
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
case SAMPLED_IN_TRACK_POINTS_TABLE:
|
||||||
dataSource.registerContentObserver(
|
dataSource.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, trackPointsTableObserver);
|
||||||
TrackPointsColumns.CONTENT_URI, trackPointsTableObserver);
|
|
||||||
break;
|
break;
|
||||||
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
case SAMPLED_OUT_TRACK_POINTS_TABLE:
|
||||||
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
// Do nothing. SAMPLED_OUT_POINT_UPDATES is mapped to POINT_UPDATES.
|
||||||
|
|||||||
@@ -29,15 +29,20 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
|||||||
public interface TrackPointsColumns extends BaseColumns {
|
public interface TrackPointsColumns extends BaseColumns {
|
||||||
|
|
||||||
String TABLE_NAME = "trackpoints";
|
String TABLE_NAME = "trackpoints";
|
||||||
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
Uri CONTENT_URI_BY_ID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME);
|
||||||
|
Uri CONTENT_URI_BY_TRACKID = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/" + TABLE_NAME + "/trackid");
|
||||||
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.trackpoint";
|
||||||
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.trackpoint";
|
||||||
String DEFAULT_SORT_ORDER = "_id";
|
String DEFAULT_SORT_ORDER = "_id";
|
||||||
|
|
||||||
// Columns
|
// Columns
|
||||||
String TRACKID = "trackid";
|
String TRACKID = "trackid";
|
||||||
|
|
||||||
String LONGITUDE = "longitude";
|
String LONGITUDE = "longitude";
|
||||||
String LATITUDE = "latitude";
|
String LATITUDE = "latitude";
|
||||||
|
double PAUSE_LATITUDE = 100.0;
|
||||||
|
double RESUME_LATITUDE = 200.0;
|
||||||
|
|
||||||
String TIME = "time";
|
String TIME = "time";
|
||||||
String ALTITUDE = "elevation";
|
String ALTITUDE = "elevation";
|
||||||
String ACCURACY = "accuracy";
|
String ACCURACY = "accuracy";
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ public class ContentProviderUtils {
|
|||||||
* Maximum number of waypoints that will be loaded at one time.
|
* Maximum number of waypoints that will be loaded at one time.
|
||||||
*/
|
*/
|
||||||
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
public static final int MAX_LOADED_WAYPOINTS_POINTS = 10000;
|
||||||
|
public static final String ID_SEPARATOR = ",";
|
||||||
|
|
||||||
private final IContentResolver contentResolver;
|
private final IContentResolver contentResolver;
|
||||||
private int defaultCursorBatchSize = 2000;
|
private int defaultCursorBatchSize = 2000;
|
||||||
@@ -181,13 +183,13 @@ public class ContentProviderUtils {
|
|||||||
* Deletes all tracks (including waypoints and track points).
|
* Deletes all tracks (including waypoints and track points).
|
||||||
*/
|
*/
|
||||||
public void deleteAllTracks(Context context) {
|
public void deleteAllTracks(Context context) {
|
||||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, null, null);
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null);
|
||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
|
||||||
// Delete tracks last since it triggers a database vaccum call
|
// Delete tracks last since it triggers a database vaccum call
|
||||||
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
|
||||||
|
|
||||||
File dir = FileUtils.getPhotoDir(context);
|
File dir = FileUtils.getPhotoDir(context);
|
||||||
deleteDirectoryRecurse(context, dir);
|
deleteDirectoryRecurse(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,11 +214,11 @@ public class ContentProviderUtils {
|
|||||||
private void deleteTrackPointsAndWaypoints(Context context, long trackId) {
|
private void deleteTrackPointsAndWaypoints(Context context, long trackId) {
|
||||||
String where = TrackPointsColumns.TRACKID + "=?";
|
String where = TrackPointsColumns.TRACKID + "=?";
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId)};
|
String[] selectionArgs = new String[]{Long.toString(trackId)};
|
||||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI, where, selectionArgs);
|
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, where, selectionArgs);
|
||||||
|
|
||||||
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
|
contentResolver.delete(WaypointsColumns.CONTENT_URI, WaypointsColumns.TRACKID + "=?",
|
||||||
new String[]{Long.toString(trackId)});
|
new String[]{Long.toString(trackId)});
|
||||||
deleteDirectoryRecurse(context, FileUtils.getPhotoDir(context, trackId));
|
deleteDirectoryRecurse(FileUtils.getPhotoDir(context, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,10 +226,10 @@ public class ContentProviderUtils {
|
|||||||
*
|
*
|
||||||
* @param dir the directory
|
* @param dir the directory
|
||||||
*/
|
*/
|
||||||
private void deleteDirectoryRecurse(Context context, File dir) {
|
private void deleteDirectoryRecurse(File dir) {
|
||||||
if (dir.exists() && dir.isDirectory()) {
|
if (dir != null && dir.exists() && dir.isDirectory()) {
|
||||||
for (File child : dir.listFiles()) {
|
for (File child : dir.listFiles()) {
|
||||||
deleteDirectoryRecurse(context, child);
|
deleteDirectoryRecurse(child);
|
||||||
}
|
}
|
||||||
dir.delete();
|
dir.delete();
|
||||||
}
|
}
|
||||||
@@ -659,7 +661,7 @@ public class ContentProviderUtils {
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
values[i] = createContentValues(locations[i], trackId);
|
values[i] = createContentValues(locations[i], trackId);
|
||||||
}
|
}
|
||||||
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
|
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI_BY_ID, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -846,7 +848,7 @@ public class ContentProviderUtils {
|
|||||||
* @return the content provider URI of the inserted track point
|
* @return the content provider URI of the inserted track point
|
||||||
*/
|
*/
|
||||||
public Uri insertTrackPoint(Location location, long trackId) {
|
public Uri insertTrackPoint(Location location, long trackId) {
|
||||||
return contentResolver.insert(TrackPointsColumns.CONTENT_URI, createContentValues(location, trackId));
|
return contentResolver.insert(TrackPointsColumns.CONTENT_URI_BY_ID, createContentValues(location, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -926,7 +928,7 @@ public class ContentProviderUtils {
|
|||||||
* @param sortOrder the sort order
|
* @param sortOrder the sort order
|
||||||
*/
|
*/
|
||||||
private Cursor getTrackPointCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
private Cursor getTrackPointCursor(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
return contentResolver.query(TrackPointsColumns.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
|
return contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, projection, selection, selectionArgs, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getDefaultCursorBatchSize() {
|
int getDefaultCursorBatchSize() {
|
||||||
@@ -942,4 +944,26 @@ public class ContentProviderUtils {
|
|||||||
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
|
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
|
||||||
this.defaultCursorBatchSize = defaultCursorBatchSize;
|
this.defaultCursorBatchSize = defaultCursorBatchSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an array of IDs as comma separated string value
|
||||||
|
*
|
||||||
|
* @param ids array with IDs
|
||||||
|
* @return comma separated list of ids
|
||||||
|
*/
|
||||||
|
public static String formatIdListForUri(long[] ids) {
|
||||||
|
StringBuilder idsPathSegment = new StringBuilder();
|
||||||
|
for (long id : ids) {
|
||||||
|
if (idsPathSegment.length() > 0) {
|
||||||
|
idsPathSegment.append(ID_SEPARATOR);
|
||||||
|
}
|
||||||
|
idsPathSegment.append(id);
|
||||||
|
}
|
||||||
|
return idsPathSegment.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] parseTrackIdsFromUri(Uri url) {
|
||||||
|
return TextUtils.split(url.getLastPathSegment(), ID_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-51
@@ -25,7 +25,6 @@ import android.database.Cursor;
|
|||||||
import android.database.SQLException;
|
import android.database.SQLException;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.database.sqlite.SQLiteQueryBuilder;
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -45,27 +44,25 @@ import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
|||||||
*/
|
*/
|
||||||
public abstract class CustomContentProvider extends ContentProvider {
|
public abstract class CustomContentProvider extends ContentProvider {
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final int DATABASE_VERSION = 23;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static final String DATABASE_NAME = "database.db";
|
|
||||||
|
|
||||||
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
private static final String TAG = CustomContentProvider.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String SQL_LIST_DELIMITER = ",";
|
||||||
|
|
||||||
private final UriMatcher uriMatcher;
|
private final UriMatcher uriMatcher;
|
||||||
|
|
||||||
private SQLiteDatabase db;
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
public CustomContentProvider() {
|
public CustomContentProvider() {
|
||||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME, UrlType.TRACKPOINTS.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath(), UrlType.TRACKPOINTS.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_BY_ID.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_ID.getPath() + "/#", UrlType.TRACKPOINTS_BY_ID.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.TABLE_NAME + "/#", UrlType.TRACKPOINTS_BY_TRACKID.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TrackPointsColumns.CONTENT_URI_BY_TRACKID.getPath() + "/*", UrlType.TRACKPOINTS_BY_TRACKID.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME, UrlType.TRACKS.ordinal());
|
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.TABLE_NAME + "/#", UrlType.TRACKS_BY_ID.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath(), UrlType.TRACKS.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.TABLE_NAME, UrlType.WAYPOINTS.ordinal());
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, TracksColumns.CONTENT_URI.getPath() + "/*", UrlType.TRACKS_BY_ID.ordinal());
|
||||||
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.TABLE_NAME + "/#", UrlType.WAYPOINTS_BY_ID.ordinal());
|
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath(), UrlType.WAYPOINTS.ordinal());
|
||||||
|
uriMatcher.addURI(ContentProviderUtils.AUTHORITY_PACKAGE, WaypointsColumns.CONTENT_URI.getPath() + "/#", UrlType.WAYPOINTS_BY_ID.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,7 +78,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean onCreate(Context context) {
|
boolean onCreate(Context context) {
|
||||||
DatabaseHelper databaseHelper = new DatabaseHelper(context);
|
CustomSQLiteOpenHelper databaseHelper = new CustomSQLiteOpenHelper(context);
|
||||||
try {
|
try {
|
||||||
db = databaseHelper.getWritableDatabase();
|
db = databaseHelper.getWritableDatabase();
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
@@ -200,11 +197,11 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID:
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
break;
|
||||||
case TRACKPOINTS_BY_TRACKID:
|
case TRACKPOINTS_BY_TRACKID:
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + "=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
break;
|
||||||
case TRACKS:
|
case TRACKS:
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
@@ -212,7 +209,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID:
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TracksColumns._ID + "=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
break;
|
||||||
case WAYPOINTS:
|
case WAYPOINTS:
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
@@ -220,7 +217,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case WAYPOINTS_BY_ID:
|
case WAYPOINTS_BY_ID:
|
||||||
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
queryBuilder.setTables(WaypointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(WaypointsColumns._ID + "=" + url.getPathSegments().get(1));
|
queryBuilder.appendWhere(WaypointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
throw new IllegalArgumentException("Unknown url " + url);
|
||||||
@@ -242,7 +239,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID:
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = TrackPointsColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -253,7 +250,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID:
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = TracksColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -264,7 +261,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case WAYPOINTS_BY_ID:
|
case WAYPOINTS_BY_ID:
|
||||||
table = WaypointsColumns.TABLE_NAME;
|
table = WaypointsColumns.TABLE_NAME;
|
||||||
whereClause = WaypointsColumns._ID + "=" + url.getPathSegments().get(1);
|
whereClause = WaypointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
@@ -289,8 +286,15 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
*
|
*
|
||||||
* @param url the url
|
* @param url the url
|
||||||
*/
|
*/
|
||||||
|
@NonNull
|
||||||
private UrlType getUrlType(Uri url) {
|
private UrlType getUrlType(Uri url) {
|
||||||
return UrlType.values()[uriMatcher.match(url)];
|
UrlType[] urlTypes = UrlType.values();
|
||||||
|
int matchIndex = uriMatcher.match(url);
|
||||||
|
if (0 <= matchIndex && matchIndex < urlTypes.length) {
|
||||||
|
return urlTypes[matchIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -328,7 +332,7 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
long rowId = db.insert(TrackPointsColumns.TABLE_NAME, TrackPointsColumns._ID, values);
|
||||||
if (rowId >= 0) {
|
if (rowId >= 0) {
|
||||||
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI.buildUpon(), rowId).build();
|
return ContentUris.appendId(TrackPointsColumns.CONTENT_URI_BY_ID.buildUpon(), rowId).build();
|
||||||
}
|
}
|
||||||
throw new SQLiteException("Failed to insert a track point " + url);
|
throw new SQLiteException("Failed to insert a track point " + url);
|
||||||
}
|
}
|
||||||
@@ -380,31 +384,4 @@ public abstract class CustomContentProvider extends ContentProvider {
|
|||||||
WAYPOINTS,
|
WAYPOINTS,
|
||||||
WAYPOINTS_BY_ID
|
WAYPOINTS_BY_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Database helper for creating and upgrading the database.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
static class DatabaseHelper extends SQLiteOpenHelper {
|
|
||||||
|
|
||||||
public DatabaseHelper(Context context) {
|
|
||||||
this(context, DATABASE_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public DatabaseHelper(Context context, String databaseName) {
|
|
||||||
super(context, databaseName, null, DATABASE_VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(SQLiteDatabase db) {
|
|
||||||
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
|
||||||
db.execSQL(TracksColumns.CREATE_TABLE);
|
|
||||||
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.WaypointsColumns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database helper for creating and upgrading the database.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final int DATABASE_VERSION = 23;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final String DATABASE_NAME = "database.db";
|
||||||
|
|
||||||
|
public CustomSQLiteOpenHelper(Context context) {
|
||||||
|
this(context, DATABASE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public CustomSQLiteOpenHelper(Context context, String databaseName) {
|
||||||
|
super(context, databaseName, null, DATABASE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(TrackPointsColumns.CREATE_TABLE);
|
||||||
|
db.execSQL(TracksColumns.CREATE_TABLE);
|
||||||
|
db.execSQL(WaypointsColumns.CREATE_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.fragments;
|
|
||||||
|
|
||||||
public class ChartDistanceFragment extends ChartFragment {
|
|
||||||
|
|
||||||
public ChartDistanceFragment() {
|
|
||||||
super(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,6 @@ import android.view.ViewGroup;
|
|||||||
import android.view.ViewGroup.LayoutParams;
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
@@ -56,9 +55,18 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public abstract class ChartFragment extends Fragment implements TrackDataListener {
|
public class ChartFragment extends Fragment implements TrackDataListener {
|
||||||
|
|
||||||
private static final String STATE_CHART_VIEW_BY_DISTANCE_KEY = "chartViewByDistance";
|
private static final String KEY_CHART_VIEW_BY_DISTANCE_KEY = "chartViewByDistance";
|
||||||
|
|
||||||
|
public static Fragment newInstance(boolean chartByDistance) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(KEY_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
||||||
|
|
||||||
|
ChartFragment chartFragment = new ChartFragment();
|
||||||
|
chartFragment.setArguments(bundle);
|
||||||
|
return chartFragment;
|
||||||
|
}
|
||||||
|
|
||||||
private final List<double[]> pendingPoints = new ArrayList<>();
|
private final List<double[]> pendingPoints = new ArrayList<>();
|
||||||
|
|
||||||
@@ -139,14 +147,13 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public ChartFragment(boolean chartByDistance) {
|
|
||||||
this.chartByDistance = chartByDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
chartByDistance = getArguments().getBoolean(KEY_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
||||||
|
|
||||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceIntervalDefault(getContext());
|
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceIntervalDefault(getContext());
|
||||||
|
|
||||||
// Create a chartView here to store data thus won't need to reload all the data on every onStart or onResume.
|
// Create a chartView here to store data thus won't need to reload all the data on every onStart or onResume.
|
||||||
@@ -190,20 +197,6 @@ public abstract class ChartFragment extends Fragment implements TrackDataListene
|
|||||||
layout.removeView(chartView);
|
layout.removeView(chartView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putBoolean(STATE_CHART_VIEW_BY_DISTANCE_KEY, chartByDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewStateRestored(savedInstanceState);
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
chartByDistance = savedInstanceState.getBoolean(STATE_CHART_VIEW_BY_DISTANCE_KEY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrackUpdated(Track track) {
|
public void onTrackUpdated(Track track) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.fragments;
|
|
||||||
|
|
||||||
public class ChartTimeFragment extends ChartFragment {
|
|
||||||
|
|
||||||
public ChartTimeFragment() {
|
|
||||||
super(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,12 +36,9 @@ import de.dennisguse.opentracks.util.DialogUtils;
|
|||||||
public class ConfirmDeleteDialogFragment extends DialogFragment {
|
public class ConfirmDeleteDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
private static final String CONFIRM_DELETE_DIALOG_TAG = "confirmDeleteDialog";
|
||||||
private ConfirmDeleteCaller caller;
|
private static final String KEY_TRACK_IDS = "trackIds";
|
||||||
private final long[] trackIds;
|
|
||||||
|
|
||||||
public ConfirmDeleteDialogFragment(long[] trackIds) {
|
private ConfirmDeleteCaller caller;
|
||||||
this.trackIds = trackIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
@@ -49,7 +46,12 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
|||||||
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
|
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
|
||||||
*/
|
*/
|
||||||
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
|
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
|
||||||
new ConfirmDeleteDialogFragment(trackIds).show(fragmentManager, CONFIRM_DELETE_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_TRACK_IDS, trackIds);
|
||||||
|
|
||||||
|
ConfirmDeleteDialogFragment deleteTrackDialogFragment = new ConfirmDeleteDialogFragment();
|
||||||
|
deleteTrackDialogFragment.setArguments(bundle);
|
||||||
|
deleteTrackDialogFragment.show(fragmentManager, CONFIRM_DELETE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,6 +67,8 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
|
||||||
|
|
||||||
int titleId;
|
int titleId;
|
||||||
int messageId;
|
int messageId;
|
||||||
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
|
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
|
||||||
|
|||||||
@@ -38,16 +38,17 @@ import de.dennisguse.opentracks.util.DialogUtils;
|
|||||||
public class DeleteMarkerDialogFragment extends DialogFragment {
|
public class DeleteMarkerDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
private static final String DELETE_MARKER_DIALOG_TAG = "deleteMarkerDialog";
|
||||||
private final long[] markerIds;
|
private static final String KEY_MARKER_IDS = "markerIds";
|
||||||
|
|
||||||
private DeleteMarkerCaller caller;
|
private DeleteMarkerCaller caller;
|
||||||
|
|
||||||
public DeleteMarkerDialogFragment(long[] markerIds) {
|
|
||||||
this.markerIds = markerIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
|
public static void showDialog(FragmentManager fragmentManager, long[] markerIds) {
|
||||||
new DeleteMarkerDialogFragment(markerIds).show(fragmentManager, DELETE_MARKER_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLongArray(KEY_MARKER_IDS, markerIds);
|
||||||
|
|
||||||
|
DeleteMarkerDialogFragment deleteMarkerDialogFragment = new DeleteMarkerDialogFragment();
|
||||||
|
deleteMarkerDialogFragment.setArguments(bundle);
|
||||||
|
deleteMarkerDialogFragment.show(fragmentManager, DELETE_MARKER_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,6 +64,8 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final long[] markerIds = getArguments().getLongArray(KEY_MARKER_IDS);
|
||||||
|
|
||||||
final FragmentActivity fragmentActivity = getActivity();
|
final FragmentActivity fragmentActivity = getActivity();
|
||||||
int titleId;
|
int titleId;
|
||||||
int messageId;
|
int messageId;
|
||||||
|
|||||||
@@ -40,18 +40,19 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
|||||||
public class FileTypeDialogFragment extends DialogFragment {
|
public class FileTypeDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
private static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
private static final String FILE_TYPE_DIALOG_TAG = "fileType";
|
||||||
private final int titleId;
|
private static final String KEY_TITLE_ID = "titleId";
|
||||||
|
private static final String KEY_OPTION_ID = "optionId";
|
||||||
|
|
||||||
private FileTypeCaller caller;
|
private FileTypeCaller caller;
|
||||||
private final int optionId;
|
|
||||||
|
|
||||||
public FileTypeDialogFragment(int titleId, int optionId) {
|
|
||||||
this.titleId = titleId;
|
|
||||||
this.optionId = optionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
|
public static void showDialog(FragmentManager fragmentManager, int titleId, int optionId) {
|
||||||
new FileTypeDialogFragment(titleId, optionId).show(fragmentManager, FILE_TYPE_DIALOG_TAG);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt(KEY_TITLE_ID, titleId);
|
||||||
|
bundle.putInt(KEY_OPTION_ID, optionId);
|
||||||
|
|
||||||
|
FileTypeDialogFragment fileTypeDialogFragment = new FileTypeDialogFragment();
|
||||||
|
fileTypeDialogFragment.setArguments(bundle);
|
||||||
|
fileTypeDialogFragment.show(fragmentManager, FILE_TYPE_DIALOG_TAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,6 +68,9 @@ public class FileTypeDialogFragment extends DialogFragment {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
int optionId = getArguments().getInt(KEY_OPTION_ID);
|
||||||
|
final int titleId = getArguments().getInt(KEY_TITLE_ID);
|
||||||
|
|
||||||
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
|
final TrackFileFormat[] trackFileFormats = {TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES, TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, TrackFileFormat.GPX};
|
||||||
String[] choices = new String[trackFileFormats.length];
|
String[] choices = new String[trackFileFormats.length];
|
||||||
for (int i = 0; i < choices.length; i++) {
|
for (int i = 0; i < choices.length; i++) {
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
|
|
||||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||||
private static final String KEY_MARKER_ID = "markerId";
|
private static final String KEY_MARKER_ID = "markerId";
|
||||||
|
|
||||||
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND_MS;
|
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND_MS;
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
private ImageView photoView;
|
private ImageView photoView;
|
||||||
|
|||||||
@@ -232,7 +232,9 @@ public enum TrackFileFormat {
|
|||||||
/**
|
/**
|
||||||
* Returns whether the format supports photos.
|
* Returns whether the format supports photos.
|
||||||
*/
|
*/
|
||||||
public boolean includesPhotos() { return false; };
|
public boolean includesPhotos() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of for each format.
|
* Returns the name of for each format.
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import de.dennisguse.opentracks.content.data.Track;
|
|||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,10 +50,10 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
private ExportActivity exportActivity;
|
private ExportActivity exportActivity;
|
||||||
private WakeLock wakeLock;
|
private WakeLock wakeLock;
|
||||||
|
|
||||||
private boolean completed;
|
private boolean completed = false;
|
||||||
|
|
||||||
private int processedTrackCount;
|
private int processedTrackCount = 0;
|
||||||
private int totalTrackCount;
|
private int totalTrackCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an AsyncTask.
|
* Creates an AsyncTask.
|
||||||
@@ -69,10 +68,6 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
context = exportActivity.getApplicationContext();
|
context = exportActivity.getApplicationContext();
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
completed = false;
|
|
||||||
processedTrackCount = 0;
|
|
||||||
totalTrackCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,17 +93,12 @@ public class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
|
|||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
try {
|
try {
|
||||||
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
|
||||||
boolean isRecording = PreferencesUtils.isRecording(exportActivity);
|
|
||||||
boolean isPaused = PreferencesUtils.isRecordingTrackPaused(exportActivity);
|
|
||||||
// Get the wake lock if not recording or paused
|
|
||||||
if (!isRecording || isPaused) {
|
|
||||||
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
|
||||||
}
|
|
||||||
return exportAllTracks();
|
return exportAllTracks();
|
||||||
} finally {
|
} finally {
|
||||||
if (wakeLock != null && wakeLock.isHeld()) {
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
wakeLock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -39,11 +39,11 @@ import javax.xml.parsers.SAXParserFactory;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
|
import de.dennisguse.opentracks.content.provider.TrackPointFactory;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
|
||||||
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TripStatisticsUpdater;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
@@ -274,7 +274,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
//If not the first segment, add a pause separator if there is at least one location in the last segment.
|
//If not the first segment, add a pause separator if there is at least one location in the last segment.
|
||||||
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment != null) {
|
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment != null) {
|
||||||
insertLocation(createLocation(TrackRecordingService.PAUSE_LATITUDE, 0.0, 0.0, trackData.lastLocationInCurrentSegment.getTime()));
|
insertLocation(createLocation(TrackPointsColumns.PAUSE_LATITUDE, 0.0, 0.0, trackData.lastLocationInCurrentSegment.getTime()));
|
||||||
}
|
}
|
||||||
trackData.lastLocationInCurrentSegment = null;
|
trackData.lastLocationInCurrentSegment = null;
|
||||||
}
|
}
|
||||||
@@ -344,8 +344,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment == null) {
|
if (trackData.numberOfSegments > 1 && trackData.lastLocationInCurrentSegment == null) {
|
||||||
// If not the first segment, add a resume separator before adding the first location.
|
// If not the first segment, add a resume separator before adding the first location.
|
||||||
insertLocation(
|
insertLocation(createLocation(TrackPointsColumns.RESUME_LATITUDE, 0.0, 0.0, location.getTime()));
|
||||||
createLocation(TrackRecordingService.RESUME_LATITUDE, 0.0, 0.0, location.getTime()));
|
|
||||||
}
|
}
|
||||||
trackData.lastLocationInCurrentSegment = location;
|
trackData.lastLocationInCurrentSegment = location;
|
||||||
return location;
|
return location;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import de.dennisguse.opentracks.TrackDetailActivity;
|
|||||||
import de.dennisguse.opentracks.TrackListActivity;
|
import de.dennisguse.opentracks.TrackListActivity;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.data.Waypoint;
|
import de.dennisguse.opentracks.content.data.Waypoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||||
@@ -70,12 +71,6 @@ import de.dennisguse.opentracks.util.UnitConversions;
|
|||||||
*/
|
*/
|
||||||
public class TrackRecordingService extends Service {
|
public class TrackRecordingService extends Service {
|
||||||
|
|
||||||
// TODO Move to a different place.
|
|
||||||
@Deprecated
|
|
||||||
public static final double PAUSE_LATITUDE = 100.0;
|
|
||||||
@Deprecated
|
|
||||||
public static final double RESUME_LATITUDE = 200.0;
|
|
||||||
|
|
||||||
// Anything faster than that (in meters per second) will be considered moving.
|
// Anything faster than that (in meters per second) will be considered moving.
|
||||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||||
|
|
||||||
@@ -386,7 +381,7 @@ public class TrackRecordingService extends Service {
|
|||||||
if (track != null) {
|
if (track != null) {
|
||||||
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
Location resume = new Location(LocationManager.GPS_PROVIDER);
|
||||||
resume.setLongitude(0);
|
resume.setLongitude(0);
|
||||||
resume.setLatitude(RESUME_LATITUDE);
|
resume.setLatitude(TrackPointsColumns.RESUME_LATITUDE);
|
||||||
resume.setTime(System.currentTimeMillis());
|
resume.setTime(System.currentTimeMillis());
|
||||||
insertLocation(track, resume, null);
|
insertLocation(track, resume, null);
|
||||||
}
|
}
|
||||||
@@ -473,7 +468,7 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||||
pause.setLongitude(0);
|
pause.setLongitude(0);
|
||||||
pause.setLatitude(PAUSE_LATITUDE);
|
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
|
||||||
pause.setTime(System.currentTimeMillis());
|
pause.setTime(System.currentTimeMillis());
|
||||||
insertLocation(track, pause, null);
|
insertLocation(track, pause, null);
|
||||||
}
|
}
|
||||||
@@ -610,7 +605,7 @@ public class TrackRecordingService extends Service {
|
|||||||
|
|
||||||
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
Location pause = new Location(LocationManager.GPS_PROVIDER);
|
||||||
pause.setLongitude(0);
|
pause.setLongitude(0);
|
||||||
pause.setLatitude(PAUSE_LATITUDE);
|
pause.setLatitude(TrackPointsColumns.PAUSE_LATITUDE);
|
||||||
pause.setTime(lastLocation.getTime());
|
pause.setTime(lastLocation.getTime());
|
||||||
insertLocation(track, pause, null);
|
insertLocation(track, pause, null);
|
||||||
|
|
||||||
@@ -713,17 +708,7 @@ public class TrackRecordingService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void showNotification(boolean isGpsStarted) {
|
private void showNotification(boolean isGpsStarted) {
|
||||||
// Dijkstra If
|
if (isRecording()) {
|
||||||
if (isRecording() && isPaused()) {
|
|
||||||
stopForeground(true);
|
|
||||||
notificationManager.cancelNotification();
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!isRecording() && !isGpsStarted) {
|
|
||||||
stopForeground(true);
|
|
||||||
notificationManager.cancelNotification();
|
|
||||||
}
|
|
||||||
if (isRecording() && !isPaused()) {
|
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
Intent intent = IntentUtils.newIntent(this, TrackDetailActivity.class)
|
||||||
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
|
.putExtra(TrackDetailActivity.EXTRA_TRACK_ID, recordingTrackId);
|
||||||
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
||||||
@@ -745,5 +730,9 @@ public class TrackRecordingService extends Service {
|
|||||||
notificationManager.updateContent(getString(R.string.gps_starting));
|
notificationManager.updateContent(getString(R.string.gps_starting));
|
||||||
startForeground(TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.getNotification());
|
startForeground(TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.getNotification());
|
||||||
}
|
}
|
||||||
|
if (!isRecording() && !isGpsStarted) {
|
||||||
|
stopForeground(true);
|
||||||
|
notificationManager.cancelNotification();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
|
|
||||||
import static de.dennisguse.opentracks.services.TrackRecordingService.MAX_NO_MOVEMENT_SPEED;
|
import static de.dennisguse.opentracks.services.TrackRecordingService.MAX_NO_MOVEMENT_SPEED;
|
||||||
import static de.dennisguse.opentracks.services.TrackRecordingService.PAUSE_LATITUDE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updater for {@link TripStatistics}.
|
* Updater for {@link TripStatistics}.
|
||||||
@@ -123,7 +123,7 @@ public class TripStatisticsUpdater {
|
|||||||
updateTime(location.getTime());
|
updateTime(location.getTime());
|
||||||
if (!LocationUtils.isValidLocation(location)) {
|
if (!LocationUtils.isValidLocation(location)) {
|
||||||
// Either pause or resume marker
|
// Either pause or resume marker
|
||||||
if (location.getLatitude() == PAUSE_LATITUDE) {
|
if (location.getLatitude() == TrackPointsColumns.PAUSE_LATITUDE) {
|
||||||
if (lastLocation != null && lastMovingLocation != null && lastLocation != lastMovingLocation) {
|
if (lastLocation != null && lastMovingLocation != null && lastLocation != lastMovingLocation) {
|
||||||
currentSegment.addTotalDistance(lastMovingLocation.distanceTo(lastLocation));
|
currentSegment.addTotalDistance(lastMovingLocation.distanceTo(lastLocation));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.dennisguse.opentracks.util;
|
package de.dennisguse.opentracks.util;
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -10,6 +9,7 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an {@link Intent} to request showing a Dashboard.
|
* Create an {@link Intent} to request showing a Dashboard.
|
||||||
@@ -24,11 +24,12 @@ public class IntentDashboardUtils {
|
|||||||
private IntentDashboardUtils() {
|
private IntentDashboardUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean startDashboard(Context context, long trackId) {
|
public static boolean startDashboard(Context context, long[] trackIds) {
|
||||||
ArrayList<Uri> uris = new ArrayList<>();
|
ArrayList<Uri> uris = new ArrayList<>();
|
||||||
|
String trackIdList = ContentProviderUtils.formatIdListForUri(trackIds);
|
||||||
|
|
||||||
uris.add(0, ContentUris.withAppendedId(TracksColumns.CONTENT_URI, trackId));
|
uris.add(0, Uri.withAppendedPath(TracksColumns.CONTENT_URI, trackIdList));
|
||||||
uris.add(1, ContentUris.withAppendedId(TrackPointsColumns.CONTENT_URI, trackId));
|
uris.add(1, Uri.withAppendedPath(TrackPointsColumns.CONTENT_URI_BY_TRACKID, trackIdList));
|
||||||
|
|
||||||
Intent intent = new Intent(ACTION_DASHBOARD);
|
Intent intent = new Intent(ACTION_DASHBOARD);
|
||||||
intent.putParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD, uris);
|
intent.putParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD, uris);
|
||||||
@@ -47,4 +48,5 @@ public class IntentDashboardUtils {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public class IntentUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IntentDashboardUtils.startDashboard(context, trackIds[0])) {
|
if (IntentDashboardUtils.startDashboard(context, trackIds)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
android:id="@+id/imageView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="239dp"
|
android:layout_height="239dp"
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:contentDescription="@string/app_name"
|
android:contentDescription="@string/app_name"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_logo_color_24dp" />
|
android:src="@drawable/ic_logo_color_24dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -80,6 +80,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:autoLink="web"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/third_party_libraries" />
|
android:text="@string/third_party_libraries" />
|
||||||
|
|
||||||
|
|||||||
@@ -44,20 +44,6 @@
|
|||||||
android:linksClickable="true"
|
android:linksClickable="true"
|
||||||
android:text="@string/help_track_map_content" />
|
android:text="@string/help_track_map_content" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="@string/help_data_statistics_customized_title"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/help_data_statistics_customized_content" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/TextMedium"
|
style="@style/TextMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -72,34 +58,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/help_recording_content" />
|
android:text="@string/help_recording_content" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="@string/help_gps_imprecise_title"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/help_gps_imprecise_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="@string/help_heart_rate_sensors_title"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/help_heart_rate_sensors_content" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/TextMedium"
|
style="@style/TextMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -128,20 +86,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/help_track_export_content" />
|
android:text="@string/help_track_export_content" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="@string/help_data_upload_title"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/generic_no" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/TextMedium"
|
style="@style/TextMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -155,20 +99,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/generic_no" />
|
android:text="@string/generic_no" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="@string/help_data_online_title"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/TextMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/generic_no" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/TextMedium"
|
style="@style/TextMedium"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -182,6 +112,36 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/help_data_storage_content" />
|
android:text="@string/help_data_storage_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="@string/help_gps_troubleshooting_title"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:linksClickable="true"
|
||||||
|
android:text="@string/help_gps_troubleshooting_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="@string/help_support_title"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:linksClickable="true"
|
||||||
|
android:text="@string/help_support_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
|||||||
@@ -15,16 +15,16 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
|
||||||
android:id="@+id/list_context_menu_share"
|
|
||||||
android:icon="@drawable/ic_share_24dp"
|
|
||||||
android:title="@string/menu_share"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/list_context_menu_show_on_map"
|
android:id="@+id/list_context_menu_show_on_map"
|
||||||
android:icon="@drawable/ic_map_24dp"
|
android:icon="@drawable/ic_map_24dp"
|
||||||
android:title="@string/menu_show_on_map"
|
android:title="@string/menu_show_on_map"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/list_context_menu_share"
|
||||||
|
android:icon="@drawable/ic_share_24dp"
|
||||||
|
android:title="@string/menu_share"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/list_context_menu_edit"
|
android:id="@+id/list_context_menu_edit"
|
||||||
android:icon="@drawable/ic_edit_24dp"
|
android:icon="@drawable/ic_edit_24dp"
|
||||||
|
|||||||
@@ -24,43 +24,77 @@ limitations under the License.
|
|||||||
<!-- Help -->
|
<!-- Help -->
|
||||||
<string name="help_feature_title">OpenTracks has the following features:</string>
|
<string name="help_feature_title">OpenTracks has the following features:</string>
|
||||||
<string name="help_feature_content">
|
<string name="help_feature_content">
|
||||||
* Create markers (incl. pictures)\n
|
* create GPS tracks\n
|
||||||
* Heart rate sensor (Bluetooth LE)\n
|
* use a heart rate sensor (Bluetooth LE only)\n
|
||||||
* Voice announcements by time or distance\n
|
* voice announcements (by time or distance)\n
|
||||||
* While recording, show on lock screen\n
|
* create markers (incl. pictures)\n
|
||||||
* Share tracks
|
* works completely offline\n
|
||||||
|
* show tracks on a map (see below)
|
||||||
</string>
|
</string>
|
||||||
<string name="help_data_statistics_customized_title">Can the statistics view be customized?</string>
|
|
||||||
<string name="help_data_statistics_customized_content">Yes, it can be customized in the Settings.</string>
|
|
||||||
<string name="help_recording_title">How does OpenTracks record data?</string>
|
|
||||||
<string name="help_recording_content">OpenTracks uses the GPS sensor of the device to record the location continuously.</string>
|
|
||||||
<string name="help_gps_imprecise_title">Statistics (e.g., speed) reported by OpenTracks are imprecise?</string>
|
|
||||||
<string name="help_gps_imprecise_content">OpenTracks directly uses the data provided by the GPS of the mobile device. If the GPS device reports inaccurate locations, OpenTracks cannot do anything about it.</string>
|
|
||||||
<string name="help_heart_rate_sensors_title">Which heart rate sensors does OpenTracks support?</string>
|
|
||||||
<string name="help_heart_rate_sensors_content">OpenTracks should work with any Bluetooth LE heart sensor that support streaming the data in real-time. Smartwatches that store the heart rate data are not supported.</string>
|
|
||||||
<string name="help_track_map_title">How to show a track on a map?</string>
|
<string name="help_track_map_title">How to show a track on a map?</string>
|
||||||
<string name="help_track_map_content">
|
<string name="help_track_map_content">
|
||||||
This requires a 3rd party application to be installed that can consume KMZ-encoded geo data.
|
Use the small map icon.\n
|
||||||
This works well with <a href="http://osmand.net/">OsmAnd~</a> and <a href="https://maps.me/">Maps.ME</a>.
|
a)
|
||||||
In fact, an App needs to support <i>ACTION_VIEW</i> with MIME <i>application/vnd.google-earth.kmz</i>.
|
(live updates) Install <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
|
||||||
At the moment, the recorded locations as well as the timestamps are send to an App while pictures and sensor data are not shared.</string>
|
It provides online or offline maps via <a href="https://www.openstreetmap.org">OpenStreetmap</a>.\n
|
||||||
|
Actually, OSMDashboard is a separate app that only can access (temporarily) access the data of track(s) that are going to be shown.
|
||||||
|
|
||||||
|
\n
|
||||||
|
b)
|
||||||
|
Via a "normal" map application (e.g., <a href="http://osmand.net/">OsmAnd</a> and <a href="https://maps.me/">MAPS.ME</a>).
|
||||||
|
Those app need to support the KMZ file format (only locations and timestamps).
|
||||||
|
In fact, an app needs to support <i>ACTION_VIEW</i> with MIME <i>application/vnd.google-earth.kmz</i>.
|
||||||
|
</string>
|
||||||
|
|
||||||
<string name="help_storage_pictures_title">How does OpenTracks store pictures that were taken as markers?</string>
|
<string name="help_storage_pictures_title">How does OpenTracks store pictures that were taken as markers?</string>
|
||||||
<string name="help_storage_pictures_content">These are stored directly on the file system.</string>
|
<string name="help_storage_pictures_content">These are stored in the app internal storage (i.e., only accessible within OpenTracks).</string>
|
||||||
|
|
||||||
<string name="help_track_export_title">How can I export/share recorded tracks?</string>
|
<string name="help_track_export_title">How can I export/share recorded tracks?</string>
|
||||||
<string name="help_track_export_content">
|
<string name="help_track_export_content">
|
||||||
Tracks can be exported from the Settings as KMZ, KML, or GPX.
|
Tracks can be exported from the Settings as KMZ, KML, or GPX.
|
||||||
KMZ contains all data incl. images, while KML contains only the sensor data, and GPX only the location.
|
KMZ/KML all data; GPX only locations.
|
||||||
Tracks shared via the <i>share icon</i> are shared as KMZ and any App can be used to send the data.
|
Tracks shared via the <i>share icon</i> are shared as KMZ.
|
||||||
</string>
|
</string>
|
||||||
<string name="help_data_upload_title">Can OpenTracks upload the data automatically?</string>
|
|
||||||
<string name="help_data_google_play_services_title">Does OpenTracks require/use Google Play Service?</string>
|
<string name="help_data_google_play_services_title">Does OpenTracks require/use Google Play Service?</string>
|
||||||
<string name="help_data_online_title">Does OpenTracks use online services?</string>
|
|
||||||
|
<string name="help_recording_title">How does OpenTracks record data?</string>
|
||||||
|
<string name="help_recording_content">
|
||||||
|
OpenTracks uses the GPS sensor of the device to record the location continuously.
|
||||||
|
If the GPS device reports inaccurate data (e.g., location, speed, elevation), OpenTracks cannot do anything about it.
|
||||||
|
</string>
|
||||||
|
|
||||||
<string name="help_data_storage_title">How does OpenTracks store sensor data?</string>
|
<string name="help_data_storage_title">How does OpenTracks store sensor data?</string>
|
||||||
<string name="help_data_storage_content">
|
<string name="help_data_storage_content">
|
||||||
OpenTracks stores sensor data per recorded location.
|
Sensor data is stored per recorded location.
|
||||||
In terms of a heart rate sensor this means that only <i>one</i> heart rate measurement is stored per location.
|
In terms of a heart rate sensor, this means that only <i>one</i> heart rate measurement is stored per location.
|
||||||
If you are standing still or are indoors, no sensor data will be recorded (but it will be shown).
|
If you are standing still or are indoors, no sensor data will be recorded (but it will be shown).
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
<string name="help_gps_troubleshooting_title">OpenTracks is not recording locations?</string>
|
||||||
|
<string name="help_gps_troubleshooting_content">
|
||||||
|
What you can do:\n
|
||||||
|
a) check if GPS is enabled,\n
|
||||||
|
b) improve GPS reception (e.g., go outdoors),\n
|
||||||
|
c) check if with <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, and\n
|
||||||
|
d) disable power saving.\n
|
||||||
|
\n
|
||||||
|
If everything does not help please report a bug.
|
||||||
|
</string>
|
||||||
|
|
||||||
|
<string name="help_support_title">How can I support OpenTracks?</string>
|
||||||
|
<string name="help_support_content">
|
||||||
|
You can:\n
|
||||||
|
a) spread the word about OpenTracks,\n
|
||||||
|
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">propose features</a>,\n
|
||||||
|
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">report bugs</a>,\n
|
||||||
|
d) help in software development,\n
|
||||||
|
e) buy in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
|
||||||
|
f) donate, and\n
|
||||||
|
g) do whatever you think helps OpenTracks.
|
||||||
|
</string>
|
||||||
|
|
||||||
<!-- Activity Type -->
|
<!-- Activity Type -->
|
||||||
<string name="activity_type_airplane">airplane</string>
|
<string name="activity_type_airplane">airplane</string>
|
||||||
<string name="activity_type_atv">ATV</string>
|
<string name="activity_type_atv">ATV</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user