/*
* Copyright (C) 2012 Simon Robinson
*
* This file is part of Com-Me.
*
* Com-Me 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 (at your option) any later version.
*
* Com-Me 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 Com-Me.
* If not, see <http://www.gnu.org/licenses/>.
*/
package ac.robinson.mediatablet.provider;
import java.io.File;
import java.util.UUID;
import ac.robinson.mediatablet.MediaTablet;
import ac.robinson.mediatablet.R;
import ac.robinson.util.DebugUtilities;
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.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class MediaTabletProvider extends ContentProvider {
public static final String URI_AUTHORITY = MediaTablet.APPLICATION_NAME;
private static final String DATABASE_NAME = URI_AUTHORITY + ".db";
private static final int DATABASE_VERSION = 1;
public static final String URI_PREFIX = "content://";
public static final String URI_SEPARATOR = File.separator;
private final String URI_PACKAGE = this.getClass().getPackage().getName();
public static final String HOMESTEADS_LOCATION = "homesteads";
public static final String PEOPLE_LOCATION = "people";
public static final String MEDIA_LOCATION = "media";
// *must* start at 1... quite hacky
public static final int TYPE_IMAGE_BACK = 1; // normal (rear) camera
public static final int TYPE_IMAGE_FRONT = 2; // front camera
public static final int TYPE_VIDEO = 3;
public static final int TYPE_AUDIO = 4;
public static final int TYPE_TEXT = 5;
public static final int TYPE_NARRATIVE = 6;
public static final int TYPE_UNKNOWN = 7; // for unknown media types
// *must* be correct length (number of media types)
public static final int[] ALL_MEDIA_TYPES = { TYPE_IMAGE_BACK, TYPE_IMAGE_FRONT, TYPE_VIDEO, TYPE_AUDIO, TYPE_TEXT,
TYPE_NARRATIVE, TYPE_UNKNOWN };
// *must* be correct length (number of media types)
public static final int[] NO_MEDIA_TYPES = { 0, 0, 0, 0, 0, 0, 0 };
private static final UriMatcher URI_MATCHER;
static {
URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
URI_MATCHER.addURI(URI_AUTHORITY, HOMESTEADS_LOCATION, R.id.uri_homesteads);
URI_MATCHER.addURI(URI_AUTHORITY, PEOPLE_LOCATION, R.id.uri_people);
URI_MATCHER.addURI(URI_AUTHORITY, MEDIA_LOCATION, R.id.uri_media);
}
private SQLiteOpenHelper mOpenHelper;
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
public static String getNewInternalId() {
return UUID.randomUUID().toString();
}
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (URI_MATCHER.match(uri)) {
case R.id.uri_homesteads:
qb.setTables(HOMESTEADS_LOCATION);
break;
case R.id.uri_people:
qb.setTables(PEOPLE_LOCATION);
break;
case R.id.uri_media:
qb.setTables(MEDIA_LOCATION);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// if no sort order is specified use none
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = null;
} else {
orderBy = sortOrder;
}
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
public String getType(Uri uri) {
switch (URI_MATCHER.match(uri)) {
case R.id.uri_homesteads:
case R.id.uri_people:
case R.id.uri_media:
return "vnd.android.cursor.dir/vnd." + URI_PACKAGE; // do these need to be unique?
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
public Uri insert(Uri uri, ContentValues initialValues) {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
throw new IllegalArgumentException("No content values passed");
}
getType(uri); // so we don't get the database unless necessary
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = 0;
Uri contentUri = null;
switch (URI_MATCHER.match(uri)) {
case R.id.uri_homesteads:
rowId = db.insert(HOMESTEADS_LOCATION, null, values);
contentUri = HomesteadItem.CONTENT_URI;
break;
case R.id.uri_people:
rowId = db.insert(PEOPLE_LOCATION, null, values);
contentUri = PersonItem.CONTENT_URI;
break;
case R.id.uri_media:
rowId = db.insert(MEDIA_LOCATION, null, values);
contentUri = MediaItem.CONTENT_URI;
break;
}
if (rowId > 0) {
Uri insertUri = ContentUris.withAppendedId(contentUri, rowId);
getContext().getContentResolver().notifyChange(uri, null);
return insertUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
public int delete(Uri uri, String selectionClause, String[] selectionArgs) {
getType(uri); // so we don't get the database unless necessary
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (URI_MATCHER.match(uri)) {
case R.id.uri_homesteads:
count = db.delete(HOMESTEADS_LOCATION, selectionClause, selectionArgs);
break;
case R.id.uri_people:
count = db.delete(PEOPLE_LOCATION, selectionClause, selectionArgs);
break;
case R.id.uri_media:
count = db.delete(MEDIA_LOCATION, selectionClause, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
public int update(Uri uri, ContentValues initialValues, String selectionClause, String[] selectionArgs) {
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
throw new IllegalArgumentException("No content values passed");
}
getType(uri); // so we don't get the database unless necessary
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int rowsAffected = 0;
switch (URI_MATCHER.match(uri)) {
case R.id.uri_media:
rowsAffected = db.update(MEDIA_LOCATION, values, selectionClause, selectionArgs);
break;
case R.id.uri_people:
rowsAffected = db.update(PEOPLE_LOCATION, values, selectionClause, selectionArgs);
break;
case R.id.uri_homesteads:
rowsAffected = db.update(HOMESTEADS_LOCATION, values, selectionClause, selectionArgs);
break;
}
if (rowsAffected > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsAffected;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + HOMESTEADS_LOCATION + " (" //
+ HomesteadItem._ID + " INTEGER PRIMARY KEY, " // required for Android Adapters
+ HomesteadItem.INTERNAL_ID + " TEXT, " // the GUID of this homestead item
+ HomesteadItem.X_POSITION + " INTEGER, " // the x position of this homestead
+ HomesteadItem.Y_POSITION + " INTEGER, " // the y position of this homestead
+ HomesteadItem.COLOUR + " INTEGER, " // the customised colour of this homestead
+ HomesteadItem.DELETED + " INTEGER);"); // whether this homestead item has been deleted
db.execSQL("CREATE INDEX " + HOMESTEADS_LOCATION + "Index" + HomesteadItem.INTERNAL_ID + " ON "
+ HOMESTEADS_LOCATION + "(" + HomesteadItem.INTERNAL_ID + ");");
db.execSQL("CREATE INDEX " + HOMESTEADS_LOCATION + "Index" + HomesteadItem.X_POSITION + " ON "
+ HOMESTEADS_LOCATION + "(" + HomesteadItem.X_POSITION + ");");
db.execSQL("CREATE TABLE " + PEOPLE_LOCATION + " (" //
+ PersonItem._ID + " INTEGER PRIMARY KEY, " // required for Android Adapters
+ PersonItem.INTERNAL_ID + " TEXT, " // the GUID of this person item
+ PersonItem.PARENT_ID + " TEXT, " // the GUID of the parent of this person item
+ PersonItem.NAME + " TEXT, " // the name this person has chosen (not unique)
+ PersonItem.DATE_CREATED + " INTEGER, " // the timestamp when this person item was created
+ PersonItem.PASSWORD_HASH + " TEXT, " // the SHA-1 hash of this person item's unlock code
+ PersonItem.LOCK_STATUS + " INTEGER, " // either PersonItem.PERSON_LOCKED or PERSON_UNLOCKED
+ PersonItem.UNLOCKED_TIMESTAMP + " TEXT, " // the timestamp when this person item was last unlocked
+ PersonItem.DELETED + " INTEGER);"); // whether this person item has been deleted
db.execSQL("CREATE INDEX " + PEOPLE_LOCATION + "Index" + PersonItem.INTERNAL_ID + " ON " + PEOPLE_LOCATION
+ "(" + PersonItem.INTERNAL_ID + ");");
db.execSQL("CREATE INDEX " + PEOPLE_LOCATION + "Index" + PersonItem.PARENT_ID + " ON " + PEOPLE_LOCATION
+ "(" + PersonItem.PARENT_ID + ");");
db.execSQL("CREATE TABLE " + MEDIA_LOCATION + " (" //
+ MediaItem._ID + " INTEGER PRIMARY KEY, " // required for Android Adapters
+ MediaItem.INTERNAL_ID + " TEXT, " // the GUID of this media item
+ MediaItem.PARENT_ID + " TEXT, " // the GUID of the parent of this media item
+ MediaItem.DATE_CREATED + " INTEGER, " // the timestamp when this media item was created
+ MediaItem.FILE_EXTENSION + " TEXT, " // the file extension of this media item
+ MediaItem.MEDIA_EXTRA + " TEXT, " // the original name of this media item, or its text content
+ MediaItem.TYPE + " INTEGER, " // the type of this media (this.TYPE_<x>)
+ MediaItem.VISIBILITY + " INTEGER, " // whether shared: MediaItem.MEDIA_PUBLIC or MEDIA_PRIVATE
+ MediaItem.DELETED + " INTEGER);"); // whether this media item has been deleted
db.execSQL("CREATE INDEX " + MEDIA_LOCATION + "Index" + MediaItem.INTERNAL_ID + " ON " + MEDIA_LOCATION
+ "(" + MediaItem.INTERNAL_ID + ");");
db.execSQL("CREATE INDEX " + MEDIA_LOCATION + "Index" + MediaItem.PARENT_ID + " ON " + MEDIA_LOCATION + "("
+ MediaItem.PARENT_ID + ");");
db.execSQL("CREATE INDEX " + MEDIA_LOCATION + "Index" + MediaItem.MEDIA_EXTRA + " ON " + MEDIA_LOCATION
+ "(" + MediaItem.MEDIA_EXTRA + ");");
db.execSQL("CREATE INDEX " + MEDIA_LOCATION + "Index" + MediaItem.VISIBILITY + " ON " + MEDIA_LOCATION
+ "(" + MediaItem.VISIBILITY + ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (MediaTablet.DEBUG) {
Log.d(DebugUtilities.getLogTag(this), "Database upgrade requested from version " + oldVersion + " to "
+ newVersion + " - ignoring.");
}
// backup database if necessary
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (MediaTablet.DEBUG) {
Log.d(DebugUtilities.getLogTag(this), "Database downgrade requested from version " + oldVersion
+ " to " + newVersion + " - ignoring.");
}
// backup database if necessary
}
}
}