Initial import of MyTracks content files and their dependencies.

MyTracksUtils and MyTracksConstants are cut-down versions of the original files
which contain only what's used by the content package, we'll later post the rest
of them.
This commit is contained in:
Rodrigo Damazio
2010-05-07 15:59:33 -03:00
parent 9eac541ce5
commit 826c0495fc
14 changed files with 2851 additions and 0 deletions
@@ -0,0 +1,34 @@
/*
* 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;
/**
* Constants used by the MyTracks application.
*
* @author Leif Hendrik Wilden
*/
public abstract class MyTracksConstants {
/**
* Should be used by all log statements
*/
public static final String TAG = "MyTracks";
/**
* This is an abstract utility class.
*/
private MyTracksConstants() { }
}
@@ -0,0 +1,41 @@
/*
* Copyright 2010 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 java.util.Vector;
/**
* An interface for an object that can generate descriptions of waypoints.
*
* @author Sandor Dornbush
*/
public interface DescriptionGenerator {
/**
* Generate a description of the waypoint.
*/
public String generateWaypointDescription(Waypoint waypoint);
/**
* Generates a description for a track (with information about the
* statistics).
*
* @param track the track
* @return a track description
*/
public String generateTrackDescription(Track track, Vector<Double> distances,
Vector<Double> elevations);
}
@@ -0,0 +1,363 @@
/*
* 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 android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
/**
* A provider that handles recorded (GPS) tracks and their track points.
*
* @author Leif Hendrik Wilden
*/
public class MyTracksProvider extends ContentProvider {
private static final String DATABASE_NAME = "mytracks.db";
private static final int DATABASE_VERSION = 17;
private static final int TRACKPOINTS = 1;
private static final int TRACKPOINTS_ID = 2;
private static final int TRACKS = 3;
private static final int TRACKS_ID = 4;
private static final int WAYPOINTS = 5;
private static final int WAYPOINTS_ID = 6;
private static final String TRACKPOINTS_TABLE = "trackpoints";
private static final String TRACKS_TABLE = "tracks";
private static final String WAYPOINTS_TABLE = "waypoints";
public static final String TAG = "MyTracksProvider";
/**
* Helper which creates or upgrades the database if necessary.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TRACKPOINTS_TABLE + " ("
+ TrackPointsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ TrackPointsColumns.TRACKID + " INTEGER, "
+ TrackPointsColumns.LONGITUDE + " INTEGER, "
+ TrackPointsColumns.LATITUDE + " INTEGER, "
+ TrackPointsColumns.TIME + " INTEGER, "
+ TrackPointsColumns.ALTITUDE + " FLOAT, "
+ TrackPointsColumns.ACCURACY + " FLOAT, "
+ TrackPointsColumns.SPEED + " FLOAT, "
+ TrackPointsColumns.BEARING + " FLOAT);");
db.execSQL("CREATE TABLE " + TRACKS_TABLE + " ("
+ TracksColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ TracksColumns.NAME + " STRING, "
+ TracksColumns.DESCRIPTION + " STRING, "
+ TracksColumns.CATEGORY + " STRING, "
+ TracksColumns.STARTID + " INTEGER, "
+ TracksColumns.STOPID + " INTEGER, "
+ TracksColumns.STARTTIME + " INTEGER, "
+ TracksColumns.STOPTIME + " INTEGER, "
+ TracksColumns.NUMPOINTS + " INTEGER, "
+ TracksColumns.TOTALDISTANCE + " FLOAT, "
+ TracksColumns.TOTALTIME + " INTEGER, "
+ TracksColumns.MOVINGTIME + " INTEGER, "
+ TracksColumns.MINLAT + " INTEGER, "
+ TracksColumns.MAXLAT + " INTEGER, "
+ TracksColumns.MINLON + " INTEGER, "
+ TracksColumns.MAXLON + " INTEGER, "
+ TracksColumns.AVGSPEED + " FLOAT, "
+ TracksColumns.AVGMOVINGSPEED + " FLOAT, "
+ TracksColumns.MAXSPEED + " FLOAT, "
+ TracksColumns.MINELEVATION + " FLOAT, "
+ TracksColumns.MAXELEVATION + " FLOAT, "
+ TracksColumns.ELEVATIONGAIN + " FLOAT, "
+ TracksColumns.MINGRADE + " FLOAT, "
+ TracksColumns.MAXGRADE + " FLOAT, "
+ TracksColumns.MAPID + " STRING);");
db.execSQL("CREATE TABLE " + WAYPOINTS_TABLE + " ("
+ WaypointsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ WaypointsColumns.NAME + " STRING, "
+ WaypointsColumns.DESCRIPTION + " STRING, "
+ WaypointsColumns.CATEGORY + " STRING, "
+ WaypointsColumns.ICON + " STRING, "
+ WaypointsColumns.TRACKID + " INTEGER, "
+ WaypointsColumns.TYPE + " INTEGER, "
+ WaypointsColumns.LENGTH + " FLOAT, "
+ WaypointsColumns.DURATION + " INTEGER, "
+ WaypointsColumns.STARTTIME + " INTEGER, "
+ WaypointsColumns.STARTID + " INTEGER, "
+ WaypointsColumns.STOPID + " INTEGER, "
+ WaypointsColumns.LONGITUDE + " INTEGER, "
+ WaypointsColumns.LATITUDE + " INTEGER, "
+ WaypointsColumns.TIME + " INTEGER, "
+ WaypointsColumns.ALTITUDE + " FLOAT, "
+ WaypointsColumns.ACCURACY + " FLOAT, "
+ WaypointsColumns.SPEED + " FLOAT, "
+ WaypointsColumns.BEARING + " FLOAT, "
+ WaypointsColumns.TOTALDISTANCE + " FLOAT, "
+ WaypointsColumns.TOTALTIME + " INTEGER, "
+ WaypointsColumns.MOVINGTIME + " INTEGER, "
+ WaypointsColumns.AVGSPEED + " FLOAT, "
+ WaypointsColumns.AVGMOVINGSPEED + " FLOAT, "
+ WaypointsColumns.MAXSPEED + " FLOAT, "
+ WaypointsColumns.MINELEVATION + " FLOAT, "
+ WaypointsColumns.MAXELEVATION + " FLOAT, "
+ WaypointsColumns.ELEVATIONGAIN + " FLOAT, "
+ WaypointsColumns.MINGRADE + " FLOAT, "
+ WaypointsColumns.MAXGRADE + " FLOAT);");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TRACKPOINTS_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + TRACKS_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + WAYPOINTS_TABLE);
onCreate(db);
}
}
private final UriMatcher urlMatcher;
private SQLiteDatabase db;
public MyTracksProvider() {
urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY,
"trackpoints", TRACKPOINTS);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY,
"trackpoints/#", TRACKPOINTS_ID);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY, "tracks", TRACKS);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY, "tracks/#", TRACKS_ID);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY, "waypoints", WAYPOINTS);
urlMatcher.addURI(MyTracksProviderUtils.AUTHORITY,
"waypoints/#", WAYPOINTS_ID);
}
@Override
public boolean onCreate() {
DatabaseHelper dbHelper = new DatabaseHelper(getContext());
db = dbHelper.getWritableDatabase();
return db != null;
}
@Override
public int delete(Uri url, String where, String[] selectionArgs) {
if (urlMatcher.match(url) == TRACKPOINTS) {
Log.w(MyTracksProvider.TAG, "provider trackpoints delete!");
int count = db.delete(TRACKPOINTS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else if (urlMatcher.match(url) == TRACKS) {
Log.w(MyTracksProvider.TAG, "provider track delete!");
int count = db.delete(TRACKS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else if (urlMatcher.match(url) == WAYPOINTS) {
Log.w(MyTracksProvider.TAG, "provider waypoint delete!");
int count = db.delete(WAYPOINTS_TABLE, where, selectionArgs);
getContext().getContentResolver().notifyChange(url, null, true);
return count;
} else {
throw new IllegalArgumentException("Unknown URL " + url);
}
}
@Override
public String getType(Uri url) {
switch (urlMatcher.match(url)) {
case TRACKPOINTS:
return TrackPointsColumns.CONTENT_TYPE;
case TRACKPOINTS_ID:
return TrackPointsColumns.CONTENT_ITEMTYPE;
case TRACKS:
return TracksColumns.CONTENT_TYPE;
case TRACKS_ID:
return TracksColumns.CONTENT_ITEMTYPE;
case WAYPOINTS:
return WaypointsColumns.CONTENT_TYPE;
case WAYPOINTS_ID:
return WaypointsColumns.CONTENT_ITEMTYPE;
default:
throw new IllegalArgumentException("Unknown URL " + url);
}
}
@Override
public Uri insert(Uri url, ContentValues initialValues) {
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insert");
ContentValues values;
if (initialValues != null) {
values = initialValues;
} else {
values = new ContentValues();
}
if (urlMatcher.match(url) == TRACKPOINTS) {
return insertTrackPoint(url, values);
} else if (urlMatcher.match(url) == TRACKS) {
return insertTrack(url, values);
} else if (urlMatcher.match(url) == WAYPOINTS) {
return insertWaypoint(url, values);
} else {
throw new IllegalArgumentException("Unknown URL " + url);
}
}
private Uri insertTrackPoint(Uri url, ContentValues values) {
Log.d(MyTracksProvider.TAG, "MyTracksProvider.insertTrackPoint");
boolean hasLat = values.containsKey(TrackPointsColumns.LATITUDE);
boolean hasLong = values.containsKey(TrackPointsColumns.LONGITUDE);
boolean hasTime = values.containsKey(TrackPointsColumns.TIME);
if (!hasLat || !hasLong || !hasTime) {
throw new IllegalArgumentException(
"Latitude, longitude, and time values are required.");
}
long rowId = db.insert(TRACKPOINTS_TABLE, TrackPointsColumns._ID, values);
if (rowId >= 0) {
Uri uri = ContentUris.appendId(
TrackPointsColumns.CONTENT_URI.buildUpon(), rowId).build();
getContext().getContentResolver().notifyChange(url, null, true);
return uri;
}
throw new SQLiteException("Failed to insert row into " + url);
}
private Uri insertTrack(Uri url, ContentValues values) {
boolean hasStartTime = values.containsKey(TracksColumns.STARTTIME);
boolean hasStartId = values.containsKey(TracksColumns.STARTID);
if (!hasStartTime || !hasStartId) {
throw new IllegalArgumentException(
"Both start time and start id values are required.");
}
long rowId = db.insert(TRACKS_TABLE, TracksColumns._ID, values);
if (rowId > 0) {
Uri uri = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), rowId).build();
getContext().getContentResolver().notifyChange(url, null, true);
return uri;
}
throw new SQLException("Failed to insert row into " + url);
}
private Uri insertWaypoint(Uri url, ContentValues values) {
long rowId = db.insert(WAYPOINTS_TABLE, WaypointsColumns._ID, values);
if (rowId > 0) {
Uri uri = ContentUris.appendId(
WaypointsColumns.CONTENT_URI.buildUpon(), rowId).build();
getContext().getContentResolver().notifyChange(url, null, true);
return uri;
}
throw new SQLException("Failed to insert row into " + url);
}
@Override
public Cursor query(
Uri url, String[] projection, String selection, String[] selectionArgs,
String sort) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
int match = urlMatcher.match(url);
String sortOrder = null;
if (match == TRACKPOINTS) {
qb.setTables(TRACKPOINTS_TABLE);
if (sort != null) {
sortOrder = sort;
} else {
sortOrder = TrackPointsColumns.DEFAULT_SORT_ORDER;
}
} else if (match == TRACKPOINTS_ID) {
qb.setTables(TRACKPOINTS_TABLE);
qb.appendWhere("_id=" + url.getPathSegments().get(1));
} else if (match == TRACKS) {
qb.setTables(TRACKS_TABLE);
if (sort != null) {
sortOrder = sort;
} else {
sortOrder = TracksColumns.DEFAULT_SORT_ORDER;
}
} else if (match == TRACKS_ID) {
qb.setTables(TRACKS_TABLE);
qb.appendWhere("_id=" + url.getPathSegments().get(1));
} else if (match == WAYPOINTS) {
qb.setTables(WAYPOINTS_TABLE);
if (sort != null) {
sortOrder = sort;
} else {
sortOrder = WaypointsColumns.DEFAULT_SORT_ORDER;
}
} else if (match == WAYPOINTS_ID) {
qb.setTables(WAYPOINTS_TABLE);
qb.appendWhere("_id=" + url.getPathSegments().get(1));
} else {
throw new IllegalArgumentException("Unknown URL " + url);
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null,
sortOrder);
c.setNotificationUri(getContext().getContentResolver(), url);
return c;
}
@Override
public int update(Uri url, ContentValues values, String where,
String[] selectionArgs) {
int count;
int match = urlMatcher.match(url);
if (match == TRACKPOINTS) {
count = db.update(TRACKPOINTS_TABLE, values, where, selectionArgs);
} else if (match == TRACKPOINTS_ID) {
String segment = url.getPathSegments().get(1);
count = db.update(TRACKPOINTS_TABLE, values, "_id=" + segment
+ (!TextUtils.isEmpty(where)
? " AND (" + where + ')'
: ""),
selectionArgs);
} else if (match == TRACKS) {
count = db.update(TRACKS_TABLE, values, where, selectionArgs);
} else if (match == TRACKS_ID) {
String segment = url.getPathSegments().get(1);
count = db.update(TRACKS_TABLE, values, "_id=" + segment
+ (!TextUtils.isEmpty(where)
? " AND (" + where + ')'
: ""),
selectionArgs);
} else if (match == WAYPOINTS) {
count = db.update(WAYPOINTS_TABLE, values, where, selectionArgs);
} else if (match == WAYPOINTS_ID) {
String segment = url.getPathSegments().get(1);
count = db.update(WAYPOINTS_TABLE, values, "_id=" + segment
+ (!TextUtils.isEmpty(where)
? " AND (" + where + ')'
: ""),
selectionArgs);
} else {
throw new IllegalArgumentException("Unknown URL " + url);
}
getContext().getContentResolver().notifyChange(url, null, true);
return count;
}
}
@@ -0,0 +1,321 @@
/*
* Copyright 2010 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 android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
/**
* Utility to access data from the mytracks content provider.
*
* @author Rodrigo Damazio
*/
public interface MyTracksProviderUtils {
/**
* Authority (first part of URI) for the MyTracks content provider:
*/
public static final String AUTHORITY = "com.google.android.maps.mytracks";
/**
* Deletes all tracks (including track points) from the provider.
*/
void deleteAllTracks();
/**
* Deletes a track with the given track id.
*
* @param trackId the unique track id
*/
void deleteTrack(long trackId);
/**
* Deletes a way point with the given way point id.
* This will also correct the next statistics way point after the deleted one
* to reflect the deletion.
* The generator is needed to stitch together statistics waypoints.
*
* @param waypointId the unique way point id
* @param descriptionGenerator the class to generate descriptions
*/
void deleteWaypoint(long waypointId,
DescriptionGenerator descriptionGenerator);
/**
* Finds the next statistics waypoint after the given waypoint.
*
* @param waypoint a given waypoint
* @return the next statistics waypoint, or null if none found
*/
Waypoint getNextStatisticsWaypointAfter(Waypoint waypoint);
/**
* Updates the waypoint in the provider.
*
* @param waypoint
* @return true if successful
*/
boolean updateWaypoint(Waypoint waypoint);
/**
* Finds the last recorded location from the location provider.
*
* @return the last location, or null if no locations available
*/
Location getLastLocation();
/**
* Finds the first recorded waypoint for a given track from the location
* provider.
* This is a special waypoint that holds the stats for current segment.
*
* @param trackId the id of the track the waypoint belongs to
* @return the first waypoint, or null if no waypoints available
*/
Waypoint getFirstWaypoint(long trackId);
/**
* Finds the given waypoint from the location provider.
*
* @param waypointId
* @return the waypoint, or null if it does not exist
*/
Waypoint getWaypoint(long waypointId);
/**
* Finds the last recorded location id from the track points provider.
*
* @param trackId find last location on this track
* @return the location id, or -1 if no locations available
*/
long getLastLocationId(long trackId);
/**
* Finds the id of the 1st waypoint for a given track.
* The 1st waypoint is special as it contains the stats for the current
* segment.
*
* @param trackId find last location on this track
* @return the waypoint id, or -1 if no waypoints are available
*/
long getFirstWaypointId(long trackId);
/**
* Finds the id of the 1st waypoint for a given track.
* The 1st waypoint is special as it contains the stats for the current
* segment.
*
* @param trackId find last location on this track
* @return the waypoint id, or -1 if no waypoints are available
*/
long getLastWaypointId(long trackId);
/**
* Finds the last recorded track from the track provider.
*
* @return the last track, or null if no tracks available
*/
Track getLastTrack();
/**
* Finds the last recorded track id from the tracks provider.
*
* @return the track id, or -1 if no tracks available
*/
long getLastTrackId();
/**
* Finds a location by given unique id.
*
* @param id the desired id
* @return a Location object, or null if not found
*/
Location getLocation(long id);
/**
* Creates a cursor over the locations in the track points provider which
* iterates over a given range of unique ids.
* Caller gets to own the returned cursor. Don't forget to close it.
*
* @param trackId the id of the track for which to get the points
* @param minTrackPointId the minimum id for the track points
* @param maxLocations maximum number of locations retrieved
* @param descending if true the results will be returned in descending id
* order (latest location first)
* @return A cursor over the selected range of locations
*/
Cursor getLocationsCursor(long trackId, long minTrackPointId,
int maxLocations, boolean descending);
/**
* Creates a cursor over the waypoints of a track.
* Caller gets to own the returned cursor. Don't forget to close it.
*
* @param trackId the id of the track for which to get the points
* @param minWaypointId the minimum id for the track points
* @param maxWaypoints the maximum number of waypoints to return
* @return A cursor over the selected range of locations
*/
Cursor getWaypointsCursor(long trackId, long minWaypointId,
long maxWaypoints);
/**
* Finds a track by given unique track id.
* Note that the returned track object does not have any track points
* attached. Use {@link #getTrackPoints(Track, int)} to load the track points.
*
* @param id desired unique track id
* @return a Track object, or null if not found
*/
Track getTrack(long id);
/**
* Loads the track points for a given track.
*
* @param track assumes that startId and stopId are filled in correctly
* @param maxPoints maximum number of points to load, or -1 for no limit
* (oldest ones will be discarded)
* @return the id of the last location in the track
*/
long getTrackPoints(Track track, int maxPoints);
/**
* Fetches some number of locations for the given track.
*
* This is designed to be used to stream through large tracks without loading
* all points into memory.
*
* @param track to load locations for
* @param buffer an array of locations to fill
*/
void getTrackPoints(Track track, TrackBuffer buffer);
/**
* Creates a cursor over the tracks provider with a given selection.
* Caller gets to own the returned cursor. Don't forget to close it.
*
* @param selection a given selection
* @return a cursor of the selected tracks
*/
Cursor getTracksCursor(String selection);
/**
* Inserts a track in the tracks provider.
* Note: This does not insert any track points.
* Use {@link #insertTrackPoint(Location, long)} to insert them.
*
* @param track the track to insert
* @return the content provider URI for the inserted track
*/
Uri insertTrack(Track track);
/**
* Inserts a track point in the tracks provider.
*
* @param location the location to insert
* @return the content provider URI for the inserted track point
*/
Uri insertTrackPoint(Location location, long trackId);
/**
* Inserts a waypoint in the provider.
*
* @param waypoint the waypoint to insert
* @return the content provider URI for the inserted track
*/
Uri insertWaypoint(Waypoint waypoint);
/**
* Inserts a track including its track points in the provider.
*
* @param track the track to insert
* @return the content provider URI for the inserted track
*/
Uri insertTrackAndTrackPoints(Track track);
/**
* Tests if a track with given id exists.
*
* @param id the unique id
* @return true if track exists
*/
boolean trackExists(long id);
/**
* Updates a track in the content provider.
* Note: This will not update any track points.
*
* @param track a given track
*/
void updateTrack(Track track);
/**
* Creates a location object from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with locations
* @return a new location object
*/
public Location createLocation(Cursor cursor);
/**
* Creates a waypoint object from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with waypoints
* @return a new waypoint object
*/
public Waypoint createWaypoint(Cursor cursor);
/**
* A factory which can produce instances of {@link MyTracksProviderUtils},
* and can be overridden in tests (a.k.a. poor man's guice).
*/
public static class Factory {
private static Factory instance = new Factory();
/**
* Creates and returns an instance of {@link MyTracksProviderUtils} which
* uses the given context to access its data.
*/
public static MyTracksProviderUtils get(Context context) {
return instance.newForContext(context);
}
/**
* Returns the global instance of this factory.
*/
public static Factory getInstance() {
return instance;
}
/**
* Overrides the global instance for this factory, to be used for testing.
* If used, don't forget to set it back to the original value after the
* test is run.
*/
public static void overrideInstance(Factory factory) {
instance = factory;
}
/**
* Creates an instance of {@link MyTracksProviderUtils}.
*/
protected MyTracksProviderUtils newForContext(Context context) {
return new MyTracksProviderUtilsImpl(context);
}
}
}
@@ -0,0 +1,975 @@
/*
* 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 android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.util.Log;
/**
* 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 Context context;
public MyTracksProviderUtilsImpl(Context context) {
this.context = context;
}
/**
* 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.LATITUDE,
(int) (location.getLatitude() * 1E6));
values.put(TrackPointsColumns.LONGITUDE,
(int) (location.getLongitude() * 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.hasBearing()) {
values.put(TrackPointsColumns.BEARING, location.getBearing());
}
if (location.hasAccuracy()) {
values.put(TrackPointsColumns.ACCURACY, location.getAccuracy());
}
if (location.hasSpeed()) {
values.put(TrackPointsColumns.SPEED, location.getSpeed());
}
return values;
}
/**
* Creates the ContentValues for a given Track object.
*
* Note: If the track has an id<0 the id column will not be filled.
*
* @param track a given track object
* @return a filled in ContentValues object
*/
private static ContentValues createContentValues(Track track) {
ContentValues values = new ContentValues();
// 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.MAPID, track.getMapId());
values.put(TracksColumns.CATEGORY, track.getCategory());
values.put(TracksColumns.NUMPOINTS, track.getNumberOfPoints());
values.put(TracksColumns.STARTID, track.getStartId());
values.put(TracksColumns.STARTTIME, track.getStartTime());
values.put(TracksColumns.STOPTIME, track.getStopTime());
values.put(TracksColumns.STOPID, track.getStopId());
values.put(TracksColumns.TOTALDISTANCE, track.getTotalDistance());
values.put(TracksColumns.TOTALTIME, track.getTotalTime());
values.put(TracksColumns.MOVINGTIME, track.getMovingTime());
values.put(TracksColumns.MAXLAT, track.getTop());
values.put(TracksColumns.MINLAT, track.getBottom());
values.put(TracksColumns.MAXLON, track.getRight());
values.put(TracksColumns.MINLON, track.getLeft());
values.put(TracksColumns.AVGSPEED, track.getAverageSpeed());
values.put(TracksColumns.AVGMOVINGSPEED, track.getAverageMovingSpeed());
values.put(TracksColumns.MAXSPEED, track.getMaxSpeed());
values.put(TracksColumns.MINELEVATION, track.getMinElevation());
values.put(TracksColumns.MAXELEVATION, track.getMaxElevation());
values.put(TracksColumns.ELEVATIONGAIN, track.getTotalElevationGain());
values.put(TracksColumns.MINGRADE, track.getMinGrade());
values.put(TracksColumns.MAXGRADE, track.getMaxGrade());
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.STARTTIME, waypoint.getStartTime());
values.put(WaypointsColumns.STARTID, waypoint.getStartId());
values.put(WaypointsColumns.STOPID, waypoint.getStopId());
values.put(WaypointsColumns.TOTALDISTANCE, waypoint.getTotalDistance());
values.put(WaypointsColumns.TOTALTIME, waypoint.getTotalTime());
values.put(WaypointsColumns.MOVINGTIME, waypoint.getMovingTime());
values.put(WaypointsColumns.AVGSPEED, waypoint.getAverageSpeed());
values.put(WaypointsColumns.AVGMOVINGSPEED,
waypoint.getAverageMovingSpeed());
values.put(WaypointsColumns.MAXSPEED, waypoint.getMaxSpeed());
values.put(WaypointsColumns.MINELEVATION, waypoint.getMinElevation());
values.put(WaypointsColumns.MAXELEVATION, waypoint.getMaxElevation());
values.put(WaypointsColumns.ELEVATIONGAIN,
waypoint.getTotalElevationGain());
values.put(WaypointsColumns.MINGRADE, waypoint.getMinGrade());
values.put(WaypointsColumns.MAXGRADE, waypoint.getMaxGrade());
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) {
int idxLatitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.LATITUDE);
int idxLongitude =
cursor.getColumnIndexOrThrow(TrackPointsColumns.LONGITUDE);
int idxAltitude = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE);
int idxTime = cursor.getColumnIndexOrThrow(TrackPointsColumns.TIME);
int idxBearing = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
int idxAccuracy = cursor.getColumnIndexOrThrow(TrackPointsColumns.ACCURACY);
int idxSpeed = cursor.getColumnIndexOrThrow(TrackPointsColumns.SPEED);
Location location = new Location("");
if (!cursor.isNull(idxLatitude)) {
location.setLatitude(1. * cursor.getInt(idxLatitude) / 1E6);
}
if (!cursor.isNull(idxLongitude)) {
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));
}
return location;
}
/**
* Creates a Track object from a given cursor.
*
* @param cursor a cursor pointing at a db or provider with tracks
* @return a new Track object
*/
private static Track createTrack(Cursor cursor) {
int idxId = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int idxName = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int idxDescription =
cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
int idxMapId = cursor.getColumnIndexOrThrow(TracksColumns.MAPID);
int idxCategory = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
int idxStartId = cursor.getColumnIndexOrThrow(TracksColumns.STARTID);
int idxStartTime = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
int idxStopTime = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
int idxStopId = cursor.getColumnIndexOrThrow(TracksColumns.STOPID);
int idxNumPoints = cursor.getColumnIndexOrThrow(TracksColumns.NUMPOINTS);
int idxMaxlat = cursor.getColumnIndexOrThrow(TracksColumns.MAXLAT);
int idxMinlat = cursor.getColumnIndexOrThrow(TracksColumns.MINLAT);
int idxMaxlon = cursor.getColumnIndexOrThrow(TracksColumns.MAXLON);
int idxMinlon = cursor.getColumnIndexOrThrow(TracksColumns.MINLON);
int idxTotalDistance =
cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
int idxTotalTime = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
int idxMovingTime = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
int idxAverageSpeed = cursor.getColumnIndexOrThrow(TracksColumns.AVGSPEED);
int idxAverageMovingSpeed =
cursor.getColumnIndexOrThrow(TracksColumns.AVGMOVINGSPEED);
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);
Track track = new Track();
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(idxMapId)) {
track.setMapId(cursor.getString(idxMapId));
}
if (!cursor.isNull(idxCategory)) {
track.setCategory(cursor.getString(idxCategory));
}
if (!cursor.isNull(idxStartId)) {
track.setStartId(cursor.getInt(idxStartId));
}
if (!cursor.isNull(idxStartTime)) {
track.setStartTime(cursor.getLong(idxStartTime));
}
if (!cursor.isNull(idxStopTime)) {
track.setStopTime(cursor.getLong(idxStopTime));
}
if (!cursor.isNull(idxStopId)) {
track.setStopId(cursor.getInt(idxStopId));
}
if (!cursor.isNull(idxNumPoints)) {
track.setNumberOfPoints(cursor.getInt(idxNumPoints));
}
if (!cursor.isNull(idxTotalDistance)) {
track.setTotalDistance(cursor.getFloat(idxTotalDistance));
}
if (!cursor.isNull(idxTotalTime)) {
track.setTotalTime(cursor.getLong(idxTotalTime));
}
if (!cursor.isNull(idxMovingTime)) {
track.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxMaxlat)
&& !cursor.isNull(idxMinlat)
&& !cursor.isNull(idxMaxlon)
&& !cursor.isNull(idxMinlon)) {
int top = cursor.getInt(idxMaxlat);
int bottom = cursor.getInt(idxMinlat);
int right = cursor.getInt(idxMaxlon);
int left = cursor.getInt(idxMinlon);
track.setBounds(left, top, right, bottom);
}
if (!cursor.isNull(idxAverageSpeed)) {
track.setAverageSpeed(cursor.getFloat(idxAverageSpeed));
}
if (!cursor.isNull(idxAverageMovingSpeed)) {
track.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed));
}
if (!cursor.isNull(idxMaxSpeed)) {
track.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
}
if (!cursor.isNull(idxMinElevation)) {
track.setMinElevation(cursor.getFloat(idxMinElevation));
}
if (!cursor.isNull(idxMaxElevation)) {
track.setMaxElevation(cursor.getFloat(idxMaxElevation));
}
if (!cursor.isNull(idxElevationGain)) {
track.setTotalElevationGain(cursor.getFloat(idxElevationGain));
}
if (!cursor.isNull(idxMinGrade)) {
track.setMinGrade(cursor.getFloat(idxMinGrade));
}
if (!cursor.isNull(idxMaxGrade)) {
track.setMaxGrade(cursor.getFloat(idxMaxGrade));
}
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 idxAverageSpeed =
cursor.getColumnIndexOrThrow(WaypointsColumns.AVGSPEED);
int idxAverageMovingSpeed =
cursor.getColumnIndexOrThrow(WaypointsColumns.AVGMOVINGSPEED);
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(idxStartTime)) {
waypoint.setStartTime(cursor.getLong(idxStartTime));
}
if (!cursor.isNull(idxStartId)) {
waypoint.setStartId(cursor.getLong(idxStartId));
}
if (!cursor.isNull(idxStopId)) {
waypoint.setStopId(cursor.getLong(idxStopId));
}
if (!cursor.isNull(idxTotalDistance)) {
waypoint.setTotalDistance(cursor.getFloat(idxTotalDistance));
}
if (!cursor.isNull(idxTotalTime)) {
waypoint.setTotalTime(cursor.getLong(idxTotalTime));
}
if (!cursor.isNull(idxMovingTime)) {
waypoint.setMovingTime(cursor.getLong(idxMovingTime));
}
if (!cursor.isNull(idxAverageSpeed)) {
waypoint.setAverageSpeed(cursor.getFloat(idxAverageSpeed));
}
if (!cursor.isNull(idxAverageMovingSpeed)) {
waypoint.setAverageMovingSpeed(cursor.getFloat(idxAverageMovingSpeed));
}
if (!cursor.isNull(idxMaxSpeed)) {
waypoint.setMaxSpeed(cursor.getFloat(idxMaxSpeed));
}
if (!cursor.isNull(idxMinElevation)) {
waypoint.setMinElevation(cursor.getFloat(idxMinElevation));
}
if (!cursor.isNull(idxMaxElevation)) {
waypoint.setMaxElevation(cursor.getFloat(idxMaxElevation));
}
if (!cursor.isNull(idxElevationGain)) {
waypoint.setTotalElevationGain(cursor.getFloat(idxElevationGain));
}
if (!cursor.isNull(idxMinGrade)) {
waypoint.setMinGrade(cursor.getFloat(idxMinGrade));
}
if (!cursor.isNull(idxMaxGrade)) {
waypoint.setMaxGrade(cursor.getFloat(idxMaxGrade));
}
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() {
context.getContentResolver().delete(TracksColumns.CONTENT_URI, null, null);
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
null, null);
context.getContentResolver().delete(
WaypointsColumns.CONTENT_URI, null, null);
}
@Override
public void deleteTrack(long trackId) {
Track track = getTrack(trackId);
if (track != null) {
context.getContentResolver().delete(TrackPointsColumns.CONTENT_URI,
"_id>=" + track.getStartId() + " AND _id<=" + track.getStopId(),
null);
}
context.getContentResolver().delete(WaypointsColumns.CONTENT_URI,
WaypointsColumns.TRACKID + "=" + trackId, null);
context.getContentResolver().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(MyTracksProvider.TAG, "Correcting marker " + nextWaypoint.getId()
+ " after deleted marker " + deletedWaypoint.getId());
nextWaypoint.combine(deletedWaypoint);
nextWaypoint.setDescription(
descriptionGenerator.generateWaypointDescription(nextWaypoint));
if (!updateWaypoint(nextWaypoint)) {
Log.w(MyTracksProvider.TAG, "Update of marker was unsuccessful.");
}
} else {
Log.d(MyTracksProvider.TAG,
"No statistics marker after the deleted one was found.");
}
}
context.getContentResolver().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 = context.getContentResolver().query(
WaypointsColumns.CONTENT_URI,
null /*projection*/,
selection,
null /*selectionArgs*/,
sortOrder);
if (cursor != null && cursor.moveToFirst()) {
return createWaypoint(cursor);
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public boolean updateWaypoint(Waypoint waypoint) {
try {
final int rows = context.getContentResolver().update(
WaypointsColumns.CONTENT_URI,
createContentValues(waypoint),
"_id=" + waypoint.getId(),
null /*selectionArgs*/);
return rows == 1;
} catch (RuntimeException e) {
Log.e(MyTracksProvider.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 = context.getContentResolver().query(
TrackPointsColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createLocation(cursor);
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.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 = context.getContentResolver().query(
TracksColumns.CONTENT_URI, null, select, null, null);
if (cursor != null && cursor.moveToNext()) {
return createTrack(cursor);
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public Location getLastLocation() {
return findLocationBy("_id=(select max(_id) from trackpoints)");
}
@Override
public Waypoint getFirstWaypoint(long trackId) {
Cursor cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return null;
}
@Override
public Waypoint getWaypoint(long waypointId) {
Cursor cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return null;
}
@Override
public long getLastLocationId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public long getFirstWaypointId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public long getLastWaypointId(long trackId) {
final String[] projection = {"_id"};
Cursor cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
cursor.close();
}
}
return -1;
}
@Override
public Track getLastTrack() {
Cursor cursor = null;
try {
cursor = context.getContentResolver().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(MyTracksProvider.TAG, "Caught an unexpected exception.", e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return null;
}
@Override
public long getLastTrackId() {
String[] proj = { TracksColumns._ID };
Cursor cursor = context.getContentResolver().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) {
String selection = TrackPointsColumns._ID + "=" + id;
return findLocationBy(selection);
}
@Override
public Cursor getLocationsCursor(long trackId, long minTrackPointId,
int maxLocations, boolean descending) {
String selection;
if (minTrackPointId > 0) {
selection = String.format("%s=%d AND %s>=%d",
TrackPointsColumns.TRACKID, trackId,
TrackPointsColumns._ID, minTrackPointId);
} else {
selection = String.format("%s=%d",
TrackPointsColumns.TRACKID, trackId);
}
String sortOrder = "_id " + (descending ? "DESC" : "ASC");
if (maxLocations > 0) {
sortOrder += " LIMIT " + maxLocations;
}
return context.getContentResolver().query(
TrackPointsColumns.CONTENT_URI, null, selection, null, sortOrder);
}
@Override
public Cursor getWaypointsCursor(long trackId, long minWaypointId,
long maxWaypoints) {
String selection;
if (minWaypointId > 0) {
selection = String.format("%s=%d AND %s>=%d",
WaypointsColumns.TRACKID, trackId,
WaypointsColumns._ID, minWaypointId);
} else {
selection = String.format("%s=%d",
WaypointsColumns.TRACKID, trackId);
}
String sortOrder = "_id ASC";
if (maxWaypoints > 0) {
sortOrder += " LIMIT " + maxWaypoints;
}
return context.getContentResolver().query(
WaypointsColumns.CONTENT_URI, null, selection, null, sortOrder);
}
@Override
public Track getTrack(long id) {
String select = TracksColumns._ID + "=" + id;
return findTrackBy(select);
}
@Override
public long getTrackPoints(Track track, int maxPoints) {
long lastId = -1;
Cursor cursor = getLocationsCursor(track.getId(), -1, maxPoints, true);
if (cursor == null) {
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
return lastId;
}
try {
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
if (cursor.moveToLast()) {
do {
Location location = createLocation(cursor);
if (location == null) {
continue;
}
track.addLocation(location);
lastId = cursor.getLong(idColumnIdx);
} while (cursor.moveToPrevious());
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
} finally {
cursor.close();
}
return lastId;
}
@Override
public void getTrackPoints(Track track, TrackBuffer buffer) {
long startingPoint = buffer.getLastLocationRead() == 0 ? track.getStartId()
: buffer.getLastLocationRead();
buffer.reset();
Cursor cursor = getLocationsCursor(track.getId(),
startingPoint,
buffer.getSize(), false);
final int idColumnIdx =
cursor.getColumnIndexOrThrow(TrackPointsColumns._ID);
if (cursor == null) {
Log.w(MyTracksProvider.TAG, "Cannot get a locations cursor!");
buffer.setInvalid();
return;
}
try {
if (cursor.getCount() == 0) {
Log.w(MyTracksProvider.TAG, "No matching locations found.");
buffer.resetAt(startingPoint + buffer.getSize());
return;
}
if (!cursor.moveToFirst()) {
Log.w(MyTracksProvider.TAG, "Could not move to first.");
buffer.setInvalid();
return;
}
while (cursor.moveToNext()) {
Location location = createLocation(cursor);
if (location == null) {
continue;
}
buffer.add(location, cursor.getLong(idColumnIdx));
}
if (buffer.getLocationsLoaded() == 0) {
Log.w(MyTracksProvider.TAG, "No locations read.");
buffer.resetAt(startingPoint + buffer.getSize());
}
} catch (RuntimeException e) {
Log.w(MyTracksProvider.TAG, "Caught unexpected exception.", e);
} finally {
cursor.close();
}
}
@Override
public Cursor getTracksCursor(String selection) {
Cursor cursor = context.getContentResolver().query(
TracksColumns.CONTENT_URI, null, selection, null, "_id");
return cursor;
}
@Override
public Uri insertTrack(Track track) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
return context.getContentResolver().insert(TracksColumns.CONTENT_URI,
createContentValues(track));
}
@Override
public Uri insertTrackPoint(Location location, long trackId) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrackPoint");
return context.getContentResolver().insert(TrackPointsColumns.CONTENT_URI,
createContentValues(location, trackId));
}
@Override
public Uri insertWaypoint(Waypoint waypoint) {
Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertWaypoint");
waypoint.setId(-1);
return context.getContentResolver().insert(WaypointsColumns.CONTENT_URI,
createContentValues(waypoint));
}
@Override
public Uri insertTrackAndTrackPoints(Track track) {
// Insert the track right away to have its ID
Uri trackUri = insertTrack(track);
long trackId = Long.parseLong(trackUri.getLastPathSegment());
track.setId(trackId);
// Insert all the points (associated with the track ID)
boolean firstPoint = true;
long pointId = -1;
for (Location location : track.getLocations()) {
Uri pointUri = insertTrackPoint(location, trackId);
pointId = Long.parseLong(pointUri.getLastPathSegment());
if (firstPoint) {
track.setStartId(pointId);
firstPoint = false;
}
}
track.setStopId(pointId);
// Update the track with the start and end point IDs
updateTrack(track);
return trackUri;
}
@Override
public boolean trackExists(long id) {
Cursor cursor = null;
try {
final String[] projection = { TracksColumns._ID };
cursor = context.getContentResolver().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(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
context.getContentResolver().update(TracksColumns.CONTENT_URI,
createContentValues(track), "_id=" + track.getId(), null);
}
}
@@ -0,0 +1,360 @@
/*
* 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 android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
/**
* A class representing a (GPS) Track.
*
* @author Leif Hendrik Wilden
*/
public class Track implements Parcelable {
/**
* Creator for a Track object
*/
public class Creator implements Parcelable.Creator<Track> {
public Track createFromParcel(Parcel source) {
Track track = new Track();
track.id = source.readLong();
track.name = source.readString();
track.description = source.readString();
track.mapId = source.readString();
track.category = source.readString();
track.startId = source.readLong();
track.stopId = source.readLong();
track.startTime = source.readLong();
track.stopTime = source.readLong();
track.totalDistance = source.readDouble();
track.totalTime = source.readLong();
track.movingTime = source.readLong();
track.averageSpeed = source.readDouble();
track.averageMovingSpeed = source.readDouble();
track.maxSpeed = source.readDouble();
track.minElevation = source.readDouble();
track.maxElevation = source.readDouble();
track.totalElevationGain = source.readDouble();
track.minGrade = source.readDouble();
track.maxGrade = source.readDouble();
left = source.readInt();
top = source.readInt();
right = source.readInt();
bottom = source.readInt();
track.numberOfPoints = source.readInt();
for (int i = 0; i < track.numberOfPoints; ++i) {
Location loc = source.readParcelable(null);
track.locations.add(loc);
}
return track;
}
public Track[] newArray(int size) {
return new Track[size];
}
}
public static final Creator CREATOR = new Track().new Creator();
/**
* The track points.
*/
private ArrayList<Location> locations = new ArrayList<Location>();
private long id = -1;
private String name = "";
private String description = "";
private String mapId = "";
private long startId = -1;
private long stopId = -1;
private String category = "";
// Fields derived from locations. Use setDerivedFields() to set them:
//-------------------------------------------------------------------
private long startTime = -1;
private long stopTime = -1;
private int numberOfPoints = 0;
private double totalDistance = 0;
private long totalTime = 0;
private long movingTime = 0;
private int left;
private int top;
private int right;
private int bottom;
private double averageSpeed = 0;
private double averageMovingSpeed = 0;
private double maxSpeed = 0;
private double minElevation = 0;
private double maxElevation = 0;
private double totalElevationGain = 0;
private double minGrade = 0;
private double maxGrade = 0;
public Track() {
left = Integer.MAX_VALUE;
top = Integer.MIN_VALUE;
right = Integer.MIN_VALUE;
bottom = Integer.MAX_VALUE;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeString(description);
dest.writeString(mapId);
dest.writeString(category);
dest.writeLong(startId);
dest.writeLong(stopId);
dest.writeLong(startTime);
dest.writeLong(stopTime);
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(averageSpeed);
dest.writeDouble(averageMovingSpeed);
dest.writeDouble(maxSpeed);
dest.writeDouble(minElevation);
dest.writeDouble(maxElevation);
dest.writeDouble(totalElevationGain);
dest.writeDouble(minGrade);
dest.writeDouble(maxGrade);
dest.writeInt(left);
dest.writeInt(top);
dest.writeInt(right);
dest.writeInt(bottom);
dest.writeInt(numberOfPoints);
for (int i = 0; i < numberOfPoints; ++i) {
dest.writeParcelable(locations.get(i), 0);
}
}
// Getters and setters:
//---------------------
public int getLeft() {
return left;
}
public int getTop() {
return top;
}
public int getRight() {
return right;
}
public int getBottom() {
return bottom;
}
public void setBounds(int aLeft, int aTop, int aRight, int aBottom) {
left = aLeft;
top = aTop;
right = aRight;
bottom = aBottom;
}
public int describeContents() {
return 0;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getTotalDistance() {
return totalDistance;
}
public void setTotalDistance(double totalDistance) {
this.totalDistance = totalDistance;
}
public long getTotalTime() {
return totalTime;
}
public void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
public long getMovingTime() {
return movingTime;
}
public void setMovingTime(long movingTime) {
this.movingTime = movingTime;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getStopTime() {
return stopTime;
}
public void setStopTime(long stopTime) {
this.stopTime = stopTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getStartId() {
return startId;
}
public void setStartId(long startId) {
this.startId = startId;
}
public long getStopId() {
return stopId;
}
public void setStopId(long stopId) {
this.stopId = stopId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getMapId() {
return mapId;
}
public void setMapId(String mapId) {
this.mapId = mapId;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public double getAverageSpeed() {
return averageSpeed;
}
public void setAverageSpeed(double averageSpeed) {
this.averageSpeed = averageSpeed;
}
public double getAverageMovingSpeed() {
return averageMovingSpeed;
}
public void setAverageMovingSpeed(double averageMovingSpeed) {
this.averageMovingSpeed = averageMovingSpeed;
}
public double getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getMinElevation() {
return minElevation;
}
public void setMinElevation(double minElevation) {
this.minElevation = minElevation;
}
public double getMaxElevation() {
return maxElevation;
}
public void setMaxElevation(double maxElevation) {
this.maxElevation = maxElevation;
}
public double getTotalElevationGain() {
return totalElevationGain;
}
public void setTotalElevationGain(double totalElevationGain) {
this.totalElevationGain = totalElevationGain;
}
public double getMinGrade() {
return minGrade;
}
public void setMinGrade(double minGrade) {
this.minGrade = minGrade;
}
public double getMaxGrade() {
return maxGrade;
}
public void setMaxGrade(double maxGrade) {
this.maxGrade = maxGrade;
}
public int getNumberOfPoints() {
return numberOfPoints;
}
public void setNumberOfPoints(int numberOfPoints) {
this.numberOfPoints = numberOfPoints;
}
public void addLocation(Location l) {
locations.add(l);
}
public ArrayList<Location> getLocations() {
return locations;
}
public void setLocations(ArrayList<Location> locations) {
this.locations = locations;
}
}
@@ -0,0 +1,133 @@
/*
* Copyright 2009 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 com.google.android.apps.mytracks.util.MyTracksUtils;
import android.location.Location;
/**
* A buffer of Locations from a track.
* This class contains some additional information about the loaded points.
*
* @author Sandor Dornbush
*/
public class TrackBuffer {
/**
* An array of location objects that are part of a track.
*/
private final Location[] buffer;
/**
* The id of the last location read from the track.
*/
private long lastLocationRead = 0;
/**
* The number of locations loaded into the buffer.
*/
private int locationsLoaded = 0;
/**
* Create a TrackBuffer with size elements.
*
* @param size The size of the buffer
*/
public TrackBuffer(int size) {
buffer = new Location[size];
}
/**
* Reset the track to a state with no locations.
*/
public void reset() {
resetAt(0);
}
/**
* Reset the track at the given starting location id.
*/
public void resetAt(long lastLocation) {
lastLocationRead = lastLocation;
locationsLoaded = 0;
}
public void setInvalid() {
lastLocationRead = Integer.MAX_VALUE;
}
/**
* @return The number of locations that can be stored in this buffer
*/
public int getSize() {
return buffer.length;
}
/**
* @param index The index of the location to fetch
* @return The location for the given index
*/
public Location get(int index) {
return buffer[index];
}
/**
* Adds a location to the end of the buffer.
* @param location The location to add
* @param id The id of the location to be added
*/
public void add(Location location, long id) {
buffer[locationsLoaded++] = location;
lastLocationRead = Math.max(lastLocationRead, id);
}
/**
* @return The id of the last location loaded into the buffer
*/
public long getLastLocationRead() {
return lastLocationRead;
}
/**
* @return The number of locations loaded into the buffer
*/
public int getLocationsLoaded() {
return locationsLoaded;
}
/**
* Finds the start location, i.e. the one which is the first point of a
* segment with at least two points.
*
* @return the start location
*/
public Location findStartLocation() {
int numValidLocations = 0;
for (int i = 0; i < getLocationsLoaded(); i++) {
Location location = buffer[i];
if (MyTracksUtils.isValidLocation(location)) {
numValidLocations++;
if (numValidLocations == 2) {
return buffer[i - 1];
}
} else {
numValidLocations = 0;
}
}
return null;
}
}
@@ -0,0 +1,45 @@
/*
* 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 android.net.Uri;
import android.provider.BaseColumns;
/**
* Defines the URI for the track points provider and the available column names
* and content types.
*
* @author Leif Hendrik Wilden
*/
public interface TrackPointsColumns extends BaseColumns {
public static final Uri CONTENT_URI =
Uri.parse("content://com.google.android.maps.mytracks/trackpoints");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.google.trackpoint";
public static final String CONTENT_ITEMTYPE =
"vnd.android.cursor.item/vnd.google.trackpoint";
public static final String DEFAULT_SORT_ORDER = "_id";
/* All columns */
public static final String TRACKID = "trackid";
public static final String LATITUDE = "latitude";
public static final String LONGITUDE = "longitude";
public static final String ALTITUDE = "elevation";
public static final String BEARING = "bearing";
public static final String TIME = "time";
public static final String ACCURACY = "accuracy";
public static final String SPEED = "speed";
}
@@ -0,0 +1,62 @@
/*
* 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 android.net.Uri;
import android.provider.BaseColumns;
/**
* Defines the URI for the tracks provider and the available column names
* and content types.
*
* @author Leif Hendrik Wilden
*/
public interface TracksColumns extends BaseColumns {
public static final Uri CONTENT_URI =
Uri.parse("content://com.google.android.maps.mytracks/tracks");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.google.track";
public static final String CONTENT_ITEMTYPE =
"vnd.android.cursor.item/vnd.google.track";
public static final String DEFAULT_SORT_ORDER = "_id";
/* All columns */
public static final String NAME = "name";
public static final String DESCRIPTION = "description";
public static final String CATEGORY = "category";
public static final String STARTID = "startid";
public static final String STOPID = "stopid";
public static final String STARTTIME = "starttime";
public static final String STOPTIME = "stoptime";
public static final String NUMPOINTS = "numpoints";
public static final String TOTALDISTANCE = "totaldistance";
public static final String TOTALTIME = "totaltime";
public static final String MOVINGTIME = "movingtime";
public static final String AVGSPEED = "avgspeed";
public static final String AVGMOVINGSPEED = "avgmovingspeed";
public static final String MAXSPEED = "maxspeed";
public static final String MINELEVATION = "minelevation";
public static final String MAXELEVATION = "maxelevation";
public static final String ELEVATIONGAIN = "elevationgain";
public static final String MINGRADE = "mingrade";
public static final String MAXGRADE = "maxgrade";
public static final String MINLAT = "minlat";
public static final String MAXLAT = "maxlat";
public static final String MINLON = "minlon";
public static final String MAXLON = "maxlon";
public static final String MAPID = "mapid";
}
@@ -0,0 +1,2 @@
package com.google.android.apps.mytracks.content;
parcelable Waypoint;
@@ -0,0 +1,358 @@
/*
* Copyright 2009 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 android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A way point. It has a location, meta data such as name, description,
* category, and icon, plus it can store track statistics for a "sub-track".
*
* @author Leif Hendrik Wilden
*/
public final class Waypoint implements Parcelable {
/**
* Creator for a Waypoint object
*/
public static class Creator implements Parcelable.Creator<Waypoint> {
public Waypoint createFromParcel(Parcel source) {
Waypoint waypoint = new Waypoint();
waypoint.id = source.readLong();
waypoint.name = source.readString();
waypoint.description = source.readString();
waypoint.category = source.readString();
waypoint.icon = source.readString();
waypoint.trackId = source.readLong();
waypoint.type = source.readInt();
waypoint.length = source.readDouble();
waypoint.duration = source.readLong();
waypoint.startTime = source.readLong();
waypoint.startId = source.readLong();
waypoint.stopId = source.readLong();
byte hasLocation = source.readByte();
if (hasLocation > 0) {
waypoint.location = source.readParcelable(null);
}
waypoint.totalDistance = source.readDouble();
waypoint.totalTime = source.readLong();
waypoint.movingTime = source.readLong();
waypoint.averageSpeed = source.readDouble();
waypoint.averageMovingSpeed = source.readDouble();
waypoint.maxSpeed = source.readDouble();
waypoint.minElevation = source.readDouble();
waypoint.maxElevation = source.readDouble();
waypoint.totalElevationGain = source.readDouble();
waypoint.minGrade = source.readDouble();
waypoint.maxGrade = source.readDouble();
return waypoint;
}
public Waypoint[] newArray(int size) {
return new Waypoint[size];
}
}
public static final Creator CREATOR = new Creator();
public static final int TYPE_WAYPOINT = 0;
public static final int TYPE_STATISTICS = 1;
private long id = -1;
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private long trackId = -1;
private int type = 0;
private Location location;
/** Length along the track in meters. */
private double length = 0;
/** Time along the track in seconds. */
private long duration = 0;
/** Start time of a segment */
private long startTime = -1;
/** Start track point id */
private long startId = -1;
/** Stop track point id */
private long stopId = -1;
private double totalDistance = 0;
private long totalTime = 0;
private long movingTime = 0;
private double averageSpeed = 0;
private double averageMovingSpeed = 0;
private double maxSpeed = 0;
private double minElevation = 0;
private double maxElevation = 0;
private double totalElevationGain = 0;
private double minGrade = 0;
private double maxGrade = 0;
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeString(description);
dest.writeString(category);
dest.writeString(icon);
dest.writeLong(trackId);
dest.writeInt(type);
dest.writeDouble(length);
dest.writeLong(duration);
dest.writeLong(startTime);
dest.writeLong(startId);
dest.writeLong(stopId);
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
if (location != null) {
dest.writeParcelable(location, 0);
}
dest.writeDouble(totalDistance);
dest.writeLong(totalTime);
dest.writeLong(movingTime);
dest.writeDouble(averageSpeed);
dest.writeDouble(averageMovingSpeed);
dest.writeDouble(maxSpeed);
dest.writeDouble(minElevation);
dest.writeDouble(maxElevation);
dest.writeDouble(totalElevationGain);
dest.writeDouble(minGrade);
dest.writeDouble(maxGrade);
}
/**
* Combines the statistics from another (earlier) waypoint with this one.
* This is needed when the user deletes a statistic waypoint.
*
* @param other the other waypoint
*/
public void combine(Waypoint other) {
totalTime += other.totalTime;
movingTime += other.movingTime;
totalDistance += other.totalDistance;
totalElevationGain += other.totalElevationGain;
averageSpeed = totalDistance / totalTime;
averageMovingSpeed = totalDistance / movingTime;
maxSpeed = Math.max(maxSpeed, other.maxSpeed);
minElevation = Math.min(minElevation, other.minElevation);
maxElevation = Math.max(maxElevation, other.maxElevation);
minGrade = Math.min(minGrade, other.minGrade);
maxGrade = Math.max(maxGrade, other.maxGrade);
}
// Getters and setters:
//---------------------
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Location getLocation() {
return location;
}
public void setTrackId(long trackId) {
this.trackId = trackId;
}
public int describeContents() {
return 0;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getTotalDistance() {
return totalDistance;
}
public void setTotalDistance(double totalDistance) {
this.totalDistance = totalDistance;
}
public long getTotalTime() {
return totalTime;
}
public void setTotalTime(long totalTime) {
this.totalTime = totalTime;
}
public long getMovingTime() {
return movingTime;
}
public void setMovingTime(long movingTime) {
this.movingTime = movingTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTrackId() {
return trackId;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public long getDuration() {
return duration;
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getStartTime() {
return startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public long getStartId() {
return startId;
}
public void setStartId(long startId) {
this.startId = startId;
}
public long getStopId() {
return stopId;
}
public void setStopId(long stopId) {
this.stopId = stopId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public double getAverageSpeed() {
return averageSpeed;
}
public void setAverageSpeed(double averageSpeed) {
this.averageSpeed = averageSpeed;
}
public double getAverageMovingSpeed() {
return averageMovingSpeed;
}
public void setAverageMovingSpeed(double averageMovingSpeed) {
this.averageMovingSpeed = averageMovingSpeed;
}
public double getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getMinElevation() {
return minElevation;
}
public void setMinElevation(double minElevation) {
this.minElevation = minElevation;
}
public double getMaxElevation() {
return maxElevation;
}
public void setMaxElevation(double maxElevation) {
this.maxElevation = maxElevation;
}
public double getTotalElevationGain() {
return totalElevationGain;
}
public void setTotalElevationGain(double totalElevationGain) {
this.totalElevationGain = totalElevationGain;
}
public double getMinGrade() {
return minGrade;
}
public void setMinGrade(double minGrade) {
this.minGrade = minGrade;
}
public double getMaxGrade() {
return maxGrade;
}
public void setMaxGrade(double maxGrade) {
this.maxGrade = maxGrade;
}
public void setLocation(Location location) {
this.location = location;
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2009 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 android.net.Uri;
import android.provider.BaseColumns;
/**
* Defines the URI for the tracks provider and the available column names
* and content types.
*
* @author Leif Hendrik Wilden
*/
public interface WaypointsColumns extends BaseColumns {
public static final Uri CONTENT_URI =
Uri.parse("content://com.google.android.maps.mytracks/waypoints");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.google.waypoint";
public static final String CONTENT_ITEMTYPE =
"vnd.android.cursor.item/vnd.google.waypoint";
public static final String DEFAULT_SORT_ORDER = "_id";
/* All columns */
public static final String NAME = "name";
public static final String DESCRIPTION = "description";
public static final String CATEGORY = "category";
public static final String ICON = "icon";
public static final String TRACKID = "trackid";
public static final String TYPE = "type";
public static final String LENGTH = "length";
public static final String DURATION = "duration";
public static final String STARTTIME = "starttime";
public static final String STARTID = "startid";
public static final String STOPID = "stopid";
public static final String LATITUDE = "latitude";
public static final String LONGITUDE = "longitude";
public static final String ALTITUDE = "elevation";
public static final String BEARING = "bearing";
public static final String TIME = "time";
public static final String ACCURACY = "accuracy";
public static final String SPEED = "speed";
public static final String TOTALDISTANCE = "totaldistance";
public static final String TOTALTIME = "totaltime";
public static final String MOVINGTIME = "movingtime";
public static final String AVGSPEED = "avgspeed";
public static final String AVGMOVINGSPEED = "avgmovingspeed";
public static final String MAXSPEED = "maxspeed";
public static final String MINELEVATION = "minelevation";
public static final String MAXELEVATION = "maxelevation";
public static final String ELEVATIONGAIN = "elevationgain";
public static final String MINGRADE = "mingrade";
public static final String MAXGRADE = "maxgrade";
}
@@ -0,0 +1,54 @@
/*
* 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.util;
import com.google.android.apps.mytracks.MyTracksConstants;
import com.google.android.apps.mytracks.content.Track;
import android.location.Location;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* Utility class for decimating tracks at a given level of precision.
*
* @author Leif Hendrik Wilden
*/
public class MyTracksUtils {
/**
* Checks if a given location is a valid (i.e. physically possible) location
* on Earth. Note: The special separator locations (which have latitude =
* 100) will not qualify as valid. Neither will locations with lat=0 and lng=0
* as these are most likely "bad" measurements which often cause trouble.
*
* @param location the location to test
* @return true if the location is a valid location
*/
public static boolean isValidLocation(Location location) {
return location != null && Math.abs(location.getLatitude()) <= 90
&& Math.abs(location.getLongitude()) <= 180;
}
/**
* This is a utility class w/ only static memebers.
*/
private MyTracksUtils() {
}
}
@@ -0,0 +1,34 @@
/*
* 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.util;
/**
* Unit conversion constants.
*
* @author Sandor Dornbush
*/
public abstract class UnitConversions {
public static final double KM_TO_MI = 0.621371192;
public static final double M_TO_FT = 3.2808399;
public static final double MI_TO_M = 1609.344;
public static final double MI_TO_FEET = 5280.0;
public static final double KMH_TO_MPH = 1000 * M_TO_FT / MI_TO_FEET;
public static final double TO_RADIANS = Math.PI / 180.0;
private UnitConversions() {
}
}