Cleanup: mainly code formatting and made LocationIterator implement autoclosable.

This commit is contained in:
Dennis Guse
2019-09-07 12:23:56 +02:00
parent 025dee6821
commit 87d92d25f9
104 changed files with 1076 additions and 1526 deletions
@@ -35,6 +35,11 @@ import de.dennisguse.opentracks.content.Waypoint.WaypointType;
*/
public interface ContentProviderUtils {
/**
* Maximum number of waypoints that will be loaded at one time.
*/
int MAX_LOADED_WAYPOINTS_POINTS = 10000;
/**
* The authority (the first part of the URI) for the app's content provider.
*/
@@ -56,7 +61,8 @@ public interface ContentProviderUtils {
};
/**
* Clears a track. Removes waypoints and trackpoints. Only keeps the track id.
* Clears a track: removes waypoints and trackpoints.
* Only keeps the track id.
*
* @param trackId the track id
*/
@@ -82,20 +88,19 @@ public interface ContentProviderUtils {
void deleteTrack(Context context, long trackId);
/**
* Gets all the tracks. If no track exists, an empty list is returned.
* <p>
* Note that the returned tracks do not have any track points attached.
* Gets all the tracks.
* If no track exists, an empty list is returned.
* NOTE: the returned tracks do not have any track points attached.
*/
List<Track> getAllTracks();
/**
* Gets the last track. Returns null if doesn't exist.
* Gets the last track or null.
*/
Track getLastTrack();
/**
* Gets a track by a track id. Returns null if not found.
* <p>
* Gets a track by a track id or null
* Note that the returned track doesn't have any track points attached.
*
* @param trackId the track id.
@@ -103,8 +108,8 @@ public interface ContentProviderUtils {
Track getTrack(long trackId);
/**
* Gets a track cursor. The caller owns the returned cursor and is responsible
* for closing it.
* Gets a track cursor.
* The caller owns the returned cursor and is responsible for closing it.
*
* @param selection the selection. Can be null
* @param selectionArgs the selection arguments. Can be null
@@ -114,8 +119,7 @@ public interface ContentProviderUtils {
/**
* Inserts a track.
* <p>
* Note: This doesn't insert any track points.
* NOTE: This doesn't insert any track points.
*
* @param track the track
* @return the content provider URI of the inserted track.
@@ -124,8 +128,7 @@ public interface ContentProviderUtils {
/**
* Updates a track.
* <p>
* Note: This doesn't update any track points.
* NOTE: This doesn't update any track points.
*
* @param track the track
*/
@@ -139,19 +142,19 @@ public interface ContentProviderUtils {
Waypoint createWaypoint(Cursor cursor);
/**
* Deletes a waypoint. If deleting a statistics waypoint, this will also
* correct the next statistics waypoint after the deleted one to reflect the
* deletion. The generator is used to update the next statistics waypoint.
* Deletes a waypoint.
* If deleting a statistics waypoint, this will also correct the next statistics waypoint after the deleted one to reflect the deletion.
* The generator is used to update the next statistics waypoint.
*
* @param waypointId the waypoint id
* @param descriptionGenerator the description generator. Can be null for
* waypoint marker
* @param descriptionGenerator the description generator. Can be null for waypoint marker
*/
void deleteWaypoint(Context context, long waypointId, DescriptionGenerator descriptionGenerator);
/**
* Gets the first waypoint id for a track. The first waypoint is special as it
* contains the stats for the track. Returns -1L if it doesn't exist.
* Gets the first waypoint id for a track.
* The first waypoint is special as it contains the stats for the track.
* Returns -1L if it doesn't exist.
*
* @param trackId the track id
*/
@@ -166,8 +169,8 @@ public interface ContentProviderUtils {
Waypoint getLastWaypoint(long trackId, WaypointType waypointType);
/**
* Gets the next waypoint number for a type. Returns -1 if not able to get the
* next waypoint number.
* Gets the next waypoint number for a type.
* Returns -1 if not able to get the next waypoint number.
*
* @param trackId the track id
* @param waypointType the waypoint type
@@ -175,15 +178,16 @@ public interface ContentProviderUtils {
int getNextWaypointNumber(long trackId, WaypointType waypointType);
/**
* Gets a waypoint from a waypoint id. Returns null if not found.
* Gets a waypoint from a waypoint id.
* Returns null if not found.
*
* @param waypointId the waypoint id
*/
Waypoint getWaypoint(long waypointId);
/**
* Gets a waypoint cursor. The caller owns the returned cursor and is
* responsible for closing it.
* Gets a waypoint cursor.
* he caller owns the returned cursor and is responsible for closing it.
*
* @param selection the selection. Can be null
* @param selectionArgs the selection arguments. Can be null
@@ -194,13 +198,12 @@ public interface ContentProviderUtils {
Cursor getWaypointCursor(String selection, String[] selectionArgs, String sortOrder, int maxWaypoints);
/**
* Gets a waypoint cursor for a track. The caller owns the returned cursor and
* is responsible for closing it.
* Gets a waypoint cursor for a track.
* The caller owns the returned cursor and is responsible for closing it.
*
* @param trackId the track id
* @param minWaypointId the minimum waypoint id. -1L to ignore
* @param maxWaypoints the maximum number of waypoints to return. -1 for no
* limit
* @param maxWaypoints the maximum number of waypoints to return. -1 for no limit
*/
Cursor getWaypointCursor(long trackId, long minWaypointId, int maxWaypoints);
@@ -220,7 +223,8 @@ public interface ContentProviderUtils {
Uri insertWaypoint(Waypoint waypoint);
/**
* Updates a waypoint. Returns true if successful.
* Updates a waypoint.
* Returns true if successful.
*
* @param waypoint the waypoint
*/
@@ -245,14 +249,16 @@ public interface ContentProviderUtils {
Location createTrackPoint(Cursor cursor);
/**
* Gets the first location id for a track. Returns -1L if it doesn't exist.
* Gets the first location id for a track.
* Returns -1L if it doesn't exist.
*
* @param trackId the track id
*/
long getFirstTrackPointId(long trackId);
/**
* Gets the last location id for a track. Returns -1L if it doesn't exist.
* Gets the last location id for a track.
* Returns -1L if it doesn't exist.
*
* @param trackId the track id
*/
@@ -268,28 +274,23 @@ public interface ContentProviderUtils {
long getTrackPointId(long trackId, Location location);
/**
* Gets the first valid location for a track. Returns null if it doesn't
* exist.
* Gets the first valid location for a track.
* Returns null if it doesn't exist.
*
* @param trackId the track id
*/
Location getFirstValidTrackPoint(long trackId);
/**
* Gets the last valid location for a track. Returns null if it doesn't exist.
* Gets the last valid location for a track.
* Returns null if it doesn't exist.
*
* @param trackId the track id
*/
Location getLastValidTrackPoint(long trackId);
/**
* Gets the last valid location.
*/
Location getLastValidTrackPoint();
/**
* Creates a location cursor. The caller owns the returned cursor and is
* responsible for closing it.
* Creates a location cursor. The caller owns the returned cursor and is responsible for closing it.
*
* @param trackId the track id
* @param startTrackPointId the starting track point id. -1L to ignore
@@ -299,20 +300,15 @@ public interface ContentProviderUtils {
Cursor getTrackPointCursor(long trackId, long startTrackPointId, int maxLocations, boolean descending);
/**
* Creates a new read-only iterator over a given track's points. It provides a
* lightweight way of iterating over long tracks without failing due to the
* underlying cursor limitations. Since it's a read-only iterator,
* {@link Iterator#remove()} always throws
* {@link UnsupportedOperationException}. Each call to
* {@link LocationIterator#next()} may advance to the next DB record, and if
* so, the iterator calls {@link LocationFactory#createLocation()} and
* populates it with information retrieved from the record. When done with
* iteration, {@link LocationIterator#close()} must be called.
* Creates a new read-only iterator over a given track's points.
* It provides a lightweight way of iterating over long tracks without failing due to the underlying cursor limitations.
* Since it's a read-only iterator, {@link Iterator#remove()} always throws {@link UnsupportedOperationException}.
* Each call to {@link LocationIterator#next()} may advance to the next DB record, and if so, the iterator calls {@link LocationFactory#createLocation()} and populates it with information retrieved from the record.
* When done with iteration, {@link LocationIterator#close()} must be called.
*
* @param trackId the track id
* @param startTrackPointId the starting track point id. -1L to ignore
* @param descending true to sort the result in descending order (latest
* location first)
* @param descending true to sort the result in descending order (latest location first)
* @param locationFactory the location factory
*/
LocationIterator getTrackPointLocationIterator(long trackId, long startTrackPointId, boolean descending, LocationFactory locationFactory);
@@ -327,10 +323,9 @@ public interface ContentProviderUtils {
Uri insertTrackPoint(Location location, long trackId);
/**
* A lightweight wrapper around the original {@link Cursor} with a method to
* clean up.
* A lightweight wrapper around the original {@link Cursor} with a method to clean up.
*/
interface LocationIterator extends Iterator<Location> {
interface LocationIterator extends Iterator<Location>, AutoCloseable {
/**
* Gets the most recently retrieved track point id by {@link #next()}.
@@ -349,8 +344,8 @@ public interface ContentProviderUtils {
interface LocationFactory {
/**
* Creates a new {@link Location}. An implementation can create new
* instances or reuse existing instances for optimization.
* Creates a new {@link Location}.
* An implementation can create new instances or reuse existing instances for optimization.
*/
Location createLocation();
}
@@ -358,6 +353,8 @@ public interface ContentProviderUtils {
/**
* A factory which can produce instances of {@link ContentProviderUtils}, and can be overridden for testing.
*/
//TODO Is this still used? From a quick glance, it doesn't looks like it; probably a left over from testing.
@Deprecated
class Factory {
private static Factory instance = new Factory();
@@ -379,8 +376,8 @@ public interface ContentProviderUtils {
}
/**
* Overrides the factory instance for testing. Don't forget to set it back
* to the original value after testing.
* Overrides the factory instance for testing.
* Don't forget to set it back to the original value after testing.
*
* @param factory the factory
*/
@@ -394,7 +391,7 @@ public interface ContentProviderUtils {
*
* @param context the context
*/
protected ContentProviderUtils newForContext(Context context) {
private ContentProviderUtils newForContext(Context context) {
return new ContentProviderUtilsImpl(context.getContentResolver());
}
}
@@ -842,14 +842,6 @@ public class ContentProviderUtilsImpl implements ContentProviderUtils {
return findTrackPointBy(selection, selectionArgs);
}
@Override
public Location getLastValidTrackPoint() {
String selection = TrackPointsColumns._ID + "=(select max(" + TrackPointsColumns._ID + ") from "
+ TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.LATITUDE + "<="
+ MAX_LATITUDE + ")";
return findTrackPointBy(selection, null);
}
@Override
public Cursor getTrackPointCursor(long trackId, long startTrackPointId, int maxLocations, boolean descending) {
if (trackId < 0) {
@@ -35,8 +35,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
/**
* A {@link ContentProvider} that handles access to track points, tracks, and
* waypoints tables.
* A {@link ContentProvider} that handles access to track points, tracks, and waypoints tables.
*
* @author Leif Hendrik Wilden
*/
@@ -44,10 +43,14 @@ 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 final UriMatcher uriMatcher;
private SQLiteDatabase db;
public CustomContentProvider() {
@@ -30,12 +30,12 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
*
* @author Rodrigo Damazio
*/
public class DataSource {
class DataSource {
private final ContentResolver contentResolver;
private final SharedPreferences sharedPreferences;
public DataSource(Context context) {
DataSource(Context context) {
contentResolver = context.getContentResolver();
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
}
@@ -46,7 +46,7 @@ public class DataSource {
* @param uri the uri
* @param observer the observer
*/
public void registerContentObserver(Uri uri, ContentObserver observer) {
void registerContentObserver(Uri uri, ContentObserver observer) {
contentResolver.registerContentObserver(uri, false, observer);
}
@@ -55,7 +55,7 @@ public class DataSource {
*
* @param observer the observer
*/
public void unregisterContentObserver(ContentObserver observer) {
void unregisterContentObserver(ContentObserver observer) {
contentResolver.unregisterContentObserver(observer);
}
@@ -64,7 +64,7 @@ public class DataSource {
*
* @param listener the listener
*/
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
}
@@ -73,8 +73,7 @@ public class DataSource {
*
* @param listener the listener
*/
public void unregisterOnSharedPreferenceChangeListener(
OnSharedPreferenceChangeListener listener) {
void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
}
}
@@ -26,17 +26,19 @@ import java.util.EnumSet;
import java.util.Set;
/**
* Data source manager. Creates observers/listeners and manages their
* registration with {@link DataSource}. The observers/listeners calls
* {@link DataSourceListener} when data changes.
* Creates observers/listeners and manages their registration with {@link DataSource}.
* The observers/listeners calls {@link DataSourceListener} when data changes.
*
* @author Rodrigo Damazio
*/
public class DataSourceManager {
class DataSourceManager {
private static final String TAG = DataSourceManager.class.getSimpleName();
private final DataSource dataSource;
private final DataSourceListener dataSourceListener;
// Registered listeners
private final Set<TrackDataType> registeredListeners = EnumSet.noneOf(TrackDataType.class);
private final Handler handler;
@@ -45,7 +47,7 @@ public class DataSourceManager {
private final TrackPointsTableObserver trackPointsTableObserver;
private final PreferenceListener preferenceListener;
public DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
DataSourceManager(DataSource dataSource, DataSourceListener dataSourceListener) {
this.dataSource = dataSource;
this.dataSourceListener = dataSourceListener;
@@ -61,7 +63,7 @@ public class DataSourceManager {
*
* @param listeners the listeners
*/
public void updateListeners(EnumSet<TrackDataType> listeners) {
void updateListeners(EnumSet<TrackDataType> listeners) {
EnumSet<TrackDataType> neededListeners = EnumSet.copyOf(listeners);
/*
@@ -156,7 +158,7 @@ public class DataSourceManager {
/**
* Unregisters all listeners with data source.
*/
public void unregisterAllListeners() {
void unregisterAllListeners() {
for (TrackDataType trackDataType : TrackDataType.values()) {
unregisterListener(trackDataType);
}
@@ -169,7 +171,7 @@ public class DataSourceManager {
*/
private class TracksTableObserver extends ContentObserver {
public TracksTableObserver() {
TracksTableObserver() {
super(handler);
}
@@ -186,7 +188,7 @@ public class DataSourceManager {
*/
private class WaypointsTableObserver extends ContentObserver {
public WaypointsTableObserver() {
WaypointsTableObserver() {
super(handler);
}
@@ -203,7 +205,7 @@ public class DataSourceManager {
*/
private class TrackPointsTableObserver extends ContentObserver {
public TrackPointsTableObserver() {
TrackPointsTableObserver() {
super(handler);
}
@@ -18,8 +18,7 @@ package de.dennisguse.opentracks.content;
import de.dennisguse.opentracks.stats.TripStatistics;
/**
* An interface for an object that can generate descriptions of track and
* waypoint.
* An interface for an object that can generate descriptions of track and waypoint.
*
* @author Sandor Dornbush
*/
@@ -115,8 +115,7 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
writeSpeed(stats.getMaxSpeed(), builder, R.string.description_max_speed, lineBreak);
// Average pace
writePace(
stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
writePace(stats.getAverageSpeed(), builder, R.string.description_average_pace_in_minute, lineBreak);
// Average moving pace
writePace(stats.getAverageMovingSpeed(), builder,
@@ -132,8 +131,7 @@ public class DescriptionGeneratorImpl implements DescriptionGenerator {
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
// Elevation gain
writeElevation(
stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
// Max grade
writeGrade(stats.getMaxGrade(), builder, R.string.description_max_grade, lineBreak);
@@ -23,6 +23,7 @@ import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -97,34 +98,35 @@ public class SearchEngine {
/**
* Oldest timestamp for which we rank based on time (2000-01-01 00:00:00.000)
*/
private static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
public static final long OLDEST_ALLOWED_TIMESTAMP = 946692000000L;
/**
* Comparador for scored results.
*/
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR =
new Comparator<ScoredResult>() {
@Override
public int compare(ScoredResult r1, ScoredResult r2) {
// Score ordering.
int scoreDiff = Double.compare(r2.score, r1.score);
if (scoreDiff != 0) {
return scoreDiff;
}
private static final Comparator<ScoredResult> SCORED_RESULT_COMPARATOR = new Comparator<ScoredResult>() {
@Override
public int compare(ScoredResult r1, ScoredResult r2) {
// Score ordering.
int scoreDiff = Double.compare(r2.score, r1.score);
if (scoreDiff != 0) {
return scoreDiff;
}
// Make tracks come before waypoints.
if (r1.waypoint != null && r2.track != null) {
return 1;
} else if (r1.track != null && r2.waypoint != null) {
return -1;
}
// Make tracks come before waypoints.
if (r1.waypoint != null && r2.track != null) {
return 1;
} else if (r1.track != null && r2.waypoint != null) {
return -1;
}
// Finally, use arbitrary ordering, by ID.
long id1 = r1.track != null ? r1.track.getId() : r1.waypoint.getId();
long id2 = r2.track != null ? r2.track.getId() : r2.waypoint.getId();
long idDiff = id2 - id1;
return Long.signum(idDiff);
}
};
// Finally, use arbitrary ordering, by ID.
long id1 = r1.track != null ? r1.track.getId() : r1.waypoint.getId();
long id2 = r2.track != null ? r2.track.getId() : r2.waypoint.getId();
long idDiff = id2 - id1;
return Long.signum(idDiff);
}
};
private final ContentProviderUtils providerUtils;
public SearchEngine(ContentProviderUtils providerUtils) {
@@ -134,6 +136,7 @@ public class SearchEngine {
/**
* Squashes a number by calculating 1 / log (1 + x).
*/
//TODO Why is this done?
private static double squash(double x) {
return 1.0 / Math.log1p(x);
}
@@ -145,15 +148,10 @@ public class SearchEngine {
* @return a set of results, sorted according to their score
*/
public SortedSet<ScoredResult> search(SearchQuery query) {
ArrayList<Track> tracks = new ArrayList<>();
ArrayList<Waypoint> waypoints = new ArrayList<>();
TreeSet<ScoredResult> scoredResults = new TreeSet<>(SCORED_RESULT_COMPARATOR);
retrieveTracks(query, tracks);
retrieveWaypoints(query, waypoints);
scoreTrackResults(tracks, query, scoredResults);
scoreWaypointResults(waypoints, query, scoredResults);
scoreTrackResults(retrieveTracks(query), query, scoredResults);
scoreWaypointResults(retrieveWaypoints(query), query, scoredResults);
return scoredResults;
}
@@ -161,18 +159,19 @@ public class SearchEngine {
/**
* Retrieves tracks matching the given query from the database.
*
* @param query the query to retrieve for
* @param tracks list to fill with the resulting tracks
* @param query the query to retrieve for
*/
private void retrieveTracks(SearchQuery query, ArrayList<Track> tracks) {
private List<Track> retrieveTracks(SearchQuery query) {
ArrayList<Track> tracks = new ArrayList<>();
String queryLikeSelection = "%" + query.textQuery + "%";
String[] trackSelectionArgs = new String[]{
queryLikeSelection,
queryLikeSelection,
queryLikeSelection};
queryLikeSelection
};
try (Cursor cursor = providerUtils.getTrackCursor(
TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
try (Cursor cursor = providerUtils.getTrackCursor(TRACK_SELECTION_QUERY, trackSelectionArgs, TRACK_SELECTION_ORDER)) {
if (cursor != null) {
tracks.ensureCapacity(cursor.getCount());
while (cursor.moveToNext()) {
@@ -180,22 +179,26 @@ public class SearchEngine {
}
}
}
return tracks;
}
/**
* Retrieves waypoints matching the given query from the database.
*
* @param query the query to retrieve for
* @param waypoints list to fill with the resulting waypoints
* @param query the query to retrieve for
*/
private void retrieveWaypoints(SearchQuery query, ArrayList<Waypoint> waypoints) {
private List<Waypoint> retrieveWaypoints(SearchQuery query) {
ArrayList<Waypoint> waypoints = new ArrayList<>();
String queryLikeSelection2 = "%" + query.textQuery + "%";
String[] waypointSelectionArgs = new String[]{
queryLikeSelection2,
queryLikeSelection2,
queryLikeSelection2};
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs,
WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
queryLikeSelection2
};
try (Cursor cursor = providerUtils.getWaypointCursor(WAYPOINT_SELECTION_QUERY, waypointSelectionArgs, WAYPOINT_SELECTION_ORDER, MAX_SCORED_WAYPOINTS)) {
if (cursor != null) {
waypoints.ensureCapacity(cursor.getCount());
while (cursor.moveToNext()) {
@@ -206,6 +209,8 @@ public class SearchEngine {
}
}
}
return waypoints;
}
/**
@@ -238,7 +243,6 @@ public class SearchEngine {
score *= getTitleBoost(query, track.getName(), track.getDescription(), track.getCategory());
TripStatistics statistics = track.getTripStatistics();
// TODO: Also boost for proximity to the currently-centered position on the map.
score *= getDistanceBoost(query, statistics.getMeanLatitude(), statistics.getMeanLongitude());
long meanTimestamp = (statistics.getStartTime() + statistics.getStopTime()) / 2L;
@@ -281,7 +285,6 @@ public class SearchEngine {
Location location = waypoint.getLocation();
score *= getTitleBoost(query, waypoint.getName(), waypoint.getDescription(), waypoint.getCategory());
// TODO: Also boost for proximity to the currently-centered position on the map.
score *= getDistanceBoost(query, location.getLatitude(), location.getLongitude());
score *= getTimeBoost(query, location.getTime());
@@ -302,8 +305,7 @@ public class SearchEngine {
* @param category the category of the track or waypoint
* @return the total boost to be applied to the result
*/
private double getTitleBoost(SearchQuery query,
String name, String description, String category) {
private double getTitleBoost(SearchQuery query, String name, String description, String category) {
// Title boost: track name > description > category.
double boost = 1.0;
if (name.toLowerCase(Locale.getDefault()).contains(query.textQuery)) {
@@ -332,8 +334,7 @@ public class SearchEngine {
}
// Score recent tracks higher.
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S
* UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
long timeAgoHours = (long) ((query.currentTimestamp - timestamp) * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN * UnitConversions.MIN_TO_HR);
if (timeAgoHours > 0L) {
return squash(timeAgoHours);
} else {
@@ -364,6 +365,7 @@ public class SearchEngine {
// Score tracks close to the current location higher.
double distanceKm = distanceResults[0] * UnitConversions.M_TO_KM;
if (distanceKm > 0.0) {
// Use the inverse of the amortized distance.
return squash(distanceKm);
@@ -377,13 +379,12 @@ public class SearchEngine {
* Description of a search query, along with all contextual data needed to execute it.
*/
public static class SearchQuery {
public final String textQuery;
public final Location currentLocation;
public final long currentTrackId;
public final long currentTimestamp;
final String textQuery;
final Location currentLocation;
final long currentTrackId;
final long currentTimestamp;
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId,
long currentTimestamp) {
public SearchQuery(String textQuery, Location currentLocation, long currentTrackId, long currentTimestamp) {
this.textQuery = textQuery.toLowerCase(Locale.getDefault());
this.currentLocation = currentLocation;
this.currentTrackId = currentTrackId;
@@ -397,7 +398,7 @@ public class SearchEngine {
public static class ScoredResult {
public final Track track;
public final Waypoint waypoint;
public final double score;
final double score;
ScoredResult(Track track, double score) {
this.track = track;
@@ -27,9 +27,9 @@ import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
/**
* A content provider that mimics the behavior of {@link androidx.core.content.FileProvider}, which shares virtual (non-existing) KML-files.
* The actual content of the virtual files is generated by using the functionality defined in {@link CustomContentProvider}.
* <p>
*
* Moreover, it manages access to OpenTrack's database via {@link CustomContentProvider}.
* <p>
*
* Explanation:
* Although a request is handled by a {@link android.content.ContentProvider} (with temporarily granted permission), Android's security infrastructure prevents forwarding queries to non-exported {@link android.content.ContentProvider}.
* Thus, if {@link ShareContentProvider} and {@link CustomContentProvider} would be two different instances, the data would not be accessible to external apps.
@@ -20,9 +20,10 @@ import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import de.dennisguse.opentracks.stats.TripStatistics;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.stats.TripStatistics;
/**
* A track.
@@ -43,22 +44,26 @@ public class Track implements Parcelable {
return new Track[size];
}
};
private long id = -1L;
private String name = "";
private String description = "";
private String category = "";
private long startId = -1L;
private long stopId = -1L;
/*
* The number of location points (present even if the points themselves are
* not loaded)
* The number of location points (present even if the points themselves are not loaded)
*/
private int numberOfPoints = 0;
private String icon = "";
@Deprecated //TODO Is this still used or a left over from Google Drive integration?
private long modifiedTime = -1L;
private TripStatistics tripStatistics = new TripStatistics();
// Location points (which may not have been loaded)
private ArrayList<Location> locations = new ArrayList<>();
private List<Location> locations = new ArrayList<>();
public Track() {
}
@@ -190,7 +195,7 @@ public class Track implements Parcelable {
locations.add(location);
}
public ArrayList<Location> getLocations() {
public List<Location> getLocations() {
return locations;
}
@@ -35,24 +35,25 @@ import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* Track data hub. Receives data from {@link DataSource} and distributes it to
* {@link TrackDataListener} after some processing.
* Track data hub. Receives data from {@link DataSource} and distributes it to {@link TrackDataListener} after some processing.
*
* @author Rodrigo Damazio
*/
public class TrackDataHub implements DataSourceListener {
/**
* Target number of track points displayed by the map overlay. We may display
* more than this number of points.
* Target number of track points displayed by the map overlay.
* We may display more than this number of points.
*/
public static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
static final int TARGET_DISPLAYED_TRACK_POINTS = 5000;
/**
* Maximum number of waypoints to displayed.
*/
@VisibleForTesting
static final int MAX_DISPLAYED_WAYPOINTS = 128;
private static final int MAX_DISPLAYED_WAYPOINTS = 128;
private static final String TAG = TrackDataHub.class.getSimpleName();
private final Context context;
private final TrackDataManager trackDataManager;
private final ContentProviderUtils contentProviderUtils;
@@ -87,8 +88,7 @@ public class TrackDataHub implements DataSourceListener {
* @param targetNumPoints the target number of points
*/
@VisibleForTesting
TrackDataHub(Context context, TrackDataManager trackDataManager,
ContentProviderUtils contentProviderUtils, int targetNumPoints) {
private TrackDataHub(Context context, TrackDataManager trackDataManager, ContentProviderUtils contentProviderUtils, int targetNumPoints) {
this.context = context;
this.trackDataManager = trackDataManager;
this.contentProviderUtils = contentProviderUtils;
@@ -96,17 +96,11 @@ public class TrackDataHub implements DataSourceListener {
resetSamplingState();
}
/**
* Creates a new instance.
*/
public synchronized static TrackDataHub newInstance(Context context) {
return new TrackDataHub(context, new TrackDataManager(), ContentProviderUtils.Factory.get(
context), TARGET_DISPLAYED_TRACK_POINTS);
}
/**
* Starts.
*/
public void start() {
if (started) {
Log.i(TAG, "TrackDataHub already started, ignoring start.");
@@ -131,9 +125,6 @@ public class TrackDataHub implements DataSourceListener {
});
}
/**
* Stops.
*/
public void stop() {
if (!started) {
Log.i(TAG, "TrackDataHub not started, ignoring stop.");
@@ -151,11 +142,6 @@ public class TrackDataHub implements DataSourceListener {
dataSourceManager = null;
}
/**
* Loads a track.
*
* @param trackId the track id
*/
public void loadTrack(final long trackId) {
runInHandlerThread(new Runnable() {
@Override
@@ -222,8 +208,7 @@ public class TrackDataHub implements DataSourceListener {
* Returns true if the selected track is recording.
*/
public boolean isSelectedTrackRecording() {
return selectedTrackId == recordingTrackId
&& recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
return selectedTrackId == recordingTrackId && recordingTrackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
}
/**
@@ -258,8 +243,7 @@ public class TrackDataHub implements DataSourceListener {
runInHandlerThread(new Runnable() {
@Override
public void run() {
notifyTrackPointsTableUpdate(
true, trackDataManager.getListeners(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE),
notifyTrackPointsTableUpdate(true, trackDataManager.getListeners(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE),
trackDataManager.getListeners(TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
}
});
@@ -270,47 +254,36 @@ public class TrackDataHub implements DataSourceListener {
runInHandlerThread(new Runnable() {
@Override
public void run() {
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.recording_track_id_key))) {
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_id_key))) {
recordingTrackId = PreferencesUtils.getLong(context, R.string.recording_track_id_key);
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(
context, R.string.recording_track_paused_key,
PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_track_paused_key))) {
recordingTrackPaused = PreferencesUtils.getBoolean(context, R.string.recording_track_paused_key, PreferencesUtils.RECORDING_TRACK_PAUSED_DEFAULT);
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.stats_units_key))) {
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.stats_units_key))) {
metricUnits = PreferencesUtils.isMetricUnits(context);
if (key != null) {
for (TrackDataListener trackDataListener :
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
if (trackDataListener.onMetricUnitsChanged(metricUnits)) {
loadDataForListener(trackDataListener);
}
}
}
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.stats_rate_key))) {
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.stats_rate_key))) {
reportSpeed = PreferencesUtils.isReportSpeed(context);
if (key != null) {
for (TrackDataListener trackDataListener :
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
if (trackDataListener.onReportSpeedChanged(reportSpeed)) {
loadDataForListener(trackDataListener);
}
}
}
}
if (key == null
|| key.equals(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key))) {
recordingGpsAccuracy = PreferencesUtils.getInt(context, R.string.recording_gps_accuracy_key,
PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
if (key == null || key.equals(PreferencesUtils.getKey(context, R.string.recording_gps_accuracy_key))) {
recordingGpsAccuracy = PreferencesUtils.getInt(context, R.string.recording_gps_accuracy_key, PreferencesUtils.RECORDING_GPS_ACCURACY_DEFAULT);
if (key != null) {
for (TrackDataListener trackDataListener :
trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
for (TrackDataListener trackDataListener : trackDataManager.getListeners(TrackDataType.PREFERENCE)) {
if (trackDataListener.onRecordingGpsAccuracy(recordingGpsAccuracy)) {
loadDataForListener(trackDataListener);
}
@@ -359,7 +332,7 @@ public class TrackDataHub implements DataSourceListener {
}
/**
* Loads data for a listener. To be run in the {@link #handler} thread.
* Loads data for a listener; to be run in the {@link #handler} thread.
*
* @param trackDataListener the track data listener.
*/
@@ -386,8 +359,7 @@ public class TrackDataHub implements DataSourceListener {
if (isOnlyListener) {
resetSamplingState();
}
Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners
: Collections.<TrackDataListener>emptySet();
Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners : Collections.<TrackDataListener>emptySet();
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
}
@@ -397,7 +369,7 @@ public class TrackDataHub implements DataSourceListener {
}
/**
* Notifies track table update. To be run in the {@link #handler} thread.
* Notifies track table update; to be run in the {@link #handler} thread.
*
* @param trackDataListeners the track data listeners to notify
*/
@@ -412,9 +384,8 @@ public class TrackDataHub implements DataSourceListener {
}
/**
* Notifies waypoint table update. Currently, reloads all the waypoints up to
* {@link #MAX_DISPLAYED_WAYPOINTS}. To be run in the {@link #handler}
* thread.
* Notifies waypoint table update.
* Currently, reloads all the waypoints up to {@link #MAX_DISPLAYED_WAYPOINTS}. To be run in the {@link #handler} thread.
*
* @param trackDataListeners the track data listeners to notify
*/
@@ -448,18 +419,17 @@ public class TrackDataHub implements DataSourceListener {
}
/**
* Notifies track points table update. To be run in the {@link #handler}
* thread.
* Notifies track points table update; to be run in the {@link #handler} thread.
*
* @param updateSamplingState true to update the sampling state
* @param sampledInListeners the sampled-in listeners
* @param sampledOutListeners the sampled-out listeners
*/
private void notifyTrackPointsTableUpdate(boolean updateSamplingState,
Set<TrackDataListener> sampledInListeners, Set<TrackDataListener> sampledOutListeners) {
private void notifyTrackPointsTableUpdate(boolean updateSamplingState, Set<TrackDataListener> sampledInListeners, Set<TrackDataListener> sampledOutListeners) {
if (sampledInListeners.isEmpty() && sampledOutListeners.isEmpty()) {
return;
}
if (updateSamplingState && numLoadedPoints >= targetNumPoints) {
// Reload and resample the track at a lower frequency.
Log.i(TAG, "Resampling track after " + numLoadedPoints + " points.");
@@ -477,11 +447,8 @@ public class TrackDataHub implements DataSourceListener {
long lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId);
int samplingFrequency = -1;
boolean includeNextPoint = false;
LocationIterator locationIterator = null;
try {
locationIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId,
localLastSeenLocationId + 1, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY);
try (LocationIterator locationIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, localLastSeenLocationId + 1, false, ContentProviderUtils.DEFAULT_LOCATION_FACTORY)) {
while (locationIterator.hasNext()) {
Location location = locationIterator.next();
@@ -509,8 +476,7 @@ public class TrackDataHub implements DataSourceListener {
}
} else {
// Also include the last point if the selected track is not recording.
if (includeNextPoint || (localNumLoadedPoints % samplingFrequency == 0)
|| (locationId == lastTrackPointId && !isSelectedTrackRecording())) {
if (includeNextPoint || (localNumLoadedPoints % samplingFrequency == 0) || (locationId == lastTrackPointId && !isSelectedTrackRecording())) {
includeNextPoint = false;
for (TrackDataListener trackDataListener : sampledInListeners) {
trackDataListener.onSampledInTrackPoint(location);
@@ -525,10 +491,6 @@ public class TrackDataHub implements DataSourceListener {
localNumLoadedPoints++;
localLastSeenLocationId = locationId;
}
} finally {
if (locationIterator != null) {
locationIterator.close();
}
}
if (updateSamplingState) {
@@ -555,7 +517,7 @@ public class TrackDataHub implements DataSourceListener {
* Creates a {@link DataSource}.
*/
@VisibleForTesting
protected DataSource newDataSource() {
private DataSource newDataSource() {
return new DataSource(context);
}
@@ -565,7 +527,7 @@ public class TrackDataHub implements DataSourceListener {
* @param runnable the runnable
*/
@VisibleForTesting
protected void runInHandlerThread(Runnable runnable) {
private void runInHandlerThread(Runnable runnable) {
if (handler == null) {
// Use a Throwable to ensure the stack trace is logged.
Log.d(TAG, "handler is null.", new Throwable());
@@ -57,10 +57,8 @@ public interface TrackDataListener {
void onSegmentSplit(Location location);
/**
* Called when finish sending new track points. This gets called after every
* batch of calls to {@link #onSampledInTrackPoint(Location)},
* {@link #onSampledOutTrackPoint(Location)} and
* {@link #onSegmentSplit(Location)}.
* Called when finish sending new track points.
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(Location)}, {@link #onSampledOutTrackPoint(Location)} and {@link #onSegmentSplit(Location)}.
*/
void onNewTrackPointsDone();
@@ -77,9 +75,8 @@ public interface TrackDataListener {
void onNewWaypoint(Waypoint waypoint);
/**
* Called when finish sending new waypoints. This gets called after every
* batch of calls to {@link #clearWaypoints()} and
* {@link #onNewWaypoint(Waypoint)}.
* Called when finish sending new waypoints.
* This gets called after every batch of calls to {@link #clearWaypoints()} and {@link #onNewWaypoint(Waypoint)}.
*/
void onNewWaypointsDone();
@@ -26,24 +26,19 @@ import java.util.Map;
import java.util.Set;
/**
* Manages register/unregister {@link TrackDataListener} and keeping the state
* for each registered listener.
* Manages register/unregister {@link TrackDataListener} and keeping the state for each registered listener.
*
* @author Rodrigo Damazio
*/
public class TrackDataManager {
class TrackDataManager {
private static final String TAG = TrackDataManager.class.getSimpleName();
// Map of listener to its track data types
private final Map<TrackDataListener, EnumSet<TrackDataType>>
listenerToTypesMap = new HashMap<>();
private final Map<TrackDataListener, EnumSet<TrackDataType>> listenerToTypesMap = new HashMap<>();
// Map of track data type to listeners
private final Map<TrackDataType, Set<TrackDataListener>>
typeToListenersMap = new EnumMap<>(TrackDataType.class);
private final Map<TrackDataType, Set<TrackDataListener>> typeToListenersMap = new EnumMap<>(TrackDataType.class);
public TrackDataManager() {
TrackDataManager() {
for (TrackDataType trackDataType : TrackDataType.values()) {
typeToListenersMap.put(trackDataType, new LinkedHashSet<TrackDataListener>());
}
@@ -55,8 +50,7 @@ public class TrackDataManager {
* @param listener the listener
* @param trackDataTypes the track data types the listener is interested
*/
public void registerListener(
TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
void registerListener(TrackDataListener listener, EnumSet<TrackDataType> trackDataTypes) {
if (listenerToTypesMap.containsKey(listener)) {
Log.w(TAG, "Tried to register a listener that is already registered. Ignore.");
return;
@@ -72,7 +66,7 @@ public class TrackDataManager {
*
* @param listener the listener
*/
public void unregisterListener(TrackDataListener listener) {
void unregisterListener(TrackDataListener listener) {
EnumSet<TrackDataType> removedTypes = listenerToTypesMap.remove(listener);
if (removedTypes == null) {
Log.w(TAG, "Tried to unregister a listener that is not registered. Ignore.");
@@ -88,7 +82,7 @@ public class TrackDataManager {
/**
* Gets the number of {@link TrackDataListener}.
*/
public int getNumberOfListeners() {
int getNumberOfListeners() {
return listenerToTypesMap.size();
}
@@ -97,7 +91,7 @@ public class TrackDataManager {
*
* @param listener the listener
*/
public EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
EnumSet<TrackDataType> getTrackDataTypes(TrackDataListener listener) {
return listenerToTypesMap.get(listener);
}
@@ -106,14 +100,14 @@ public class TrackDataManager {
*
* @param type the type
*/
public Set<TrackDataListener> getListeners(TrackDataType type) {
Set<TrackDataListener> getListeners(TrackDataType type) {
return typeToListenersMap.get(type);
}
/**
* Gets all the registered {@link TrackDataType}.
*/
public EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
EnumSet<TrackDataType> getRegisteredTrackDataTypes() {
EnumSet<TrackDataType> types = EnumSet.noneOf(TrackDataType.class);
for (EnumSet<TrackDataType> value : listenerToTypesMap.values()) {
types.addAll(value);
@@ -27,25 +27,9 @@ import android.provider.BaseColumns;
public interface TracksColumns extends BaseColumns {
String TABLE_NAME = "tracks";
/**
* Tracks provider uri.
*/
Uri CONTENT_URI = Uri.parse(ContentProviderUtils.CONTENT_BASE_URI + "/tracks");
/**
* Track content type.
*/
String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.de.dennisguse.track";
/**
* Track id content type.
*/
String CONTENT_ITEMTYPE = "vnd.android.cursor.item/vnd.de.dennisguse.track";
/**
* Tracks table default sort order.
*/
String DEFAULT_SORT_ORDER = "_id";
// Columns
@@ -42,6 +42,7 @@ public final class Waypoint implements Parcelable {
return new Waypoint[size];
}
};
private long id = -1L;
private String name = "";
private String description = "";
@@ -60,9 +61,7 @@ public final class Waypoint implements Parcelable {
public Waypoint() {
}
public Waypoint(String name, String description, String category, String icon, long trackId,
WaypointType type, double length, long duration, long startId, long stopId, Location location,
TripStatistics tripStatistics, String photoUrl) {
public Waypoint(String name, String description, String category, String icon, long trackId, WaypointType type, double length, long duration, long startId, long stopId, Location location, TripStatistics tripStatistics, String photoUrl) {
this.name = name;
this.description = description;
this.category = category;
@@ -36,8 +36,10 @@ public class WaypointCreationRequest implements Parcelable {
public static final Creator CREATOR = new Creator();
private WaypointType type;
// true if this marker contains the track statistics
private boolean isTrackStatistics;
private String name;
private String category;
private String description;