package com.github.andlyticsproject.db; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import android.annotation.SuppressLint; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; @SuppressLint("SimpleDateFormat") public class AndlyticsContentProvider extends ContentProvider { private static final int ID_TABLE_STATS = 0; private static final int ID_TABLE_APPINFO = 1; private static final int ID_TABLE_COMMENTS = 2; private static final int ID_TABLE_ADMOB = 3; private static final int ID_APP_VERSION_CHANGE = 4; private static final int ID_UNIQUE_PACKAGES = 5; private static final int ID_TABLE_LINKS = 6; private static final int ID_TABLE_REVENUE_SUMMARY = 7; public static final String AUTHORITY = "com.github.andlyticsproject.db.AndlyticsContentProvider"; private static final UriMatcher sUriMatcher; public static final String APP_VERSION_CHANGE = "appVersionChange"; private AndlyticsDb dbHelper; @Override public int delete(Uri uri, String where, String[] whereArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case ID_TABLE_STATS: count = db.delete(AppStatsTable.DATABASE_TABLE_NAME, where, whereArgs); break; case ID_TABLE_APPINFO: count = db.delete(AppInfoTable.DATABASE_TABLE_NAME, where, whereArgs); break; case ID_TABLE_COMMENTS: count = db.delete(CommentsTable.DATABASE_TABLE_NAME, where, whereArgs); break; case ID_TABLE_ADMOB: count = db.delete(AdmobTable.DATABASE_TABLE_NAME, where, whereArgs); break; case ID_TABLE_LINKS: count = db.delete(LinksTable.DATABASE_TABLE_NAME, where, whereArgs); break; case ID_TABLE_REVENUE_SUMMARY: count = db.delete(RevenueSummaryTable.DATABASE_TABLE_NAME, where, whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (sUriMatcher.match(uri)) { case ID_TABLE_STATS: return AppStatsTable.CONTENT_TYPE; case ID_TABLE_APPINFO: return AppInfoTable.CONTENT_TYPE; case ID_TABLE_COMMENTS: return CommentsTable.CONTENT_TYPE; case ID_TABLE_ADMOB: return AdmobTable.CONTENT_TYPE; case ID_APP_VERSION_CHANGE: return APP_VERSION_CHANGE; case ID_TABLE_REVENUE_SUMMARY: return RevenueSummaryTable.CONTENT_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 = dbHelper.getWritableDatabase(); long rowId = -1; switch (sUriMatcher.match(uri)) { case ID_TABLE_STATS: rowId = getAppStatsIdByDate(values.getAsString(AppStatsTable.KEY_STATS_PACKAGENAME), parseDate(values.getAsString(AppStatsTable.KEY_STATS_REQUESTDATE)), db); if (rowId > -1) { db.update(AppStatsTable.DATABASE_TABLE_NAME, values, AppStatsTable.KEY_ROWID + "=" + rowId, null); } else { rowId = db.insert(AppStatsTable.DATABASE_TABLE_NAME, null, values); } if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(AppStatsTable.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } case ID_TABLE_APPINFO: Cursor mCursor = db.query(AppInfoTable.DATABASE_TABLE_NAME, new String[] { AppInfoTable.KEY_ROWID, AppInfoTable.KEY_APP_ACCOUNT, AppInfoTable.KEY_APP_PACKAGENAME }, AppInfoTable.KEY_APP_PACKAGENAME + "='" + values.getAsString(AppInfoTable.KEY_APP_PACKAGENAME) + "'", null, null, null, null); if (mCursor != null && mCursor.moveToFirst()) { rowId = mCursor.getInt(mCursor.getColumnIndex(AppInfoTable.KEY_ROWID)); } mCursor.close(); if (rowId > -1) { db.update(AppInfoTable.DATABASE_TABLE_NAME, initialValues, AppInfoTable.KEY_ROWID + "=" + rowId, null); } else { rowId = db.insert(AppInfoTable.DATABASE_TABLE_NAME, null, values); } if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(AppInfoTable.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } case ID_TABLE_COMMENTS: rowId = db.insert(CommentsTable.DATABASE_TABLE_NAME, null, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(CommentsTable.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } case ID_TABLE_ADMOB: rowId = db.insert(AdmobTable.DATABASE_TABLE_NAME, null, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(AdmobTable.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } case ID_TABLE_REVENUE_SUMMARY: rowId = db.insert(RevenueSummaryTable.DATABASE_TABLE_NAME, null, values); if (rowId > 0) { Uri noteUri = ContentUris.withAppendedId(RevenueSummaryTable.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(noteUri, null); return noteUri; } } throw new SQLException("Failed to insert row into " + uri); } @Override public boolean onCreate() { dbHelper = AndlyticsDb.getInstance(getContext()); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; switch (sUriMatcher.match(uri)) { case ID_TABLE_STATS: qb.setTables(AppStatsTable.DATABASE_TABLE_NAME); groupBy = AppStatsTable.KEY_STATS_REQUESTDATE; // fix for duplicate entries qb.setProjectionMap(AppStatsTable.PROJECTION_MAP); break; case ID_TABLE_APPINFO: qb.setTables(AppInfoTable.DATABASE_TABLE_NAME); groupBy = AppInfoTable.KEY_APP_PACKAGENAME; // fix for duplicate entries qb.setProjectionMap(AppInfoTable.PROJECTION_MAP); break; case ID_TABLE_COMMENTS: qb.setTables(CommentsTable.DATABASE_TABLE_NAME); qb.setProjectionMap(CommentsTable.PROJECTION_MAP); break; case ID_TABLE_ADMOB: qb.setTables(AdmobTable.DATABASE_TABLE_NAME); qb.setProjectionMap(AdmobTable.PROJECTION_MAP); break; case ID_APP_VERSION_CHANGE: qb.setTables(AppStatsTable.DATABASE_TABLE_NAME); qb.setProjectionMap(AppStatsTable.PROJECTION_MAP); break; case ID_UNIQUE_PACKAGES: qb.setTables(AppInfoTable.DATABASE_TABLE_NAME); qb.setProjectionMap(AppInfoTable.PACKAGE_NAMES_MAP); qb.setDistinct(true); break; case ID_TABLE_REVENUE_SUMMARY: qb.setTables(RevenueSummaryTable.DATABASE_TABLE_NAME); qb.setProjectionMap(RevenueSummaryTable.PROJECTION_MAP); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // TODO What is the best way to deal with upgrades while still // only getting write access if we really need it? SQLiteDatabase db = null; try { db = dbHelper.getReadableDatabase(); } catch (SQLException ex) { if (ex.getMessage().startsWith("Can't upgrade read-only database")) { db = dbHelper.getWritableDatabase(); } else { throw ex; } } Cursor c = null; if (sUriMatcher.match(uri) == ID_APP_VERSION_CHANGE) { c = qb.query(db, projection, selection, selectionArgs, AppStatsTable.KEY_STATS_VERSIONCODE, null, sortOrder); } else { c = qb.query(db, projection, selection, selectionArgs, groupBy, null, sortOrder); } c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { case ID_TABLE_STATS: count = db.update(AppStatsTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; case ID_TABLE_APPINFO: count = db.update(AppInfoTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; case ID_TABLE_COMMENTS: count = db.update(CommentsTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; case ID_TABLE_LINKS: count = db.update(LinksTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; case ID_TABLE_ADMOB: count = db.update(AdmobTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; case ID_TABLE_REVENUE_SUMMARY: count = db.update(RevenueSummaryTable.DATABASE_TABLE_NAME, values, where, whereArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(AUTHORITY, AppStatsTable.DATABASE_TABLE_NAME, ID_TABLE_STATS); sUriMatcher.addURI(AUTHORITY, AppInfoTable.DATABASE_TABLE_NAME, ID_TABLE_APPINFO); sUriMatcher.addURI(AUTHORITY, AppInfoTable.UNIQUE_PACKAGE_NAMES, ID_UNIQUE_PACKAGES); sUriMatcher.addURI(AUTHORITY, CommentsTable.DATABASE_TABLE_NAME, ID_TABLE_COMMENTS); sUriMatcher.addURI(AUTHORITY, LinksTable.DATABASE_TABLE_NAME, ID_TABLE_LINKS); sUriMatcher.addURI(AUTHORITY, AdmobTable.DATABASE_TABLE_NAME, ID_TABLE_ADMOB); sUriMatcher.addURI(AUTHORITY, APP_VERSION_CHANGE, ID_APP_VERSION_CHANGE); sUriMatcher.addURI(AUTHORITY, RevenueSummaryTable.DATABASE_TABLE_NAME, ID_TABLE_REVENUE_SUMMARY); } public long getAppStatsIdByDate(String packagename, Date date, SQLiteDatabase db) throws SQLException { long result = -1; // make sure there is only one entry / day SimpleDateFormat dateFormatStart = new SimpleDateFormat("yyyy-MM-dd 00:00:00"); SimpleDateFormat dateFormatEnd = new SimpleDateFormat("yyyy-MM-dd 23:59:59"); Cursor mCursor = db.query( AppStatsTable.DATABASE_TABLE_NAME, new String[] { AppStatsTable.KEY_ROWID, AppStatsTable.KEY_STATS_REQUESTDATE }, AppStatsTable.KEY_STATS_PACKAGENAME + "='" + packagename + "' and " + AppStatsTable.KEY_STATS_REQUESTDATE + " BETWEEN '" + dateFormatStart.format(date) + "' and '" + dateFormatEnd.format(date) + "'", null, null, null, null); if (mCursor != null && mCursor.moveToFirst()) { result = mCursor.getInt(mCursor.getColumnIndex(AppStatsTable.KEY_ROWID)); } mCursor.close(); return result; } private Date parseDate(String string) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { return dateFormat.parse(string); } catch (ParseException e) { return null; } } }