Files
OpenTracks/MyTracksLib/src/com/google/android/apps/mytracks/content/MyTracksProviderUtilsImpl.java
T

1121 lines
38 KiB
Java

/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import static com.google.android.apps.mytracks.lib.MyTracksLibConstants.TAG;
import com.google.android.apps.mytracks.content.Sensor.SensorDataSet;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.protobuf.InvalidProtocolBufferException;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* Helper class providing easy access to locations and tracks in the
* MyTracksProvider. All static members.
*
* @author Leif Hendrik Wilden
*/
public class MyTracksProviderUtilsImpl implements MyTracksProviderUtils {
private final ContentResolver contentResolver;
private int defaultCursorBatchSize = 2000;
public MyTracksProviderUtilsImpl(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
/**
* Creates the ContentValues for a given location object.
*
* @param location a given location
* @param trackId the id of the track it belongs to
* @return a filled in ContentValues object
*/
private static ContentValues createContentValues(Location location, long trackId) {
ContentValues values = new ContentValues();
values.put(TrackPointsColumns.TRACKID, trackId);
values.put(TrackPointsColumns.LONGITUDE, (int) (location.getLongitude() * 1E6));
values.put(TrackPointsColumns.LATITUDE, (int) (location.getLatitude() * 1E6));
// This is an ugly hack for Samsung phones that don't properly populate the
// time field.
values.put(TrackPointsColumns.TIME,
(location.getTime() == 0) ? System.currentTimeMillis() : location.getTime());
if (location.hasAltitude()) {
values.put(TrackPointsColumns.ALTITUDE, location.getAltitude());
}
if (location.hasAccuracy()) {
values.put(TrackPointsColumns.ACCURACY, location.getAccuracy());
}
if (location.hasSpeed()) {
values.put(TrackPointsColumns.SPEED, location.getSpeed());
}
if (location.hasBearing()) {
values.put(TrackPointsColumns.BEARING, location.getBearing());
}
if (location instanceof MyTracksLocation) {
MyTracksLocation mtLocation = (MyTracksLocation) location;
if (mtLocation.getSensorDataSet() != null) {
values.put(TrackPointsColumns.SENSOR, mtLocation.getSensorDataSet().toByteArray());
}
}
return values;
}
@Override
public ContentValues createContentValues(Track track) {
ContentValues values = new ContentValues();
TripStatistics stats = track.getTripStatistics();
// Values id < 0 indicate no id is available:
if (track.getId() >= 0) {
values.put(TracksColumns._ID, track.getId());
}
values.put(TracksColumns.NAME, track.getName());
values.put(TracksColumns.DESCRIPTION, track.getDescription());
values.put(TracksColumns.CATEGORY, track.getCategory());
values.put(TracksColumns.STARTID, track.getStartId());
values.put(TracksColumns.STOPID, track.getStopId());
values.put(TracksColumns.STARTTIME, stats.getStartTime());
values.put(TracksColumns.STOPTIME, stats.getStopTime());
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
values.put(TracksColumns.TOTALDISTANCE, stats.getTotalDistance());
values.put(TracksColumns.TOTALTIME, stats.getTotalTime());
values.put(TracksColumns.MOVINGTIME, stats.getMovingTime());
values.put(TracksColumns.MINLAT, stats.getBottom());
values.put(TracksColumns.MAXLAT, stats.getTop());
values.put(TracksColumns.MINLON, stats.getLeft());
values.put(TracksColumns.MAXLON, stats.getRight());
values.put(TracksColumns.AVGSPEED, stats.getAverageSpeed());
values.put(TracksColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
values.put(TracksColumns.MAXSPEED, stats.getMaxSpeed());
values.put(TracksColumns.MINELEVATION, stats.getMinElevation());
values.put(TracksColumns.MAXELEVATION, stats.getMaxElevation());
values.put(TracksColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
values.put(TracksColumns.MINGRADE, stats.getMinGrade());
values.put(TracksColumns.MAXGRADE, stats.getMaxGrade());
values.put(TracksColumns.MAPID, track.getMapId());
values.put(TracksColumns.TABLEID, track.getTableId());
values.put(TracksColumns.ICON, track.getIcon());
return values;
}
private static ContentValues createContentValues(Waypoint waypoint) {
ContentValues values = new ContentValues();
// Values id < 0 indicate no id is available:
if (waypoint.getId() >= 0) {
values.put(WaypointsColumns._ID, waypoint.getId());
}
values.put(WaypointsColumns.NAME, waypoint.getName());
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription());
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
values.put(WaypointsColumns.ICON, waypoint.getIcon());
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId());
values.put(WaypointsColumns.TYPE, waypoint.getType());
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
values.put(WaypointsColumns.STARTID, waypoint.getStartId());
values.put(WaypointsColumns.STOPID, waypoint.getStopId());
TripStatistics stats = waypoint.getStatistics();
if (stats != null) {
values.put(WaypointsColumns.TOTALDISTANCE, stats.getTotalDistance());
values.put(WaypointsColumns.TOTALTIME, stats.getTotalTime());
values.put(WaypointsColumns.MOVINGTIME, stats.getMovingTime());
values.put(WaypointsColumns.AVGSPEED, stats.getAverageSpeed());
values.put(WaypointsColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
values.put(WaypointsColumns.MAXSPEED, stats.getMaxSpeed());
values.put(WaypointsColumns.MINELEVATION, stats.getMinElevation());
values.put(WaypointsColumns.MAXELEVATION, stats.getMaxElevation());
values.put(WaypointsColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
values.put(WaypointsColumns.MINGRADE, stats.getMinGrade());
values.put(WaypointsColumns.MAXGRADE, stats.getMaxGrade());
values.put(WaypointsColumns.STARTTIME, stats.getStartTime());
}
Location location = waypoint.getLocation();
if (location != null) {
values.put(WaypointsColumns.LATITUDE,
(int) (location.getLatitude() * 1E6));
values.put(WaypointsColumns.LONGITUDE,
(int) (location.getLongitude() * 1E6));
values.put(WaypointsColumns.TIME, location.getTime());
if (location.hasAltitude()) {
values.put(WaypointsColumns.ALTITUDE, location.getAltitude());
}
if (location.hasBearing()) {
values.put(WaypointsColumns.BEARING, location.getBearing());
}
if (location.hasAccuracy()) {
values.put(WaypointsColumns.ACCURACY, location.getAccuracy());
}
if (location.hasSpeed()) {
values.put(WaypointsColumns.SPEED, location.getSpeed());
}
}
return values;
}
@Override
public Location createLocation(Cursor cursor) {
Location location = new MyTracksLocation("");
fillLocation(cursor, location);
return location;
}
/**
* A cache of track column indices.
*/
private static class CachedTrackColumnIndices {
public final int idxId;
public final int idxLongitude;
public final int idxLatitude;
public final int idxTime;
public final int idxAltitude;
public final int idxAccuracy;
public final int idxSpeed;
public final int idxBearing;
public final int idxSensor;
public CachedTrackColumnIndices(Cursor cursor) {
idxId = cursor.getColumnIndex(TrackPointsColumns._ID);
idxLongitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
idxTime = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
idxAltitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
idxSensor = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR);
}
}
private void fillLocation(
Cursor cursor, CachedTrackColumnIndices columnIndices, Location location) {
location.reset();
if (!cursor.isNull(columnIndices.idxLongitude)) {
location.setLongitude(1. * cursor.getInt(columnIndices.idxLongitude) / 1E6);
}
if (!cursor.isNull(columnIndices.idxLatitude)) {
location.setLatitude(1. * cursor.getInt(columnIndices.idxLatitude) / 1E6);
}
if (!cursor.isNull(columnIndices.idxTime)) {
location.setTime(cursor.getLong(columnIndices.idxTime));
}
if (!cursor.isNull(columnIndices.idxAltitude)) {
location.setAltitude(cursor.getFloat(columnIndices.idxAltitude));
}
if (!cursor.isNull(columnIndices.idxAccuracy)) {
location.setAccuracy(cursor.getFloat(columnIndices.idxAccuracy));
}
if (!cursor.isNull(columnIndices.idxSpeed)) {
location.setSpeed(cursor.getFloat(columnIndices.idxSpeed));
}
if (!cursor.isNull(columnIndices.idxBearing)) {
location.setBearing(cursor.getFloat(columnIndices.idxBearing));
}
if (location instanceof MyTracksLocation && !cursor.isNull(columnIndices.idxSensor)) {
MyTracksLocation myTracksLocation = (MyTracksLocation) location;
try {
myTracksLocation.setSensorDataSet(
SensorDataSet.parseFrom(cursor.getBlob(columnIndices.idxSensor)));
} catch (InvalidProtocolBufferException e) {
Log.w(TAG, "Failed to parse sensor data.", e);
}
}
}
@Override
public void fillLocation(Cursor cursor, Location location) {
CachedTrackColumnIndices columnIndicies = new CachedTrackColumnIndices(cursor);
fillLocation(cursor, columnIndicies, location);
}
@Override
public Track createTrack(Cursor cursor) {
int idxId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int idxName = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int idxDescription = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
int idxCategory = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
int idxStartId = cursor.getColumnIndexOrThrow(TracksColumns.STARTID);
int idxStopId = cursor.getColumnIndexOrThrow(TracksColumns.STOPID);
int idxStartTime = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
int idxStopTime = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
int idxNumPoints = cursor.getColumnIndexOrThrow(TracksColumns.NUMPOINTS);
int idxTotalDistance = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
int idxTotalTime = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
int idxMovingTime = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
int idxMinlat = cursor.getColumnIndexOrThrow(TracksColumns.MINLAT);
int idxMaxlat = cursor.getColumnIndexOrThrow(TracksColumns.MAXLAT);
int idxMinlon = cursor.getColumnIndexOrThrow(TracksColumns.MINLON);
int idxMaxlon = cursor.getColumnIndexOrThrow(TracksColumns.MAXLON);
int idxMaxSpeed = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
int idxMinElevation = cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
int idxMaxElevation = cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
int idxElevationGain = cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONGAIN);
int idxMinGrade = cursor.getColumnIndexOrThrow(TracksColumns.MINGRADE);
int idxMaxGrade = cursor.getColumnIndexOrThrow(TracksColumns.MAXGRADE);
int idxMapId = cursor.getColumnIndexOrThrow(TracksColumns.MAPID);
int idxTableId = cursor.getColumnIndexOrThrow(TracksColumns.TABLEID);
int idxIcon = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
Track track = new Track();
TripStatistics tripStatistics = track.getTripStatistics();
if (!cursor.isNull(idxId)) {
track.setId(cursor.getLong(idxId));
}
if (!cursor.isNull(idxName)) {
track.setName(cursor.getString(idxName));
}
if (!cursor.isNull(idxDescription)) {
track.setDescription(cursor.getString(idxDescription));
}
if (!cursor.isNull(idxCategory)) {
track.setCategory(cursor.getString(idxCategory));
}
if (!cursor.isNull(idxStartId)) {
track.setStartId(cursor.getInt(idxStartId));
}
if (!cursor.isNull(idxStopId)) {
track.setStopId(cursor.getInt(idxStopId));
}
if (!cursor.isNull(idxStartTime)) {
tripStatistics.setStartTime(cursor.getLong(idxStartTime));
}
if (!cursor.isNull(idxStopTime)) {
tripStatistics.setStopTime(cursor.getLong(idxStopTime));
}
if (!cursor.isNull(idxNumPoints)) {
track.setNumberOfPoints(cursor.getInt(idxNumPoints));
}
if (!cursor.isNull(idxTotalDistance)) {
tripStatistics.setTotalDistance(cursor.getFloat(idxTotalDistance));
}
if (!cursor.isNull(idxTotalTime)) {
tripStatistics.setTotalTime(cursor.getLong(idxTotalTime));
}
if (!cursor.isNull(idxMovingTime)) {
tripStatistics.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxMinlat)
&& !cursor.isNull(idxMaxlat)
&& !cursor.isNull(idxMinlon)
&& !cursor.isNull(idxMaxlon)) {
int bottom = cursor.getInt(idxMinlat);
int top = cursor.getInt(idxMaxlat);
int left = cursor.getInt(idxMinlon);
int right = cursor.getInt(idxMaxlon);
tripStatistics.setBounds(left, top, right, bottom);
}
if (!cursor.isNull(idxMaxSpeed)) {
tripStatistics.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
}
if (!cursor.isNull(idxMinElevation)) {
tripStatistics.setMinElevation(cursor.getFloat(idxMinElevation));
}
if (!cursor.isNull(idxMaxElevation)) {
tripStatistics.setMaxElevation(cursor.getFloat(idxMaxElevation));
}
if (!cursor.isNull(idxElevationGain)) {
tripStatistics.setTotalElevationGain(cursor.getFloat(idxElevationGain));
}
if (!cursor.isNull(idxMinGrade)) {
tripStatistics.setMinGrade(cursor.getFloat(idxMinGrade));
}
if (!cursor.isNull(idxMaxGrade)) {
tripStatistics.setMaxGrade(cursor.getFloat(idxMaxGrade));
}
if (!cursor.isNull(idxMapId)) {
track.setMapId(cursor.getString(idxMapId));
}
if (!cursor.isNull(idxTableId)) {
track.setTableId(cursor.getString(idxTableId));
}
if (!cursor.isNull(idxIcon)) {
track.setIcon(cursor.getString(idxIcon));
}
return track;
}
@Override
public Waypoint createWaypoint(Cursor cursor) {
int idxId = cursor.getColumnIndexOrThrow(WaypointsColumns._ID);
int idxName = cursor.getColumnIndexOrThrow(WaypointsColumns.NAME);
int idxDescription =
cursor.getColumnIndexOrThrow(WaypointsColumns.DESCRIPTION);
int idxCategory = cursor.getColumnIndexOrThrow(WaypointsColumns.CATEGORY);
int idxIcon = cursor.getColumnIndexOrThrow(WaypointsColumns.ICON);
int idxTrackId = cursor.getColumnIndexOrThrow(WaypointsColumns.TRACKID);
int idxType = cursor.getColumnIndexOrThrow(WaypointsColumns.TYPE);
int idxLength = cursor.getColumnIndexOrThrow(WaypointsColumns.LENGTH);
int idxDuration = cursor.getColumnIndexOrThrow(WaypointsColumns.DURATION);
int idxStartTime = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTTIME);
int idxStartId = cursor.getColumnIndexOrThrow(WaypointsColumns.STARTID);
int idxStopId = cursor.getColumnIndexOrThrow(WaypointsColumns.STOPID);
int idxTotalDistance =
cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALDISTANCE);
int idxTotalTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TOTALTIME);
int idxMovingTime =
cursor.getColumnIndexOrThrow(WaypointsColumns.MOVINGTIME);
int idxMaxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXSPEED);
int idxMinElevation =
cursor.getColumnIndexOrThrow(WaypointsColumns.MINELEVATION);
int idxMaxElevation =
cursor.getColumnIndexOrThrow(WaypointsColumns.MAXELEVATION);
int idxElevationGain =
cursor.getColumnIndexOrThrow(WaypointsColumns.ELEVATIONGAIN);
int idxMinGrade = cursor.getColumnIndexOrThrow(WaypointsColumns.MINGRADE);
int idxMaxGrade = cursor.getColumnIndexOrThrow(WaypointsColumns.MAXGRADE);
int idxLatitude = cursor.getColumnIndexOrThrow(WaypointsColumns.LATITUDE);
int idxLongitude = cursor.getColumnIndexOrThrow(WaypointsColumns.LONGITUDE);
int idxAltitude = cursor.getColumnIndexOrThrow(WaypointsColumns.ALTITUDE);
int idxTime = cursor.getColumnIndexOrThrow(WaypointsColumns.TIME);
int idxBearing = cursor.getColumnIndexOrThrow(WaypointsColumns.BEARING);
int idxAccuracy = cursor.getColumnIndexOrThrow(WaypointsColumns.ACCURACY);
int idxSpeed = cursor.getColumnIndexOrThrow(WaypointsColumns.SPEED);
Waypoint waypoint = new Waypoint();
if (!cursor.isNull(idxId)) {
waypoint.setId(cursor.getLong(idxId));
}
if (!cursor.isNull(idxName)) {
waypoint.setName(cursor.getString(idxName));
}
if (!cursor.isNull(idxDescription)) {
waypoint.setDescription(cursor.getString(idxDescription));
}
if (!cursor.isNull(idxCategory)) {
waypoint.setCategory(cursor.getString(idxCategory));
}
if (!cursor.isNull(idxIcon)) {
waypoint.setIcon(cursor.getString(idxIcon));
}
if (!cursor.isNull(idxTrackId)) {
waypoint.setTrackId(cursor.getLong(idxTrackId));
}
if (!cursor.isNull(idxType)) {
waypoint.setType(cursor.getInt(idxType));
}
if (!cursor.isNull(idxLength)) {
waypoint.setLength(cursor.getDouble(idxLength));
}
if (!cursor.isNull(idxDuration)) {
waypoint.setDuration(cursor.getLong(idxDuration));
}
if (!cursor.isNull(idxStartId)) {
waypoint.setStartId(cursor.getLong(idxStartId));
}
if (!cursor.isNull(idxStopId)) {
waypoint.setStopId(cursor.getLong(idxStopId));
}
TripStatistics stats = new TripStatistics();
boolean hasStats = false;
if (!cursor.isNull(idxStartTime)) {
stats.setStartTime(cursor.getLong(idxStartTime));
hasStats = true;
}
if (!cursor.isNull(idxTotalDistance)) {
stats.setTotalDistance(cursor.getFloat(idxTotalDistance));
hasStats = true;
}
if (!cursor.isNull(idxTotalTime)) {
stats.setTotalTime(cursor.getLong(idxTotalTime));
hasStats = true;
}
if (!cursor.isNull(idxMovingTime)) {
stats.setMovingTime(cursor.getLong(idxMovingTime));
hasStats = true;
}
if (!cursor.isNull(idxMaxSpeed)) {
stats.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
hasStats = true;
}
if (!cursor.isNull(idxMinElevation)) {
stats.setMinElevation(cursor.getFloat(idxMinElevation));
hasStats = true;
}
if (!cursor.isNull(idxMaxElevation)) {
stats.setMaxElevation(cursor.getFloat(idxMaxElevation));
hasStats = true;
}
if (!cursor.isNull(idxElevationGain)) {
stats.setTotalElevationGain(cursor.getFloat(idxElevationGain));
hasStats = true;
}
if (!cursor.isNull(idxMinGrade)) {
stats.setMinGrade(cursor.getFloat(idxMinGrade));
hasStats = true;
}
if (!cursor.isNull(idxMaxGrade)) {
stats.setMaxGrade(cursor.getFloat(idxMaxGrade));
hasStats = true;
}
if (hasStats) {
waypoint.setStatistics(stats);
}
Location location = new Location("");
if (!cursor.isNull(idxLatitude) && !cursor.isNull(idxLongitude)) {
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
location.setLongitude(1. * cursor.getInt(idxLongitude) / 1E6);
}
if (!cursor.isNull(idxAltitude)) {
location.setAltitude(cursor.getFloat(idxAltitude));
}
if (!cursor.isNull(idxTime)) {
location.setTime(cursor.getLong(idxTime));
}
if (!cursor.isNull(idxBearing)) {
location.setBearing(cursor.getFloat(idxBearing));
}
if (!cursor.isNull(idxSpeed)) {
location.setSpeed(cursor.getFloat(idxSpeed));
}
if (!cursor.isNull(idxAccuracy)) {
location.setAccuracy(cursor.getFloat(idxAccuracy));
}
waypoint.setLocation(location);
return waypoint;
}
@Override
public void deleteAllTracks() {
contentResolver.delete(TracksColumns.CONTENT_URI, null, null);
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
null, null);
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
}
@Override
public void deleteTrack(long trackId) {
Track track = getTrack(trackId);
if (track != null) {
contentResolver.delete(TrackPointsColumns.CONTENT_URI,
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
null);
}
contentResolver.delete(WaypointsColumns.CONTENT_URI,
WaypointsColumns.TRACKID + "=" + trackId, null);
contentResolver.delete(
TracksColumns.CONTENT_URI, "_id=" + trackId, null);
}
@Override
public void deleteWaypoint(long waypointId,
DescriptionGenerator descriptionGenerator) {
final Waypoint deletedWaypoint = getWaypoint(waypointId);
if (deletedWaypoint != null
&& deletedWaypoint.getType() == Waypoint.TYPE_STATISTICS) {
final Waypoint nextWaypoint =
getNextStatisticsWaypointAfter(deletedWaypoint);
if (nextWaypoint != null) {
Log.d(TAG, "Correcting marker " + nextWaypoint.getId()
+ " after deleted marker " + deletedWaypoint.getId());
nextWaypoint.getStatistics().merge(deletedWaypoint.getStatistics());
nextWaypoint.setDescription(
descriptionGenerator.generateWaypointDescription(nextWaypoint));
if (!updateWaypoint(nextWaypoint)) {
Log.w(TAG, "Update of marker was unsuccessful.");
}
} else {
Log.d(TAG, "No statistics marker after the deleted one was found.");
}
}
contentResolver.delete(
WaypointsColumns.CONTENT_URI, "_id=" + waypointId, null);
}
@Override
public Waypoint getNextStatisticsWaypointAfter(Waypoint waypoint) {
final String selection = WaypointsColumns._ID + ">" + waypoint.getId()
+ " AND " + WaypointsColumns.TRACKID + "=" + waypoint.getTrackId()
+ " AND " + WaypointsColumns.TYPE + "=" + Waypoint.TYPE_STATISTICS;
final String sortOrder = WaypointsColumns._ID + " LIMIT 1";
Cursor cursor = null;
try {
cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
selection,
null /*selectionArgs*/,
sortOrder);
if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public boolean updateWaypoint(Waypoint waypoint) {
try {
final int rows = contentResolver.update(
WaypointsColumns.CONTENT_URI,
createContentValues(waypoint),
"_id=" + waypoint.getId(),
null /*selectionArgs*/);
return rows == 1;
} catch (RuntimeException e) {
Log.e(TAG, "Caught unexpected exception.", e);
}
return false;
}
/**
* Finds a locations from the provider by the given selection.
*
* @param select a selection argument that identifies a unique location
* @return the fist location matching, or null if not found
*/
private Location findLocationBy(String select) {
Cursor cursor = null;
try {
cursor = contentResolver.query(
TrackPointsColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createLocation(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpeceted exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
/**
* Finds a track from the provider by the given selection.
*
* @param select a selection argument that identifies a unique track
* @return the first track matching, or null if not found
*/
private Track findTrackBy(String select) {
Cursor cursor = null;
try {
cursor = contentResolver.query(
TracksColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public Location getFirstLocation() {
return findLocationBy("_id=(select min(_id) from trackpoints)");
}
@Override
public Location getLastLocation() {
return findLocationBy("_id=(select max(_id) from trackpoints)");
}
@Override
public Waypoint getFirstWaypoint(long trackId) {
if (trackId < 0) {
return null;
}
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
"trackid=" + trackId,
null /*selectionArgs*/,
"_id LIMIT 1");
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return createWaypoint(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return null;
}
@Override
public Waypoint getWaypoint(long waypointId) {
if (waypointId < 0) {
return null;
}
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
"_id=" + waypointId,
null /*selectionArgs*/,
null /*sortOrder*/);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return createWaypoint(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return null;
}
@Override
public long getLastLocationId(long trackId) {
if (trackId < 0) {
return -1;
}
final String[] projection = {"_id"};
Cursor cursor = contentResolver.query(
TrackPointsColumns.CONTENT_URI,
projection,
"_id=(select max(_id) from trackpoints WHERE trackid=" + trackId + ")",
null /*selectionArgs*/,
null /*sortOrder*/);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID));
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public long getFirstWaypointId(long trackId) {
if (trackId < 0) {
return -1;
}
final String[] projection = {"_id"};
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
projection,
"trackid=" + trackId,
null /*selectionArgs*/,
"_id LIMIT 1" /*sortOrder*/);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(
cursor.getColumnIndexOrThrow(WaypointsColumns._ID));
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public long getLastWaypointId(long trackId) {
if (trackId < 0) {
return -1;
}
final String[] projection = {"_id"};
Cursor cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI,
projection,
WaypointsColumns.TRACKID + "=" + trackId,
null /*selectionArgs*/,
"_id DESC LIMIT 1" /*sortOrder*/);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(
cursor.getColumnIndexOrThrow(WaypointsColumns._ID));
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public int getNextMarkerNumber(long trackId, boolean statistics) {
if (trackId == -1L) {
return -1;
}
String[] projection = { "_id" };
String selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns.TYPE + "=?";
String[] selectionArgs = new String[] { String.valueOf(trackId),
String.valueOf(statistics ? Waypoint.TYPE_STATISTICS : Waypoint.TYPE_WAYPOINT) };
Cursor cursor = null;
try {
cursor = contentResolver.query(
WaypointsColumns.CONTENT_URI, projection, selection, selectionArgs, null);
if (cursor != null) {
int count = cursor.getCount();
/*
* For statistics markers, the first marker is for the track statistics,
* thus just return the count as the next user visible number.
*/
return statistics ? count : count + 1;
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return -1;
}
@Override
public Track getLastTrack() {
Cursor cursor = null;
try {
cursor = contentResolver.query(
TracksColumns.CONTENT_URI, null, "_id=(select max(_id) from tracks)",
null, null);
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
} catch (RuntimeException e) {
Log.w(TAG, "Caught an unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public long getLastTrackId() {
String[] proj = { TracksColumns._ID };
Cursor cursor = contentResolver.query(
TracksColumns.CONTENT_URI, proj, "_id=(select max(_id) from tracks)",
null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
return cursor.getLong(
cursor.getColumnIndexOrThrow(TracksColumns._ID));
}
} finally {
cursor.close();
}
}
return -1;
}
@Override
public Location getLocation(long id) {
if (id < 0) {
return null;
}
String selection = TrackPointsColumns._ID + "=" + id;
return findLocationBy(selection);
}
@Override
public Cursor getLocationsCursor(long trackId, long minTrackPointId,
int maxLocations, boolean descending) {
if (trackId < 0) {
return null;
}
String selection;
String[] selectionArgs;
if (minTrackPointId >= 0) {
String comparison = descending ? "<=" : ">=";
selection = TrackPointsColumns.TRACKID + "=? AND " + TrackPointsColumns._ID + comparison
+ "?";
selectionArgs = new String[] { String.valueOf(trackId), String.valueOf(minTrackPointId) };
} else {
selection = TrackPointsColumns.TRACKID + "=?";
selectionArgs = new String[] { String.valueOf(trackId) };
}
String sortOrder = "_id " + (descending ? "DESC" : "ASC");
if (maxLocations > 0) {
sortOrder += " LIMIT " + maxLocations;
}
return contentResolver.query(TrackPointsColumns.CONTENT_URI, null, selection, selectionArgs, sortOrder);
}
@Override
public Cursor getWaypointsCursor(long trackId, long minWaypointId,
int maxWaypoints) {
if (trackId < 0) {
return null;
}
String selection;
String[] selectionArgs;
if (minWaypointId > 0) {
selection = WaypointsColumns.TRACKID + "=? AND " + WaypointsColumns._ID + ">=?";
selectionArgs = new String[] {
Long.toString(trackId),
Long.toString(minWaypointId)
};
} else {
selection = WaypointsColumns.TRACKID + "=?";
selectionArgs = new String[] { Long.toString(trackId) };
}
return getWaypointsCursor(selection, selectionArgs, null, maxWaypoints);
}
@Override
public Cursor getWaypointsCursor(String selection, String[] selectionArgs, String order, int maxWaypoints) {
if (order == null) {
order = "_id ASC";
}
if (maxWaypoints > 0) {
order += " LIMIT " + maxWaypoints;
}
return contentResolver.query(
WaypointsColumns.CONTENT_URI, null, selection, selectionArgs, order);
}
@Override
public Track getTrack(long id) {
if (id < 0) {
return null;
}
String select = TracksColumns._ID + "=" + id;
return findTrackBy(select);
}
@Override
public List<Track> getAllTracks() {
Cursor cursor = getTracksCursor(null, null, TracksColumns._ID);
ArrayList<Track> tracks = new ArrayList<Track>();
if (cursor != null) {
tracks.ensureCapacity(cursor.getCount());
if (cursor.moveToFirst()) {
do {
tracks.add(createTrack(cursor));
} while(cursor.moveToNext());
}
cursor.close();
}
return tracks;
}
@Override
public Cursor getTracksCursor(String selection, String[] selectionArgs, String order) {
return contentResolver.query(
TracksColumns.CONTENT_URI, null, selection, selectionArgs, order);
}
@Override
public Uri insertTrack(Track track) {
Log.d(TAG, "MyTracksProviderUtilsImpl.insertTrack");
return contentResolver.insert(TracksColumns.CONTENT_URI,
createContentValues(track));
}
@Override
public Uri insertTrackPoint(Location location, long trackId) {
Log.d(TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
return contentResolver.insert(TrackPointsColumns.CONTENT_URI,
createContentValues(location, trackId));
}
@Override
public int bulkInsertTrackPoints(Location[] locations, int length, long trackId) {
if (length == -1) { length = locations.length; }
ContentValues[] values = new ContentValues[length];
for (int i = 0; i < length; i++) {
values[i] = createContentValues(locations[i], trackId);
}
return contentResolver.bulkInsert(TrackPointsColumns.CONTENT_URI, values);
}
@Override
public Uri insertWaypoint(Waypoint waypoint) {
Log.d(TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
waypoint.setId(-1);
return contentResolver.insert(WaypointsColumns.CONTENT_URI,
createContentValues(waypoint));
}
@Override
public boolean trackExists(long id) {
if (id < 0) {
return false;
}
Cursor cursor = null;
try {
final String[] projection = { TracksColumns._ID };
cursor = contentResolver.query(
TracksColumns.CONTENT_URI,
projection,
TracksColumns._ID + "=" + id/*selection*/,
null/*selectionArgs*/,
null/*sortOrder*/);
if (cursor != null && cursor.moveToNext()) {
return true;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return false;
}
@Override
public void updateTrack(Track track) {
Log.d(TAG, "MyTracksProviderUtilsImpl.updateTrack");
contentResolver.update(TracksColumns.CONTENT_URI,
createContentValues(track), "_id=" + track.getId(), null);
}
@Override
public LocationIterator getLocationIterator(final long trackId, final long startTrackPointId,
final boolean descending, final LocationFactory locationFactory) {
if (locationFactory == null) {
throw new IllegalArgumentException("Expecting non-null locationFactory");
}
return new LocationIterator() {
private long lastTrackPointId = startTrackPointId;
private Cursor cursor = getCursor(startTrackPointId);
private final CachedTrackColumnIndices columnIndices = cursor != null ?
new CachedTrackColumnIndices(cursor) : null;
private Cursor getCursor(long trackPointId) {
return getLocationsCursor(trackId, trackPointId, defaultCursorBatchSize, descending);
}
private boolean advanceCursorToNextBatch() {
long pointId = lastTrackPointId + (descending ? -1 : 1);
Log.d(TAG, "Advancing cursor point ID: " + pointId);
cursor.close();
cursor = getCursor(pointId);
return cursor != null;
}
@Override
public long getLocationId() {
return lastTrackPointId;
}
@Override
public boolean hasNext() {
if (cursor == null) {
return false;
}
if (cursor.isAfterLast()) {
return false;
}
if (cursor.isLast()) {
// If the current batch size was less that max, we can safely return, otherwise
// we need to advance to the next batch.
return cursor.getCount() == defaultCursorBatchSize &&
advanceCursorToNextBatch() && !cursor.isAfterLast();
}
return true;
}
@Override
public Location next() {
if (cursor == null ||
!(cursor.moveToNext() || advanceCursorToNextBatch() || cursor.moveToNext())) {
throw new NoSuchElementException();
}
lastTrackPointId = cursor.getLong(columnIndices.idxId);
Location location = locationFactory.createLocation();
fillLocation(cursor, columnIndices, location);
return location;
}
@Override
public void close() {
if (cursor != null) {
cursor.close();
cursor = null;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
// @VisibleForTesting
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
this.defaultCursorBatchSize = defaultCursorBatchSize;
}
}