Work in progress: Category class and content provider

for recently-used categories list
This commit is contained in:
Brion Vibber 2013-04-22 16:14:28 -07:00
parent 9288aec9d6
commit 58e48399d5
2 changed files with 321 additions and 0 deletions

View file

@ -0,0 +1,151 @@
package org.wikimedia.commons.category;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
import org.wikimedia.commons.contributions.ContributionsContentProvider;
import java.util.Date;
/**
* Created with IntelliJ IDEA.
* User: brion
* Date: 4/22/13
* Time: 3:37 PM
* To change this template use File | Settings | File Templates.
*/
public class Category /*implements Parcelable */{
private ContentProviderClient client;
private Uri contentUri;
private String name;
private Date lastUsed;
private int timesUsed;
/*
// Do we need parcelable stuff?
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(name);
parcel.writeSerializable(lastUsed);
parcel.writeInt(timesUsed);
}
public Category(Parcel in) {
name = in.readString();
lastUsed = (Date)in.readSerializable();
timesUsed = in.readInt();
}
public int describeContents() {
return 0;
}
*/
// Getters/setters
public String getName() {
return name;
}
public void setName(String name_) {
name = name_;
}
public Date getLastUsed() {
// warning: Date objects are mutable.
return (Date)lastUsed.clone();
}
public void setLastUsed(Date lastUsed_) {
// warning: Date objects are mutable.
lastUsed = (Date)lastUsed_.clone();
}
public int getTimesUsed() {
return timesUsed;
}
public void setTimesUsed(int timesUsed_) {
timesUsed = timesUsed_;
}
public void incTimesUsed() {
timesUsed++;
}
// Database/content-provider stuff
public void setContentProviderClient(ContentProviderClient client) {
this.client = client;
}
public void save() {
try {
if(contentUri == null) {
contentUri = client.insert(CategoryContentProvider.BASE_URI, this.toContentValues());
} else {
client.update(contentUri, toContentValues(), null, null);
}
} catch(RemoteException e) {
throw new RuntimeException(e);
}
}
public ContentValues toContentValues() {
ContentValues cv = new ContentValues();
cv.put(Table.COLUMN_NAME, getName());
cv.put(Table.COLUMN_LAST_USED, getLastUsed().getTime());
cv.put(Table.COLUMN_TIMES_USED, getTimesUsed());
return cv;
}
public static Category fromCursor(Cursor cursor) {
// Hardcoding column positions!
Category c = new Category();
c.name = cursor.getString(0);
c.lastUsed = new Date(cursor.getLong(1));
c.timesUsed = cursor.getInt(2);
return c;
}
public static class Table {
public static final String TABLE_NAME = "categories";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_LAST_USED = "last_used";
public static final String COLUMN_TIMES_USED = "times_used";
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
COLUMN_ID,
COLUMN_NAME,
COLUMN_LAST_USED,
COLUMN_TIMES_USED
};
private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY,"
+ COLUMN_NAME + " STRING,"
+ COLUMN_LAST_USED + " INTEGER," // Will this roll over in 2038? :)
+ COLUMN_TIMES_USED + " INTEGER"
+ ");";
public static void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_STATEMENT);
}
public static void onUpdate(SQLiteDatabase db, int from, int to) {
if(from == to) {
return;
}
}
}
}

View file

@ -0,0 +1,170 @@
package org.wikimedia.commons.category;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import org.wikimedia.commons.CommonsApplication;
import org.wikimedia.commons.data.DBOpenHelper;
/**
* Created with IntelliJ IDEA.
* User: brion
* Date: 4/22/13
* Time: 4:09 PM
* To change this template use File | Settings | File Templates.
*/
public class CategoryContentProvider extends ContentProvider {
// ????
private static final int CATEGORIES = 1;
private static final int CATEGORIES_ID = 2;
public static final String AUTHORITY = "org.wikimedia.commons.categories.contentprovider";
private static final String BASE_PATH = "categories";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
}
public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id);
}
private DBOpenHelper dbOpenHelper;
@Override
public boolean onCreate() {
dbOpenHelper = ((CommonsApplication)this.getContext().getApplicationContext()).getDbOpenHelper();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Category.Table.TABLE_NAME);
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor;
switch(uriType) {
case CATEGORIES:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORIES_ID:
cursor = queryBuilder.query(db,
Category.Table.ALL_FIELDS,
"_id = ?",
new String[] { uri.getLastPathSegment() },
null,
null,
sortOrder
);
break;
default:
throw new IllegalArgumentException("Unknown URI" + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
long id = 0;
switch (uriType) {
case CATEGORIES:
id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(BASE_URI + "/" + id);
}
@Override
public int delete(Uri uri, String s, String[] strings) {
return 0;
}
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
Log.d("Commons", "Hello, bulk insert!");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
sqlDB.beginTransaction();
switch (uriType) {
case CATEGORIES:
for(ContentValues value: values) {
Log.d("Commons", "Inserting! " + value.toString());
sqlDB.insert(Category.Table.TABLE_NAME, null, value);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
sqlDB.setTransactionSuccessful();
sqlDB.endTransaction();
getContext().getContentResolver().notifyChange(uri, null);
return values.length;
}
@Override
public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
should be fine. So only issues are those that pass in via concating.
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
int rowsUpdated = 0;
switch (uriType) {
case CATEGORIES:
rowsUpdated = sqlDB.update(Category.Table.TABLE_NAME,
contentValues,
selection,
selectionArgs);
break;
case CATEGORIES_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Category.Table.TABLE_NAME,
contentValues,
Category.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri + " with type " + uriType);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
}