/*------------------------------------------------------------------------------
** Ident: Innovation en Inspiration > Google Android
** Author: rene
** Copyright: (c) Jan 22, 2009 Sogeti Nederland B.V. All Rights Reserved.
**------------------------------------------------------------------------------
** Sogeti Nederland B.V. | No part of this file may be reproduced
** Distributed Software Engineering | or transmitted in any form or by any
** Lange Dreef 17 | means, electronic or mechanical, for the
** 4131 NJ Vianen | purpose, without the express written
** The Netherlands | permission of the copyright holder.
*------------------------------------------------------------------------------
*
* This file is part of OpenGPSTracker.
*
* OpenGPSTracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenGPSTracker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenGPSTracker. If not, see <http://www.gnu.org/licenses/>.
*
*/
package edu.stanford.cs.sujogger.db;
import java.io.IOException;
import java.util.List;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.location.Location;
import android.net.Uri;
import android.provider.LiveFolders;
import android.util.Log;
import edu.stanford.cs.sujogger.db.GPStracking.Media;
import edu.stanford.cs.sujogger.db.GPStracking.Segments;
import edu.stanford.cs.sujogger.db.GPStracking.Tracks;
import edu.stanford.cs.sujogger.db.GPStracking.Waypoints;
/**
* Goal of this Content Provider is to make the GPS Tracking information
* uniformly available to this application and even other applications. The
* GPS-tracking database can hold, tracks, segments or waypoints
* <p>
* A track is an actual route taken from start to finish. All the GPS locations
* collected are waypoints. Waypoints taken in sequence without loss of
* GPS-signal are considered connected and are grouped in segments. A route is
* build up out of 1 or more segments.
* <p>
* For example:<br>
* <code>content://edu.stanford.cs.sujogger/tracks</code> is the URI that
* returns all the stored tracks or starts a new track on insert
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2</code> is the URI string
* that would return a single result row, the track with ID = 23.
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments</code> is the URI
* that returns all the stored segments of a track with ID = 2 or starts a new
* segment on insert
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/waypoints</code> is the URI
* that returns all the stored waypoints of a track with ID = 2
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments</code> is the URI
* that returns all the stored segments of a track with ID = 2
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments/3</code> is the
* URI string that would return a single result row, the segment with ID = 3 of
* a track with ID = 2 .
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments/1/waypoints</code>
* is the URI that returns all the waypoints of a segment 1 of track 2.
* <p>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments/1/waypoints/52</code>
* is the URI string that would return a single result row, the waypoint with ID
* = 52
* <p>
* Media is stored under a waypoint and may be queried as:<br>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments/3/waypoints/22/media</code>
* <p>
* All media for a segment can be queried with:<br>
* <code>content://edu.stanford.cs.sujogger/tracks/2/segments/3/media</code>
* <p>
* All media for a track can be queried with:<br>
* <code>content://edu.stanford.cs.sujogger/tracks/2/media</code>
* <p>
* The whole set of collected media may be queried as:<br>
* <code>content://edu.stanford.cs.sujogger/media</code>
* <p>
* A single media is stored with an ID, for instance ID = 12:<br>
* <code>content://edu.stanford.cs.sujogger/media/12</code>
*
* @version $Id: GPStrackingProvider.java 468 2010-03-28 13:47:13Z rcgroot $
* @author rene (c) Jan 22, 2009, Sogeti B.V.
*/
public class GPStrackingProvider extends ContentProvider {
private static final String TAG = "OGT.GPStrackingProvider";
/* Action types as numbers for using the UriMatcher */
private static final int TRACKS = 1;
private static final int TRACK_ID = 2;
private static final int TRACK_MEDIA = 3;
private static final int TRACK_WAYPOINTS = 4;
private static final int SEGMENTS = 5;
private static final int SEGMENT_ID = 6;
private static final int SEGMENT_MEDIA = 7;
private static final int WAYPOINTS = 8;
private static final int WAYPOINT_ID = 9;
private static final int WAYPOINT_MEDIA = 10;
private static final int SEARCH_SUGGEST_ID = 11;
private static final int LIVE_FOLDERS = 12;
private static final int MEDIA = 13;
private static final int MEDIA_ID = 14;
private static final String[] SUGGEST_PROJECTION = new String[] {
Tracks._ID,
Tracks.NAME + " AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
"datetime(" + Tracks.CREATION_TIME + "/1000, 'unixepoch') as "
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
Tracks._ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
};
private static final String[] LIVE_PROJECTION = new String[] {
Tracks._ID + " AS " + LiveFolders._ID,
Tracks.NAME + " AS " + LiveFolders.NAME,
"datetime(" + Tracks.CREATION_TIME + "/1000, 'unixepoch') as "
+ LiveFolders.DESCRIPTION };
private static UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* Although it is documented that in addURI(null, path, 0) "path" should be
* an absolute path this does not seem to work. A relative path gets the
* jobs done and matches an absolute path.
*/
static {
GPStrackingProvider.sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks",
GPStrackingProvider.TRACKS);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#",
GPStrackingProvider.TRACK_ID);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#/media",
GPStrackingProvider.TRACK_MEDIA);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#/waypoints",
GPStrackingProvider.TRACK_WAYPOINTS);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#/segments",
GPStrackingProvider.SEGMENTS);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#/segments/#",
GPStrackingProvider.SEGMENT_ID);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "tracks/#/segments/#/media",
GPStrackingProvider.SEGMENT_MEDIA);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY,
"tracks/#/segments/#/waypoints", GPStrackingProvider.WAYPOINTS);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY,
"tracks/#/segments/#/waypoints/#", GPStrackingProvider.WAYPOINT_ID);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY,
"tracks/#/segments/#/waypoints/#/media", GPStrackingProvider.WAYPOINT_MEDIA);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "media",
GPStrackingProvider.MEDIA);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "media/#",
GPStrackingProvider.MEDIA_ID);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "live_folders/tracks",
GPStrackingProvider.LIVE_FOLDERS);
GPStrackingProvider.sURIMatcher.addURI(GPStracking.AUTHORITY, "search_suggest_query",
GPStrackingProvider.SEARCH_SUGGEST_ID);
}
private DatabaseHelper mDbHelper;
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#delete(android.net.Uri,
* java.lang.String, java.lang.String[])
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match = GPStrackingProvider.sURIMatcher.match(uri);
int affected = 0;
switch (match) {
case GPStrackingProvider.TRACK_ID:
affected = this.mDbHelper.deleteTrack(new Long(uri.getLastPathSegment()).longValue());
break;
case GPStrackingProvider.MEDIA_ID:
affected = this.mDbHelper.deleteMedia(new Long(uri.getLastPathSegment()).longValue());
break;
default:
affected = 0;
break;
}
return affected;
}
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#getType(android.net.Uri)
*/
@Override
public String getType(Uri uri) {
int match = GPStrackingProvider.sURIMatcher.match(uri);
String mime = null;
switch (match) {
case TRACKS:
mime = Tracks.CONTENT_TYPE;
break;
case TRACK_ID:
mime = Tracks.CONTENT_ITEM_TYPE;
break;
case SEGMENTS:
mime = Segments.CONTENT_TYPE;
break;
case SEGMENT_ID:
mime = Segments.CONTENT_ITEM_TYPE;
break;
case WAYPOINTS:
mime = Waypoints.CONTENT_TYPE;
break;
case WAYPOINT_ID:
mime = Waypoints.CONTENT_ITEM_TYPE;
break;
}
return mime;
}
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#insert(android.net.Uri,
* android.content.ContentValues)
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
// Log.d( TAG, "insert on "+uri );
Uri insertedUri = null;
int match = GPStrackingProvider.sURIMatcher.match(uri);
List<String> pathSegments = null;
long trackId;
long segmentId;
long waypointId = -1;
long mediaId;
switch (match) {
case WAYPOINTS:
pathSegments = uri.getPathSegments();
trackId = Integer.parseInt(pathSegments.get(1));
segmentId = Integer.parseInt(pathSegments.get(3));
Location loc = new Location(TAG);
Double latitude = values.getAsDouble(Waypoints.LATITUDE);
Double longitude = values.getAsDouble(Waypoints.LONGITUDE);
Long time = values.getAsLong(Waypoints.TIME);
Float speed = values.getAsFloat(Waypoints.SPEED);
if (time == null) {
time = System.currentTimeMillis();
}
if (speed == null) {
speed = 0f;
}
loc.setLatitude(latitude);
loc.setLongitude(longitude);
loc.setTime(time);
loc.setSpeed(speed);
if (values.containsKey(Waypoints.ACCURACY)) {
loc.setAccuracy(values.getAsFloat(Waypoints.ACCURACY));
}
if (values.containsKey(Waypoints.ALTITUDE)) {
loc.setAltitude(values.getAsDouble(Waypoints.ALTITUDE));
}
if (values.containsKey(Waypoints.BEARING)) {
loc.setBearing(values.getAsFloat(Waypoints.BEARING));
}
waypointId = this.mDbHelper.insertWaypoint(trackId, segmentId, loc);
// Log.d( TAG,
// "Have inserted to segment "+segmentId+" with waypoint "+waypointId
// );
insertedUri = ContentUris.withAppendedId(uri, waypointId);
break;
case WAYPOINT_MEDIA:
pathSegments = uri.getPathSegments();
trackId = Integer.parseInt(pathSegments.get(1));
segmentId = Integer.parseInt(pathSegments.get(3));
waypointId = Integer.parseInt(pathSegments.get(5));
String mediaUri = values.getAsString(Media.URI);
mediaId = this.mDbHelper.insertMedia(trackId, segmentId, waypointId, mediaUri);
insertedUri = ContentUris.withAppendedId(Media.CONTENT_URI, mediaId);
break;
case SEGMENTS:
pathSegments = uri.getPathSegments();
trackId = Integer.parseInt(pathSegments.get(1));
segmentId = this.mDbHelper.toNextSegment(trackId);
insertedUri = ContentUris.withAppendedId(uri, segmentId);
break;
case TRACKS:
String name = (values == null) ? "" : values.getAsString(Tracks.NAME);
trackId = this.mDbHelper.toNextTrack(name);
insertedUri = ContentUris.withAppendedId(uri, trackId);
break;
default:
Log.e(GPStrackingProvider.TAG, "Unable to match the insert URI: " + uri.toString());
insertedUri = null;
break;
}
return insertedUri;
}
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#onCreate()
*/
@Override
public boolean onCreate() {
if (this.mDbHelper == null) {
this.mDbHelper = new DatabaseHelper(getContext());
}
return true;
}
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#query(android.net.Uri,
* java.lang.String[], java.lang.String, java.lang.String[],
* java.lang.String)
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Log.d( TAG, "Query on Uri:"+uri );
int match = GPStrackingProvider.sURIMatcher.match(uri);
String tableName = null;
String whereclause = null;
String sortorder = null;
List<String> pathSegments = uri.getPathSegments();
switch (match) {
case TRACKS:
tableName = Tracks.TABLE;
sortorder = Tracks.CREATION_TIME + " desc";
break;
case TRACK_ID:
tableName = Tracks.TABLE;
whereclause = Tracks._ID + " = " + new Long(pathSegments.get(1)).longValue();
break;
case SEGMENTS:
tableName = Segments.TABLE;
whereclause = Segments.TRACK + " = " + new Long(pathSegments.get(1)).longValue();
break;
case SEGMENT_ID:
tableName = Segments.TABLE;
whereclause = Segments.TRACK + " = " + new Long(pathSegments.get(1)).longValue()
+ " and " + Segments._ID + " = " + new Long(pathSegments.get(3)).longValue();
break;
case WAYPOINTS:
tableName = Waypoints.TABLE;
whereclause = Waypoints.SEGMENT + " = " + new Long(pathSegments.get(3)).longValue();
break;
case WAYPOINT_ID:
tableName = Waypoints.TABLE;
whereclause = Waypoints.SEGMENT + " = " + new Long(pathSegments.get(3)).longValue()
+ " and " + Waypoints._ID + " = " + new Long(pathSegments.get(5)).longValue();
break;
case TRACK_WAYPOINTS:
tableName = Waypoints.TABLE + " INNER JOIN " + Segments.TABLE + " ON " + Segments.TABLE
+ "." + Segments._ID + "==" + Waypoints.SEGMENT;
whereclause = Segments.TRACK + " = " + new Long(pathSegments.get(1)).longValue();
break;
case GPStrackingProvider.MEDIA:
tableName = Media.TABLE;
break;
case GPStrackingProvider.MEDIA_ID:
tableName = Media.TABLE;
whereclause = Media._ID + " = " + new Long(pathSegments.get(1)).longValue();
break;
case TRACK_MEDIA:
tableName = Media.TABLE;
whereclause = Media.TRACK + " = " + new Long(pathSegments.get(1)).longValue();
break;
case SEGMENT_MEDIA:
tableName = Media.TABLE;
whereclause = Media.TRACK + " = " + new Long(pathSegments.get(1)).longValue() + " and "
+ Media.SEGMENT + " = " + new Long(pathSegments.get(3)).longValue();
break;
case WAYPOINT_MEDIA:
tableName = Media.TABLE;
whereclause = Media.TRACK + " = " + new Long(pathSegments.get(1)).longValue() + " and "
+ Media.SEGMENT + " = " + new Long(pathSegments.get(3)).longValue() + " and "
+ Media.WAYPOINT + " = " + new Long(pathSegments.get(5)).longValue();
break;
case SEARCH_SUGGEST_ID:
tableName = Tracks.TABLE;
if (selectionArgs[0] == null || selectionArgs[0].equals("")) {
selection = null;
selectionArgs = null;
sortorder = Tracks.CREATION_TIME + " desc";
}
else {
selectionArgs[0] = "%" + selectionArgs[0] + "%";
}
projection = SUGGEST_PROJECTION;
break;
case LIVE_FOLDERS:
tableName = Tracks.TABLE;
projection = LIVE_PROJECTION;
break;
default:
Log.e(GPStrackingProvider.TAG, "Unable to come to an action in the query uri: "
+ uri.toString());
return null;
}
// SQLiteQueryBuilder is a helper class that creates the
// proper SQL syntax for us.
SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder();
// Set the table we're querying.
qBuilder.setTables(tableName);
// If the query ends in a specific record number, we're
// being asked for a specific record, so set the
// WHERE clause in our query.
if (whereclause != null) {
qBuilder.appendWhere(whereclause);
}
// Make the query.
SQLiteDatabase mDb = this.mDbHelper.openAndGetDb();
Cursor c = qBuilder.query(mDb, projection, selection, selectionArgs, null, null, sortorder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* (non-Javadoc)
*
* @see android.content.ContentProvider#update(android.net.Uri,
* android.content.ContentValues, java.lang.String, java.lang.String[])
*/
@Override
public int update(Uri uri, ContentValues givenValues, String selection, String[] selectionArgs) {
int updates = -1;
int match = GPStrackingProvider.sURIMatcher.match(uri);
String tableName;
String whereclause;
ContentValues args = new ContentValues();
Uri notifyUri;
switch (match) {
case TRACK_ID:
tableName = Tracks.TABLE;
long trackId = new Long(uri.getLastPathSegment()).longValue();
whereclause = Tracks._ID + " = " + trackId;
if (givenValues.getAsString(Tracks.NAME) != null)
args.put(Tracks.NAME, givenValues.getAsString(Tracks.NAME));
if (givenValues.getAsString(Tracks.DURATION) != null)
args.put(Tracks.DURATION, givenValues.getAsString(Tracks.DURATION));
if (givenValues.getAsString(Tracks.DISTANCE) != null)
args.put(Tracks.DISTANCE, givenValues.getAsString(Tracks.DISTANCE));
if (givenValues.getAsString(Tracks.TRACK_ID) != null)
args.put(Tracks.TRACK_ID, givenValues.getAsString(Tracks.TRACK_ID));
if (givenValues.getAsString(Tracks.USER_ID) != null)
args.put(Tracks.USER_ID, givenValues.getAsString(Tracks.USER_ID));
if (givenValues.getAsString(Tracks.IS_PARTNER) != null)
args.put(Tracks.IS_PARTNER, givenValues.getAsString(Tracks.IS_PARTNER));
notifyUri = ContentUris.withAppendedId(Tracks.CONTENT_URI, trackId);
break;
default:
Log.e(GPStrackingProvider.TAG, "Unable to come to an action in the query uri"
+ uri.toString());
return -1;
}
if (args.size() == 0)
return -1;
// Execute the query.
SQLiteDatabase mDb = this.mDbHelper.openAndGetDb();
updates = mDb.update(tableName, args, whereclause, null);
ContentResolver resolver = this.getContext().getContentResolver();
resolver.notifyChange(notifyUri, null);
return updates;
}
}