/* * Author: * Stjepan Rajko * urbanSTEW * * Copyright 2008,2009 Stjepan Rajko. * * This file is part of the Android version of Rehearsal Assistant. * * Rehearsal Assistant 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. * * Rehearsal Assistant 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 Rehearsal Assistant. * If not, see <http://www.gnu.org/licenses/>. */ package urbanstew.RehearsalAssistant; import java.io.File; import java.util.HashMap; import urbanstew.RehearsalAssistant.Rehearsal.Annotations; import urbanstew.RehearsalAssistant.Rehearsal.AppData; import urbanstew.RehearsalAssistant.Rehearsal.Projects; import urbanstew.RehearsalAssistant.Rehearsal.Sessions; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; 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.preference.PreferenceManager; import android.provider.BaseColumns; import android.text.TextUtils; import android.util.Log; public class RehearsalData extends ContentProvider { @Override public boolean onCreate() { // Access the database. mOpenHelper = new DatabaseHelper(getContext()); return true; } static ContentValues valuesForMemoProject(Context context) { ContentValues values = new ContentValues(); values.put(Projects.TITLE, "stegdroid"); values.put(Projects.IDENTIFIER, "memo_project"); values.put(Projects.TYPE, Projects.TYPE_SIMPLE); return values; } static void addSimpleProject(SQLiteDatabase db, Context context) { db.insert("projects", "identifier", valuesForMemoProject(context)); } static void addSessionProject(SQLiteDatabase db, Context context) { ContentValues values = new ContentValues(); values.put(Projects.TITLE,"stegdroid"); values.put(Projects.IDENTIFIER, "session_project"); values.put(Projects.TYPE, Projects.TYPE_SESSION); db.insert("projects", "identifier", values); } enum Project { _ID, TITLE, IDENTIFIER, TYPE } enum Session { _ID, PROJECT_ID, TITLE, IDENTIFIER, START_TIME, END_TIME } enum Annotation { _ID, RUN_ID, START_TIME, END_TIME, FILE_NAME } private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, "rehearsal_assistant.db", null, 9); mContext = context; } public void onCreate(SQLiteDatabase db) { createProjectsTable(db); createSessionsTable(db); createAnnotationsTable(db); } void createProjectsTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + Projects.TABLE_NAME + "(" + Projects._ID + " INTEGER PRIMARY KEY," + Projects.TITLE + " TEXT," + Projects.IDENTIFIER + " TEXT," + Projects.TYPE + " INTEGER DEFAULT " + Projects.TYPE_SESSION + ");"); addSimpleProject(db, mContext); addSessionProject(db, mContext); } void createSessionsTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + Sessions.TABLE_NAME + "(" + Sessions._ID + " INTEGER PRIMARY KEY," + Sessions.PROJECT_ID + " INTEGER," + Sessions.TITLE + " TEXT," + Sessions.IDENTIFIER + " TEXT," + Sessions.START_TIME + " INTEGER," + Sessions.END_TIME + " INTEGER" + ");"); } void createAnnotationsTable(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + Annotations.TABLE_NAME + "(" + Annotations._ID + " INTEGER PRIMARY KEY," + Annotations.SESSION_ID + " INTEGER," + Annotations.START_TIME + " INTEGER," + Annotations.END_TIME + " INTEGER," + Annotations.FILE_NAME + " TEXT," + Annotations.VIEWED + " BOOLEAN DEFAULT FALSE," + Annotations.LABEL + " TEXT DEFAULT ''" + ");"); } @SuppressWarnings("unused") void migrateTable(SQLiteDatabase db, String name) { String backupName = name + "_backup"; db.execSQL("DROP TABLE IF EXISTS " + backupName + ";"); db.execSQL("ALTER TABLE " + name + " RENAME TO " + backupName + ";"); if(name.equals(Sessions.TABLE_NAME)) createSessionsTable(db); else createAnnotationsTable(db); db.execSQL("INSERT INTO " + name + " SELECT * FROM " + backupName + ";"); db.execSQL("DROP TABLE IF EXISTS " + backupName + ";"); } void upgrade5to6(SQLiteDatabase db) { db.execSQL("CREATE TABLE " + AppData.TABLE_NAME + "(" + AppData._ID + " INTEGER PRIMARY KEY" + ");"); } void upgrade6to7(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + Annotations.TABLE_NAME + " ADD COLUMN " + Annotations.LABEL + " TEXT DEFAULT ''"); } void upgrade7to8(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + Projects.TABLE_NAME + " ADD COLUMN " + Projects.TYPE + " INTEGER DEFAULT " + Projects.TYPE_SESSION); } void upgrade8to9(SQLiteDatabase db) { // Display license if this is the first time running this version. String[] appDataProjection = { AppData._ID, AppData.KEY, AppData.VALUE }; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences.Editor editor = preferences.edit(); // get the current project information from AppData and move to preferences Cursor project = db.query(AppData.TABLE_NAME, appDataProjection, AppData.KEY + "=" + "'current_project_id'", null, null, null, AppData.DEFAULT_SORT_ORDER, null); if(project.getCount()>0) { project.moveToFirst(); editor.putLong("current_project_id", project.getLong(2)); } project.close(); Cursor visited_version = db.query(AppData.TABLE_NAME, appDataProjection, AppData.KEY + "=" + "'app_visited_version'", null, null, null, AppData.DEFAULT_SORT_ORDER, null); if(visited_version.getCount()>0) { visited_version.moveToFirst(); editor.putFloat("app_visited_version", Float.valueOf(visited_version.getString(2))); } editor.commit(); db.execSQL("DROP TABLE " + AppData.TABLE_NAME); } public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w("RehearsalAssistant", "Upgrading database from version " + oldVersion + " to " + newVersion); switch(oldVersion) { case 5: upgrade5to6(db); case 6: upgrade6to7(db); case 7: upgrade7to8(db); case 8: upgrade8to9(db); break; default: Log.w("RehearsalAssistant", "Reinitializing database tables"); // drop tables that existed prior to 5 db.execSQL("DROP TABLE IF EXISTS " + Projects.TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS " + Sessions.TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS " + Annotations.TABLE_NAME); db.execSQL("DROP TABLE IF EXISTS runs"); onCreate(db); } } Context mContext; } private static final int APPDATA = 1; private static final int APPDATA_ID = 2; private static final int PROJECTS = 3; private static final int PROJECT_ID = 4; private static final int SESSIONS = 5; private static final int SESSION_ID = 6; private static final int ANNOTATIONS = 7; private static final int ANNOTATION_ID = 8; private static final UriMatcher sUriMatcher; private static HashMap<String, String> sAppDataProjectionMap; private static HashMap<String, String> sProjectsProjectionMap; private static HashMap<String, String> sSessionsProjectionMap; private static HashMap<String, String> sAnnotationsProjectionMap; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(Rehearsal.AUTHORITY, "appdata", APPDATA); sUriMatcher.addURI(Rehearsal.AUTHORITY, "appdata/#", APPDATA_ID); sUriMatcher.addURI(Rehearsal.AUTHORITY, "projects", PROJECTS); sUriMatcher.addURI(Rehearsal.AUTHORITY, "projects/#", PROJECT_ID); sUriMatcher.addURI(Rehearsal.AUTHORITY, "sessions", SESSIONS); sUriMatcher.addURI(Rehearsal.AUTHORITY, "sessions/#", SESSION_ID); sUriMatcher.addURI(Rehearsal.AUTHORITY, "annotations", ANNOTATIONS); sUriMatcher.addURI(Rehearsal.AUTHORITY, "annotations/#", ANNOTATION_ID); sAppDataProjectionMap = new HashMap<String, String>(); sAppDataProjectionMap.put(BaseColumns._ID, BaseColumns._ID); sAppDataProjectionMap.put(AppData.KEY, AppData.KEY); sAppDataProjectionMap.put(AppData.VALUE, AppData.VALUE); sProjectsProjectionMap = new HashMap<String, String>(); sProjectsProjectionMap.put(BaseColumns._ID, BaseColumns._ID); sProjectsProjectionMap.put(Projects.TITLE, Projects.TITLE); sProjectsProjectionMap.put(Projects.IDENTIFIER, Projects.IDENTIFIER); sProjectsProjectionMap.put(Projects.TYPE, Projects.TYPE); sSessionsProjectionMap = new HashMap<String, String>(); sSessionsProjectionMap.put(BaseColumns._ID, BaseColumns._ID); sSessionsProjectionMap.put(Sessions.PROJECT_ID, Sessions.PROJECT_ID); sSessionsProjectionMap.put(Sessions.TITLE, Sessions.TITLE); sSessionsProjectionMap.put(Sessions.IDENTIFIER, Sessions.IDENTIFIER); sSessionsProjectionMap.put(Sessions.START_TIME, Sessions.START_TIME); sSessionsProjectionMap.put(Sessions.END_TIME, Sessions.END_TIME); sAnnotationsProjectionMap = new HashMap<String, String>(); sAnnotationsProjectionMap.put(BaseColumns._ID, BaseColumns._ID); sAnnotationsProjectionMap.put(Annotations.SESSION_ID, Annotations.SESSION_ID); sAnnotationsProjectionMap.put(Annotations.START_TIME, Annotations.START_TIME); sAnnotationsProjectionMap.put(Annotations.END_TIME, Annotations.END_TIME); sAnnotationsProjectionMap.put(Annotations.FILE_NAME, Annotations.FILE_NAME); sAnnotationsProjectionMap.put(Annotations.LABEL, Annotations.LABEL); sAnnotationsProjectionMap.put(Annotations.VIEWED, Annotations.VIEWED); } private DatabaseHelper mOpenHelper; @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case SESSIONS: Cursor c = db.query(Sessions.TABLE_NAME, new String[] {Sessions._ID}, selection, selectionArgs, null, null, null); count = 0; for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) count += delete(ContentUris.withAppendedId(Sessions.CONTENT_URI, c.getLong(0)), null, null); break; case SESSION_ID: String sessionId = uri.getPathSegments().get(1); count = db.delete(Sessions.TABLE_NAME, Sessions._ID + "=" + sessionId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); if(count>0) deleteAnnotations(db, Annotations.SESSION_ID + "=" + sessionId, null); break; case ANNOTATIONS: count = deleteAnnotations(db, selection, selectionArgs); break; case ANNOTATION_ID: String annotationId = uri.getPathSegments().get(1); count = deleteAnnotations(db, Annotations._ID + "=" + annotationId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); break; case PROJECT_ID: String projectId = uri.getPathSegments().get(1); count = db.delete(Projects.TABLE_NAME, Projects._ID + "=" + projectId + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs); if(count>0) delete(Sessions.CONTENT_URI, Sessions.PROJECT_ID + "=" + projectId, null); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } int deleteAnnotations(SQLiteDatabase db, String selection, String[] selectionArgs) { // query and erase files SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(Annotations.TABLE_NAME); qb.setProjectionMap(sAnnotationsProjectionMap); String[] projection = { Annotations.FILE_NAME }; Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null); for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) { if(c.getString(0)!=null) { Log.w("Rehearsal Assistant erasing", c.getString(0)); (new File(c.getString(0))).delete(); } } // delete int result = db.delete(Annotations.TABLE_NAME, selection, selectionArgs); c.close(); return result; } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case APPDATA: return AppData.CONTENT_TYPE; case APPDATA_ID: return AppData.CONTENT_ITEM_TYPE; case PROJECTS: return Projects.CONTENT_TYPE; case PROJECT_ID: return Projects.CONTENT_ITEM_TYPE; case SESSIONS: return Sessions.CONTENT_TYPE; case SESSION_ID: return Sessions.CONTENT_ITEM_TYPE; case ANNOTATIONS: return Annotations.CONTENT_TYPE; case ANNOTATION_ID: return Annotations.CONTENT_ITEM_TYPE; default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public Uri insert(Uri uri, ContentValues initialValues) { ContentValues values; if (initialValues != null) values = new ContentValues(initialValues); else values = new ContentValues(); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); long rowId=0; Uri contentURI; switch(sUriMatcher.match(uri)) { case APPDATA: { rowId = db.insert(AppData.TABLE_NAME, AppData.KEY, values); contentURI = AppData.CONTENT_URI; break; } case SESSIONS: { if(!values.containsKey(Sessions.IDENTIFIER)) values.put(Sessions.IDENTIFIER, values.getAsString(Sessions.TITLE).toLowerCase().replace(" ", "_")); rowId = db.insert(Sessions.TABLE_NAME, Sessions.TITLE, values); contentURI = Sessions.CONTENT_URI; break; } case ANNOTATIONS: { rowId = db.insert(Annotations.TABLE_NAME, Annotations.FILE_NAME, values); contentURI = Annotations.CONTENT_URI; break; } case PROJECTS: { rowId = db.insert(Projects.TABLE_NAME, Projects.TITLE, values); contentURI = Projects.CONTENT_URI; break; } default: throw new IllegalArgumentException("Unknown URI " + uri); } if (rowId >= 0) { Uri noteUri = ContentUris.withAppendedId(contentURI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } throw new SQLException("Failed to insert row into " + uri); } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (sUriMatcher.match(uri)) { case APPDATA: qb.setTables(AppData.TABLE_NAME); qb.setProjectionMap(sAppDataProjectionMap); break; case APPDATA_ID: qb.setTables(AppData.TABLE_NAME); qb.setProjectionMap(sAppDataProjectionMap); qb.appendWhere(AppData._ID + "=" + uri.getPathSegments().get(1)); break; case PROJECTS: qb.setTables(Projects.TABLE_NAME); qb.setProjectionMap(sProjectsProjectionMap); break; case PROJECT_ID: qb.setTables(Projects.TABLE_NAME); qb.setProjectionMap(sProjectsProjectionMap); qb.appendWhere(Projects._ID + "=" + uri.getPathSegments().get(1)); break; case SESSIONS: qb.setTables(Sessions.TABLE_NAME); qb.setProjectionMap(sSessionsProjectionMap); break; case SESSION_ID: qb.setTables(Sessions.TABLE_NAME); qb.setProjectionMap(sSessionsProjectionMap); qb.appendWhere(Sessions._ID + "=" + uri.getPathSegments().get(1)); break; case ANNOTATIONS: qb.setTables(Annotations.TABLE_NAME); qb.setProjectionMap(sAnnotationsProjectionMap); break; case ANNOTATION_ID: qb.setTables(Annotations.TABLE_NAME); qb.setProjectionMap(sAnnotationsProjectionMap); qb.appendWhere(Annotations._ID + "=" + uri.getPathSegments().get(1)); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // If no sort order is specified use the default String orderBy; if (TextUtils.isEmpty(sortOrder)) { orderBy = Rehearsal.Sessions.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } // Get the database and run the query SQLiteDatabase db = mOpenHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); // Tell the cursor what uri to watch, so it knows when its source data changes c.setNotificationUri(getContext().getContentResolver(), uri); return c; } public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count; if(uri.getPathSegments().size()>1) selection = BaseColumns._ID + "=" + uri.getPathSegments().get(1) + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); switch (sUriMatcher.match(uri)) { case APPDATA: count = db.update(AppData.TABLE_NAME, values, selection, selectionArgs); break; case PROJECT_ID: count = db.update(Projects.TABLE_NAME, values, selection, selectionArgs); break; case SESSION_ID: count = db.update(Sessions.TABLE_NAME, values, selection, selectionArgs); break; case ANNOTATION_ID: count = db.update(Annotations.TABLE_NAME, values, selection, selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } }