/**
* Filename: RedpinContentProvider.java (in org.repin.android.provider)
* This file is part of the Redpin project.
*
* Redpin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Redpin 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Redpin. If not, see <http://www.gnu.org/licenses/>.
*
* (c) Copyright ETH Zurich, Pascal Brogle, Philipp Bolliger, 2010, ALL RIGHTS RESERVED.
*
* www.redpin.org
*/
package org.redpin.android.provider;
import org.redpin.android.core.Location;
import org.redpin.android.core.Map;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
/**
* Redpin {@link ContentProvider} for {@link Map}s and {@link Location}s
*
* @author Pascal Brogle (broglep@student.ethz.ch)
*
*/
public class RedpinContentProvider extends ContentProvider {
private SQLiteDatabase mDB;
private DatabaseHelper dbHelper;
private static final String TAG = RedpinContentProvider.class
.getSimpleName();
private static final String DATABASE_NAME = "redpin.db";
private static final int DATABASE_VERSION = 1;
private static final String MAP_TABLE = "map";
private static final String LOCATION_TABLE = "location";
private static final int MAP = 1;
private static final int MAP_ID = 2;
private static final int MAP_LOCATIONS = 3;
private static final int MAP_LOCATIONS_ID = 4;
private static final int LOCATION = 5;
private static final int LOCATION_ID = 6;
private static final UriMatcher URI_MATCHER = new UriMatcher(
UriMatcher.NO_MATCH);
private static final String REMOTE_PARAMETER_DISALOWED = "remote parameter not allowed in URL ";
static {
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Map.PATH_SEGMENT, MAP);
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Map.PATH_SEGMENT + "/#", MAP_ID);
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Map.PATH_SEGMENT + "/#/"
+ RedpinContract.Location.PATH_SEGMENT, MAP_LOCATIONS);
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Map.PATH_SEGMENT + "/#/"
+ RedpinContract.Location.PATH_SEGMENT + "/#",
MAP_LOCATIONS_ID);
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Location.PATH_SEGMENT, LOCATION);
URI_MATCHER.addURI(RedpinContract.AUTHORITY,
RedpinContract.Location.PATH_SEGMENT + "/#", LOCATION_ID);
}
/**
* {@link SQLiteOpenHelper} that handles database creation and version
* management
*
* @see SQLiteOpenHelper
*
* @author Pascal Brogle (broglep@student.ethz.ch)
*
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
/**
* Creates the Helper
*
* @param context
* to use to open or create the database
*/
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + MAP_TABLE + " ("
+ RedpinContract.Map._ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ RedpinContract.Map.REMOTE_ID + " INTEGER,"
+ RedpinContract.Map.NAME + " TEXT,"
+ RedpinContract.Map.URL + " TEXT );");
db.execSQL("CREATE TABLE " + LOCATION_TABLE + " ("
+ RedpinContract.Location._ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ RedpinContract.Location.REMOTE_ID + " INTEGER,"
+ RedpinContract.Location._MAP_ID + " INTEGER,"
+ RedpinContract.Location.SYMBOLIC_ID + " TEXT,"
+ RedpinContract.Location.X + " INTEGER,"
+ RedpinContract.Location.Y + " INTEGER );");
}
/**
* {@inheritDoc}
*/
@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 " + MAP_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + LOCATION_TABLE);
onCreate(db);
}
}
/**
* Opens or creates the database
*
* @return <code>true</code> if successful
*/
@Override
public boolean onCreate() {
dbHelper = new DatabaseHelper(getContext());
mDB = dbHelper.getWritableDatabase();
return mDB != null;
}
/**
* {@inheritDoc}
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count;
String segment;
int match = URI_MATCHER.match(uri);
boolean remote = uri.getQueryParameter(RedpinContract.REMOTE_PARAMETER) != null;
switch (match) {
case MAP:
count = mDB.delete(MAP_TABLE, selection, selectionArgs);
count += mDB.delete(LOCATION_TABLE, "1", null);
Log.d(TAG, "deleting all maps and locations");
break;
case MAP_ID:
if (remote) {
throw new IllegalArgumentException(REMOTE_PARAMETER_DISALOWED
+ uri);
}
segment = uri.getPathSegments().get(1);
count = mDB.delete(MAP_TABLE, RedpinContract.Map._ID
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : ""), selectionArgs);
count += mDB.delete(LOCATION_TABLE, RedpinContract.Location._MAP_ID
+ "=" + segment, null);
Log.d(TAG, "deleting map #" + segment
+ " and corresponding locations");
break;
case MAP_LOCATIONS:
if (remote) {
throw new IllegalArgumentException(REMOTE_PARAMETER_DISALOWED
+ uri);
}
segment = uri.getPathSegments().get(1);
count = mDB.delete(LOCATION_TABLE, RedpinContract.Location._MAP_ID
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : ""), selectionArgs);
Log.d(TAG, "deleting all locations of map #" + segment);
break;
case MAP_LOCATIONS_ID:
segment = uri.getPathSegments().get(3);
count = mDB.delete(LOCATION_TABLE,
(remote ? RedpinContract.Location.REMOTE_ID
: RedpinContract.Location._ID)
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND ("
+ selection + ')' : ""), selectionArgs);
Log.d(TAG, "deleting location #" + segment);
break;
case LOCATION:
count = mDB.delete(LOCATION_TABLE, selection, selectionArgs);
Log.d(TAG, "deleting all locations");
break;
case LOCATION_ID:
segment = uri.getPathSegments().get(1);
count = mDB.delete(LOCATION_TABLE,
(remote ? RedpinContract.Location.REMOTE_ID
: RedpinContract.Location._ID)
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND ("
+ selection + ')' : ""), selectionArgs);
Log.d(TAG, "deleting location #" + segment);
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
/**
* {@inheritDoc}
*/
@Override
public String getType(Uri uri) {
int match = URI_MATCHER.match(uri);
switch (match) {
case MAP:
return RedpinContract.Map.LIST_TYPE; // "vnd.android.cursor.dir/vnd.redpin.maps";
case MAP_ID:
return RedpinContract.Map.ITEM_TYPE; // "vnd.android.cursor.item/vnd.redpin.maps";
case MAP_LOCATIONS:
return RedpinContract.Location.LIST_TYPE; // "vnd.android.cursor.dir/vnd.redpin.locations";
case MAP_LOCATIONS_ID:
return RedpinContract.Location.ITEM_TYPE; // "vnd.android.cursor.item/vnd.redpin.locations";
case LOCATION:
return RedpinContract.Location.LIST_TYPE; // "vnd.android.cursor.dir/vnd.redpin.locations";
case LOCATION_ID:
return RedpinContract.Location.ITEM_TYPE; // "vnd.android.cursor.item/vnd.redpin.locations";
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
/**
* {@inheritDoc}
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int match = URI_MATCHER.match(uri);
boolean remote = uri.getQueryParameter(RedpinContract.REMOTE_PARAMETER) != null;
switch (match) {
case MAP:
return insertMap(values);
case MAP_LOCATIONS:
if (remote) {
throw new IllegalArgumentException(REMOTE_PARAMETER_DISALOWED
+ uri);
}
values.put(RedpinContract.Location._MAP_ID, uri.getPathSegments()
.get(1));
return insertLocation(values);
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
}
/**
* Insert an {@link Map} into the database
*
* @param initialValues
* {@link ContentValues} representing a {@link Map}
* @return {@link Uri} of the inserted {@link Map}
*/
public Uri insertMap(ContentValues initialValues) {
long rowID;
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
Resources r = Resources.getSystem();
if (values.containsKey(RedpinContract.Map.REMOTE_ID) == false) {
values.put(RedpinContract.Map.REMOTE_ID, -1);
}
if (values.containsKey(RedpinContract.Map.NAME) == false) {
values.put(RedpinContract.Map.NAME, r
.getString(android.R.string.untitled));
}
if (values.containsKey(RedpinContract.Map.URL) == false) {
values.put(RedpinContract.Map.URL, "");
}
rowID = mDB.insert(MAP_TABLE, RedpinContract.Map.NAME, values);
if (rowID > 0) {
Uri uri = ContentUris.withAppendedId(
RedpinContract.Map.CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
throw new SQLException("Failed to insert row");
}
/**
* Insert an {@link Location} into the database
*
* @param initialValues
* {@link ContentValues} representing a {@link Location}
* @return {@link Uri} of the inserted {@link Map}
*/
public Uri insertLocation(ContentValues values) {
long rowID;
Resources r = Resources.getSystem();
if (values.containsKey(RedpinContract.Location.REMOTE_ID) == false) {
values.put(RedpinContract.Location.REMOTE_ID, -1);
}
if (values.containsKey(RedpinContract.Location.X) == false
|| values.containsKey(RedpinContract.Location.Y) == false) {
Log
.e(TAG,
"the x and y need to be set in order to insert a point.");
}
if (values.containsKey(RedpinContract.Location.SYMBOLIC_ID) == false) {
values.put(RedpinContract.Location.SYMBOLIC_ID, r
.getString(android.R.string.untitled));
}
rowID = mDB.insert(LOCATION_TABLE, RedpinContract.Location.SYMBOLIC_ID,
values);
if (rowID > 0) {
Uri uri = ContentUris
.appendId(
RedpinContract.Map.CONTENT_URI
.buildUpon()
.appendEncodedPath(
values
.getAsString(RedpinContract.Location._MAP_ID))
.appendEncodedPath(
RedpinContract.Location.PATH_SEGMENT),
rowID).build();
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
throw new SQLException("Failed to insert row");
}
/**
* {@inheritDoc}
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
boolean remote = uri.getQueryParameter(RedpinContract.REMOTE_PARAMETER) != null;
switch (URI_MATCHER.match(uri)) {
case MAP:
qb.setTables(MAP_TABLE);
break;
case MAP_ID:
qb.setTables(MAP_TABLE);
qb.appendWhere((remote ? RedpinContract.Map.REMOTE_ID
: RedpinContract.Map._ID)
+ "=" + uri.getPathSegments().get(1));
break;
case MAP_LOCATIONS:
if (remote) {
throw new IllegalArgumentException(REMOTE_PARAMETER_DISALOWED
+ uri);
}
qb.setTables(LOCATION_TABLE);
qb.appendWhere(RedpinContract.Location._MAP_ID + "="
+ uri.getPathSegments().get(1));
break;
case MAP_LOCATIONS_ID:
if (remote) {
throw new IllegalArgumentException(REMOTE_PARAMETER_DISALOWED
+ uri);
}
qb.setTables(LOCATION_TABLE);
qb.appendWhere(RedpinContract.Location._MAP_ID + "="
+ uri.getPathSegments().get(1));
qb.appendWhere(" AND " + RedpinContract.Location._ID + "="
+ uri.getPathSegments().get(3));
break;
case LOCATION:
qb.setTables(LOCATION_TABLE);
break;
case LOCATION_ID:
qb.setTables(LOCATION_TABLE);
qb.appendWhere((remote ? RedpinContract.Map.REMOTE_ID
: RedpinContract.Map._ID)
+ "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
Cursor c = qb.query(mDB, projection, selection, selectionArgs, null,
null, null);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
/**
* {@inheritDoc}
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
String segment;
int count;
int match = URI_MATCHER.match(uri);
boolean remote = uri.getQueryParameter(RedpinContract.REMOTE_PARAMETER) != null;
switch (match) {
case MAP_ID:
segment = uri.getPathSegments().get(1);
count = mDB.update(MAP_TABLE, values,
(remote ? RedpinContract.Map.REMOTE_ID
: RedpinContract.Map._ID)
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND ("
+ selection + ')' : ""), selectionArgs);
break;
case MAP_LOCATIONS_ID:
segment = uri.getPathSegments().get(3);
count = mDB.update(LOCATION_TABLE, values,
(remote ? RedpinContract.Location.REMOTE_ID
: RedpinContract.Location._ID)
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND ("
+ selection + ')' : ""), selectionArgs);
break;
case LOCATION_ID:
segment = uri.getPathSegments().get(1);
count = mDB.update(LOCATION_TABLE, values,
(remote ? RedpinContract.Location.REMOTE_ID
: RedpinContract.Location._ID)
+ "="
+ segment
+ (!TextUtils.isEmpty(selection) ? " AND ("
+ selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URL " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
}