package me.guillaumin.android.osmtracker.db;
import java.util.ArrayList;
import java.util.List;
import me.guillaumin.android.osmtracker.OSMTracker;
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.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;
/**
* Content provider for track data, using Android
* {@link ContentProvider} mechanism.
*
* @author Nicolas Guillaumin
*
*/
public class TrackContentProvider extends ContentProvider {
private static final String TAG = TrackContentProvider.class.getSimpleName();
/**
* Authority for Uris
*/
public static final String AUTHORITY = OSMTracker.class.getPackage().getName() + ".provider";
/**
* Uri for track
*/
public static final Uri CONTENT_URI_TRACK = Uri.parse("content://" + AUTHORITY + "/" + Schema.TBL_TRACK);
/**
* Uri for the active track
*/
public static final Uri CONTENT_URI_TRACK_ACTIVE = Uri.parse("content://" + AUTHORITY + "/" + Schema.TBL_TRACK + "/active");
/**
* Uri for a specific waypoint
*/
public static final Uri CONTENT_URI_WAYPOINT_UUID = Uri.parse("content://" + AUTHORITY + "/" + Schema.TBL_WAYPOINT + "/uuid");
/**
* tables and joins to be used within a query to get the important informations of a track
*/
private static final String TRACK_TABLES = Schema.TBL_TRACK + " left join " + Schema.TBL_TRACKPOINT + " on " + Schema.TBL_TRACK + "." + Schema.COL_ID + " = " + Schema.TBL_TRACKPOINT + "." + Schema.COL_TRACK_ID;
/**
* the projection to be used to get the important informations of a track
*/
private static final String[] TRACK_TABLES_PROJECTION = {
Schema.TBL_TRACK + "." + Schema.COL_ID + " as " + Schema.COL_ID,
Schema.COL_ACTIVE,
Schema.COL_DIR,
Schema.COL_EXPORT_DATE,
Schema.COL_OSM_UPLOAD_DATE,
Schema.TBL_TRACK + "." + Schema.COL_NAME + " as "+ Schema.COL_NAME,
Schema.COL_DESCRIPTION,
Schema.COL_TAGS,
Schema.COL_OSM_VISIBILITY,
Schema.COL_START_DATE,
"count(" + Schema.TBL_TRACKPOINT + "." + Schema.COL_ID + ") as " + Schema.COL_TRACKPOINT_COUNT,
"(SELECT count("+Schema.TBL_WAYPOINT+"."+Schema.COL_TRACK_ID+") FROM "+Schema.TBL_WAYPOINT+" WHERE "+Schema.TBL_WAYPOINT+"."+Schema.COL_TRACK_ID+" = " + Schema.TBL_TRACK + "." + Schema.COL_ID + ") as " + Schema.COL_WAYPOINT_COUNT
};
/**
* the group by statement that is used for the track statements
*/
private static final String TRACK_TABLES_GROUP_BY = Schema.TBL_TRACK + "." + Schema.COL_ID;
/**
* Uri Matcher
*/
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK, Schema.URI_CODE_TRACK);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/active", Schema.URI_CODE_TRACK_ACTIVE);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/#", Schema.URI_CODE_TRACK_ID);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/#/start", Schema.URI_CODE_TRACK_START);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/#/end", Schema.URI_CODE_TRACK_END);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/#/" + Schema.TBL_WAYPOINT + "s", Schema.URI_CODE_TRACK_WAYPOINTS);
uriMatcher.addURI(AUTHORITY, Schema.TBL_TRACK + "/#/" + Schema.TBL_TRACKPOINT + "s", Schema.URI_CODE_TRACK_TRACKPOINTS);
uriMatcher.addURI(AUTHORITY, Schema.TBL_WAYPOINT + "/uuid/*", Schema.URI_CODE_WAYPOINT_UUID);
}
/**
* @param trackId target track id
* @return Uri for the waypoints of the track
*/
public static final Uri waypointsUri(long trackId) {
return Uri.withAppendedPath(
ContentUris.withAppendedId(CONTENT_URI_TRACK, trackId),
Schema.TBL_WAYPOINT + "s" );
}
/**
* @param trackId target track id
* @return Uri for the trackpoints of the track
*/
public static final Uri trackPointsUri(long trackId) {
return Uri.withAppendedPath(
ContentUris.withAppendedId(CONTENT_URI_TRACK, trackId),
Schema.TBL_TRACKPOINT + "s" );
}
/**
* @param trackId target track id
* @return Uri for the startpoint of the track
*/
public static final Uri trackStartUri(long trackId) {
return Uri.withAppendedPath(
ContentUris.withAppendedId(CONTENT_URI_TRACK, trackId),
"start" );
}
/**
* @param trackId target track id
* @return Uri for the endpoint of the track
*/
public static final Uri trackEndUri(long trackId) {
return Uri.withAppendedPath(
ContentUris.withAppendedId(CONTENT_URI_TRACK, trackId),
"end" );
}
/**
* Database Helper
*/
private DatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.v(TAG, "delete(), uri=" + uri);
int count;
// Select which data type to delete
switch (uriMatcher.match(uri)) {
case Schema.URI_CODE_TRACK:
count = dbHelper.getWritableDatabase().delete(Schema.TBL_TRACK, selection, selectionArgs);
break;
case Schema.URI_CODE_TRACK_ID:
// the URI matches a specific track, delete all related entities
String trackId = Long.toString(ContentUris.parseId(uri));
dbHelper.getWritableDatabase().delete(Schema.TBL_WAYPOINT, Schema.COL_TRACK_ID + " = ?", new String[] {trackId});
dbHelper.getWritableDatabase().delete(Schema.TBL_TRACKPOINT, Schema.COL_TRACK_ID + " = ?", new String[] {trackId});
count = dbHelper.getWritableDatabase().delete(Schema.TBL_TRACK, Schema.COL_ID + " = ?", new String[] {trackId});
break;
case Schema.URI_CODE_WAYPOINT_UUID:
String uuid = uri.getLastPathSegment();
if(uuid != null){
count = dbHelper.getWritableDatabase().delete(Schema.TBL_WAYPOINT, Schema.COL_UUID + " = ?", new String[]{uuid});
}else{
count = 0;
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* Match and get the URI type, if recognized:
* Matches {@link Schema#URI_CODE_TRACK_TRACKPOINTS}, {@link Schema#URI_CODE_TRACK_WAYPOINTS},
* or {@link Schema#URI_CODE_TRACK}.
* @throws IllegalArgumentException if not matched
*/
@Override
public String getType(Uri uri) throws IllegalArgumentException {
Log.v(TAG, "getType(), uri=" + uri);
// Select which type to return
switch (uriMatcher.match(uri)) {
case Schema.URI_CODE_TRACK_TRACKPOINTS:
return ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + OSMTracker.class.getPackage() + "."
+ Schema.TBL_TRACKPOINT;
case Schema.URI_CODE_TRACK_WAYPOINTS:
return ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + OSMTracker.class.getPackage() + "."
+ Schema.TBL_WAYPOINT;
case Schema.URI_CODE_TRACK:
return ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd." + OSMTracker.class.getPackage() + "."
+ Schema.TBL_TRACK;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.v(TAG, "insert(), uri=" + uri + ", values=" + values.toString());
// Select which data type to insert
switch (uriMatcher.match(uri)) {
case Schema.URI_CODE_TRACK_TRACKPOINTS:
// Check that mandatory columns are present.
if (values.containsKey(Schema.COL_TRACK_ID) && values.containsKey(Schema.COL_LONGITUDE)
&& values.containsKey(Schema.COL_LATITUDE) && values.containsKey(Schema.COL_TIMESTAMP)) {
long rowId = dbHelper.getWritableDatabase().insert(Schema.TBL_TRACKPOINT, null, values);
if (rowId > 0) {
Uri trackpointUri = ContentUris.withAppendedId(uri, rowId);
getContext().getContentResolver().notifyChange(trackpointUri, null);
return trackpointUri;
}
} else {
throw new IllegalArgumentException("values should provide " + Schema.COL_LONGITUDE + ", "
+ Schema.COL_LATITUDE + ", " + Schema.COL_TIMESTAMP);
}
break;
case Schema.URI_CODE_TRACK_WAYPOINTS:
// Check that mandatory columns are present.
if (values.containsKey(Schema.COL_TRACK_ID) && values.containsKey(Schema.COL_LONGITUDE)
&& values.containsKey(Schema.COL_LATITUDE) && values.containsKey(Schema.COL_TIMESTAMP) ) {
long rowId = dbHelper.getWritableDatabase().insert(Schema.TBL_WAYPOINT, null, values);
if (rowId > 0) {
Uri waypointUri = ContentUris.withAppendedId(uri, rowId);
getContext().getContentResolver().notifyChange(waypointUri, null);
return waypointUri;
}
} else {
throw new IllegalArgumentException("values should provide " + Schema.COL_LONGITUDE + ", "
+ Schema.COL_LATITUDE + ", " + Schema.COL_TIMESTAMP);
}
break;
case Schema.URI_CODE_TRACK:
if (values.containsKey(Schema.COL_START_DATE)) {
long rowId = dbHelper.getWritableDatabase().insert(Schema.TBL_TRACK, null, values);
if (rowId > 0) {
Uri trackUri = ContentUris.withAppendedId(CONTENT_URI_TRACK, rowId);
getContext().getContentResolver().notifyChange(trackUri, null);
return trackUri;
}
} else {
throw new IllegalArgumentException("values should provide " + Schema.COL_START_DATE);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
return null;
}
@Override
public Cursor query(Uri uri, String[] projection, String selectionIn, String[] selectionArgsIn, String sortOrder) {
Log.v(TAG, "query(), uri=" + uri);
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String selection = selectionIn;
String[] selectionArgs = selectionArgsIn;
String groupBy = null;
String limit = null;
// Select which datatype was requested
switch (uriMatcher.match(uri)) {
case Schema.URI_CODE_TRACK_TRACKPOINTS:
String trackId = uri.getPathSegments().get(1);
qb.setTables(Schema.TBL_TRACKPOINT);
selection = Schema.COL_TRACK_ID + " = ?";
// Deal with any additional selection info provided by the caller
if (null != selectionIn) {
selection += " AND " + selectionIn;
}
List<String> selctionArgsList = new ArrayList<String>();
selctionArgsList.add(trackId);
// Add the callers selection arguments, if any
if (null != selectionArgsIn) {
for (String arg : selectionArgsIn) {
selctionArgsList.add(arg);
}
}
selectionArgs = selctionArgsList.toArray(new String[0]);
// Finished with the temporary selection arguments list. release it for GC
selctionArgsList.clear();
selctionArgsList = null;
break;
case Schema.URI_CODE_TRACK_WAYPOINTS:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
trackId = uri.getPathSegments().get(1);
qb.setTables(Schema.TBL_WAYPOINT);
selection = Schema.COL_TRACK_ID + " = ?";
selectionArgs = new String[] {trackId};
break;
case Schema.URI_CODE_TRACK_START:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
trackId = uri.getPathSegments().get(1);
qb.setTables(Schema.TBL_TRACKPOINT);
selection = Schema.COL_TRACK_ID + " = ?";
selectionArgs = new String[] {trackId};
sortOrder = Schema.COL_ID + " asc";
limit = "1";
break;
case Schema.URI_CODE_TRACK_END:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
trackId = uri.getPathSegments().get(1);
qb.setTables(Schema.TBL_TRACKPOINT);
selection = Schema.COL_TRACK_ID + " = ?";
selectionArgs = new String[] {trackId};
sortOrder = Schema.COL_ID + " desc";
limit = "1";
break;
case Schema.URI_CODE_TRACK:
qb.setTables(TRACK_TABLES);
if (projection == null)
projection = TRACK_TABLES_PROJECTION;
groupBy = TRACK_TABLES_GROUP_BY;
break;
case Schema.URI_CODE_TRACK_ID:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
trackId = uri.getLastPathSegment();
qb.setTables(TRACK_TABLES);
if (projection == null)
projection = TRACK_TABLES_PROJECTION;
groupBy = TRACK_TABLES_GROUP_BY;
selection = Schema.TBL_TRACK + "." + Schema.COL_ID + " = ?";
selectionArgs = new String[] {trackId};
break;
case Schema.URI_CODE_TRACK_ACTIVE:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
qb.setTables(Schema.TBL_TRACK);
selection = Schema.COL_ACTIVE + " = ?";
selectionArgs = new String[] {Integer.toString(Schema.VAL_TRACK_ACTIVE)};
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
Cursor c = qb.query(dbHelper.getReadableDatabase(), projection, selection, selectionArgs, groupBy, null, sortOrder, limit);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int update(Uri uri, ContentValues values, String selectionIn, String[] selectionArgsIn) {
Log.v(TAG, "update(), uri=" + uri);
String table;
String selection = selectionIn;
String[] selectionArgs = selectionArgsIn;
switch (uriMatcher.match(uri)) {
case Schema.URI_CODE_TRACK_WAYPOINTS:
if (selectionIn == null || selectionArgsIn == null) {
// Caller must narrow to a specific waypoint
throw new IllegalArgumentException();
}
table = Schema.TBL_WAYPOINT;
break;
case Schema.URI_CODE_TRACK_ID:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
table = Schema.TBL_TRACK;
String trackId = uri.getLastPathSegment();
selection = Schema.COL_ID + " = ?";
selectionArgs = new String[] {trackId};
break;
case Schema.URI_CODE_TRACK_ACTIVE:
if (selectionIn != null || selectionArgsIn != null) {
// Any selection/selectionArgs will be ignored
throw new UnsupportedOperationException();
}
table = Schema.TBL_TRACK;
selection = Schema.COL_ACTIVE + " = ?";
selectionArgs = new String[] {Integer.toString(Schema.VAL_TRACK_ACTIVE)};
break;
case Schema.URI_CODE_TRACK:
// Dangerous: Will update all the tracks, but necessary for instance
// to switch all the tracks to inactive
table = Schema.TBL_TRACK;
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
int rows = dbHelper.getWritableDatabase().update(table, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return rows;
}
/**
* Represents Data Schema.
*/
public static final class Schema {
public static final String TBL_TRACKPOINT = "trackpoint";
public static final String TBL_WAYPOINT = "waypoint";
public static final String TBL_TRACK = "track";
public static final String COL_ID = "_id";
public static final String COL_TRACK_ID = "track_id";
public static final String COL_UUID = "uuid";
public static final String COL_LONGITUDE = "longitude";
public static final String COL_LATITUDE = "latitude";
public static final String COL_SPEED = "speed";
public static final String COL_ELEVATION = "elevation";
public static final String COL_ACCURACY = "accuracy";
public static final String COL_NBSATELLITES = "nb_satellites";
public static final String COL_TIMESTAMP = "point_timestamp";
public static final String COL_NAME = "name";
public static final String COL_DESCRIPTION = "description";
public static final String COL_TAGS = "tags";
public static final String COL_OSM_VISIBILITY = "osm_visibility";
public static final String COL_LINK = "link";
public static final String COL_START_DATE = "start_date";
@Deprecated
public static final String COL_DIR = "directory";
public static final String COL_ACTIVE = "active";
public static final String COL_EXPORT_DATE = "export_date";
public static final String COL_OSM_UPLOAD_DATE = "osm_upload_date";
public static final String COL_COMPASS = "compass_heading";
public static final String COL_COMPASS_ACCURACY = "compass_accuracy";
// virtual colums that are used in some sqls but dont exist in database
public static final String COL_TRACKPOINT_COUNT = "tp_count";
public static final String COL_WAYPOINT_COUNT = "wp_count";
// Codes for UriMatcher
public static final int URI_CODE_TRACK = 3;
public static final int URI_CODE_TRACK_ID = 4;
public static final int URI_CODE_TRACK_WAYPOINTS = 5;
public static final int URI_CODE_TRACK_TRACKPOINTS = 6;
public static final int URI_CODE_TRACK_ACTIVE = 7;
public static final int URI_CODE_WAYPOINT_UUID = 8;
public static final int URI_CODE_TRACK_START = 9;
public static final int URI_CODE_TRACK_END = 10;
public static final int VAL_TRACK_ACTIVE = 1;
public static final int VAL_TRACK_INACTIVE = 0;
}
}