TrackListActivity now loads marker count via join rather than a 2nd select.

Fixes #328.
This commit is contained in:
Dennis Guse
2020-11-01 17:34:54 +01:00
parent 43e0f33776
commit 8d141b1324
4 changed files with 43 additions and 58 deletions
@@ -42,11 +42,11 @@ import androidx.core.app.ActivityCompat;
import androidx.cursoradapter.widget.ResourceCursorAdapter; import androidx.cursoradapter.widget.ResourceCursorAdapter;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
import androidx.loader.app.LoaderManager.LoaderCallbacks; import androidx.loader.app.LoaderManager.LoaderCallbacks;
import androidx.loader.content.CursorLoader;
import androidx.loader.content.Loader; import androidx.loader.content.Loader;
import de.dennisguse.opentracks.content.data.Track; 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.databinding.TrackListBinding; import de.dennisguse.opentracks.databinding.TrackListBinding;
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment; import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
@@ -60,6 +60,7 @@ import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils; import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils; import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.util.TrackUtils;
/** /**
* An activity displaying a list of tracks. * An activity displaying a list of tracks.
@@ -71,7 +72,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private static final String TAG = TrackListActivity.class.getSimpleName(); private static final String TAG = TrackListActivity.class.getSimpleName();
// The following are set in onCreate // The following are set in onCreate
private ContentProviderUtils contentProviderUtils;
private SharedPreferences sharedPreferences; private SharedPreferences sharedPreferences;
private TrackRecordingServiceConnection trackRecordingServiceConnection; private TrackRecordingServiceConnection trackRecordingServiceConnection;
private TrackController trackController; private TrackController trackController;
@@ -83,7 +83,11 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() { private final LoaderCallbacks<Cursor> loaderCallbacks = new LoaderCallbacks<Cursor>() {
@Override @Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return ContentProviderUtils.getTracksCursorLoader(TrackListActivity.this); String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, "markerCount"};
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null, null, TrackUtils.TRACK_SORT_ORDER);
} }
@Override @Override
@@ -224,7 +228,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this); recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this); sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback); trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
@@ -256,14 +259,15 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) { resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
@Override @Override
public void bindView(View view, Context context, Cursor cursor) { public void bindView(View view, Context context, Cursor cursor) {
int idIndex = cursor.getColumnIndex(TracksColumns._ID); int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int iconIndex = cursor.getColumnIndex(TracksColumns.ICON); int iconIndex = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
int nameIndex = cursor.getColumnIndex(TracksColumns.NAME); int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME); int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE); int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME); int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
int categoryIndex = cursor.getColumnIndex(TracksColumns.CATEGORY); int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
int descriptionIndex = cursor.getColumnIndex(TracksColumns.DESCRIPTION); int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
Track.Id trackId = new Track.Id(cursor.getLong(idIndex)); Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
boolean isRecording = trackId.equals(recordingTrackId); boolean isRecording = trackId.equals(recordingTrackId);
@@ -272,7 +276,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
String name = cursor.getString(nameIndex); String name = cursor.getString(nameIndex);
String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex)); String totalTime = StringUtils.formatElapsedTime(cursor.getLong(totalTimeIndex));
String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits); String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits);
int markerCount = contentProviderUtils.getMarkerCount(trackId); int markerCount = cursor.getInt(markerCountIndex);
long startTime = cursor.getLong(startTimeIndex); long startTime = cursor.getLong(startTimeIndex);
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex); String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
String description = cursor.getString(descriptionIndex); String description = cursor.getString(descriptionIndex);
@@ -35,12 +35,13 @@ public interface TracksColumns extends BaseColumns {
String DEFAULT_SORT_ORDER = _ID; String DEFAULT_SORT_ORDER = _ID;
// Columns // Columns
String UUID = "uuid"; String UUID = "uuid"; // identifier to make tracks globally unique (prevent re-import)
String NAME = "name"; // track name String NAME = "name"; // track name
String DESCRIPTION = "description"; // track description String DESCRIPTION = "description"; // track description
String CATEGORY = "category"; // track activity type String CATEGORY = "category"; // track activity type
String STARTTIME = "starttime"; // track start time String STARTTIME = "starttime"; // track start time
String STOPTIME = "stoptime"; // track stop time String STOPTIME = "stoptime"; // track stop time
String MARKER_COUNT = "markerCount"; // the numbers of markers (virtual column)
@Deprecated @Deprecated
String NUMPOINTS = "numpoints"; // number of track points //TODO UNUSED String NUMPOINTS = "numpoints"; // number of track points //TODO UNUSED
String TOTALDISTANCE = "totaldistance"; // total distance String TOTALDISTANCE = "totaldistance"; // total distance
@@ -55,25 +56,25 @@ public interface TracksColumns extends BaseColumns {
String ELEVATIONGAIN = "elevationgain"; // elevation gain String ELEVATIONGAIN = "elevationgain"; // elevation gain
String ICON = "icon"; // track activity type icon String ICON = "icon"; // track activity type icon
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" // table String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // id + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ NAME + " TEXT, " // name + NAME + " TEXT, "
+ DESCRIPTION + " TEXT, " // description + DESCRIPTION + " TEXT, "
+ CATEGORY + " TEXT, " // category + CATEGORY + " TEXT, "
+ STARTTIME + " INTEGER, " // start time + STARTTIME + " INTEGER, "
+ STOPTIME + " INTEGER, " // stop time + STOPTIME + " INTEGER, "
+ NUMPOINTS + " INTEGER, " // num points + NUMPOINTS + " INTEGER, "
+ TOTALDISTANCE + " FLOAT, " // total distance + TOTALDISTANCE + " FLOAT, "
+ TOTALTIME + " INTEGER, " // total time + TOTALTIME + " INTEGER, "
+ MOVINGTIME + " INTEGER, " // moving time + MOVINGTIME + " INTEGER, "
+ AVGSPEED + " FLOAT, " // average speed + AVGSPEED + " FLOAT, "
+ AVGMOVINGSPEED + " FLOAT, " // average moving speed + AVGMOVINGSPEED + " FLOAT, "
+ MAXSPEED + " FLOAT, " // max speed + MAXSPEED + " FLOAT, "
+ MINELEVATION + " FLOAT, " // min elevation + MINELEVATION + " FLOAT, "
+ MAXELEVATION + " FLOAT, " // max elevation + MAXELEVATION + " FLOAT, "
+ ELEVATIONGAIN + " FLOAT, " // elevation gain + ELEVATIONGAIN + " FLOAT, "
+ ICON + " TEXT, " // icon + ICON + " TEXT, "
+ UUID + " BLOB)"; // UUID + UUID + " BLOB)";
String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")"; String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")";
@@ -47,7 +47,6 @@ 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.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils; import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.TrackUtils;
import de.dennisguse.opentracks.util.UUIDUtils; import de.dennisguse.opentracks.util.UUIDUtils;
/** /**
@@ -220,14 +219,6 @@ public class ContentProviderUtils {
return tracks; return tracks;
} }
public static Loader<Cursor> getTracksCursorLoader(Context context) {
String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME,
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON};
return new CursorLoader(context, TracksColumns.CONTENT_URI, PROJECTION, null, null, TrackUtils.TRACK_SORT_ORDER);
}
@Deprecated @Deprecated
//TODO Do not use; rather get the proper TrackId (e.g., resume a track might give you some weird effects). //TODO Do not use; rather get the proper TrackId (e.g., resume a track might give you some weird effects).
@VisibleForTesting @VisibleForTesting
@@ -498,7 +489,7 @@ public class ContentProviderUtils {
} }
} }
@Deprecated //TODO Expose information via Cursor @Deprecated //TODO TracksColumns.MARKER_COUNT while querying for tracks
public int getMarkerCount(Track.Id trackId) { public int getMarkerCount(Track.Id trackId) {
if (!trackId.isValid()) { if (!trackId.isValid()) {
return 0; return 0;
@@ -33,6 +33,8 @@ import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import java.util.Arrays;
import de.dennisguse.opentracks.content.data.MarkerColumns; import de.dennisguse.opentracks.content.data.MarkerColumns;
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;
@@ -210,7 +212,11 @@ public class CustomContentProvider extends ContentProvider {
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")"); queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
break; break;
case TRACKS: case TRACKS:
queryBuilder.setTables(TracksColumns.TABLE_NAME); if (projection != null && Arrays.asList(projection).contains(TracksColumns.MARKER_COUNT)) {
queryBuilder.setTables(TracksColumns.TABLE_NAME + " LEFT OUTER JOIN (SELECT " + MarkerColumns.TRACKID + " AS markerTrackId, COUNT(*) AS " + TracksColumns.MARKER_COUNT + " FROM " + MarkerColumns.TABLE_NAME + " GROUP BY " + MarkerColumns.TRACKID + ") ON (" + TracksColumns.TABLE_NAME + "." + TracksColumns._ID + "= markerTrackId)");
} else {
queryBuilder.setTables(TracksColumns.TABLE_NAME);
}
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER; sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
break; break;
case TRACKS_BY_ID: case TRACKS_BY_ID:
@@ -291,11 +297,6 @@ public class CustomContentProvider extends ContentProvider {
return count; return count;
} }
/**
* Gets the {@link UrlType} for a url.
*
* @param url the url
*/
@NonNull @NonNull
private UrlType getUrlType(Uri url) { private UrlType getUrlType(Uri url) {
UrlType[] urlTypes = UrlType.values(); UrlType[] urlTypes = UrlType.values();
@@ -327,12 +328,6 @@ public class CustomContentProvider extends ContentProvider {
} }
} }
/**
* Inserts a track point.
*
* @param url the content url
* @param values the content values
*/
private Uri insertTrackPoint(Uri url, ContentValues values) { private Uri insertTrackPoint(Uri url, ContentValues values) {
boolean hasLatitude = values.containsKey(TrackPointsColumns.LATITUDE); boolean hasLatitude = values.containsKey(TrackPointsColumns.LATITUDE);
boolean hasLongitude = values.containsKey(TrackPointsColumns.LONGITUDE); boolean hasLongitude = values.containsKey(TrackPointsColumns.LONGITUDE);
@@ -347,12 +342,6 @@ public class CustomContentProvider extends ContentProvider {
throw new SQLiteException("Failed to insert a track point " + url); throw new SQLiteException("Failed to insert a track point " + url);
} }
/**
* Inserts a track.
*
* @param url the content url
* @param contentValues the content values
*/
private Uri insertTrack(Uri url, ContentValues contentValues) { private Uri insertTrack(Uri url, ContentValues contentValues) {
boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME); boolean hasStartTime = contentValues.containsKey(TracksColumns.STARTTIME);
if (!hasStartTime) { if (!hasStartTime) {