diff --git a/README.md b/README.md index 4719903..8a0d50e 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ These are self-explanatory so here is an example: "databaseVersion": 1, "enableForeignKeys": true, "useAnnotations": true, + "useEncryptedDatabase": false } ``` diff --git a/sqliteopenhelpercallbacks.ftl b/sqliteopenhelpercallbacks.ftl new file mode 100644 index 0000000..f162d5f --- /dev/null +++ b/sqliteopenhelpercallbacks.ftl @@ -0,0 +1,48 @@ +<#if header??> +${header} + +package ${config.providerJavaPackage}; + +import android.content.Context; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteDatabase; +<#else> +import android.database.sqlite.SQLiteDatabase; + +import android.util.Log; + +import ${config.projectPackageId}.BuildConfig; + +/** + * Implement your custom database creation or upgrade code here. + * + * This file will not be overwritten if you re-run the content provider generator. + */ +public class ${config.sqliteOpenHelperCallbacksClassName} { + private static final String TAG = ${config.sqliteOpenHelperCallbacksClassName}.class.getSimpleName(); + + public void onOpen(final Context context, final SQLiteDatabase db) { + if (BuildConfig.DEBUG) Log.d(TAG, "onOpen"); + // Insert your db open code here. + } + + public void onPreCreate(final Context context, final SQLiteDatabase db) { + if (BuildConfig.DEBUG) Log.d(TAG, "onPreCreate"); + // Insert your db creation code here. This is called before your tables are created. + } + + public void onPostCreate(final Context context, final SQLiteDatabase db) { + if (BuildConfig.DEBUG) Log.d(TAG, "onPostCreate"); + // Insert your db creation code here. This is called after your tables are created. + } + + public void onUpgrade(final Context context, final SQLiteDatabase db, final int oldVersion, final int newVersion) { + if (BuildConfig.DEBUG) Log.d(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); + // Insert your upgrading code here. + <#if config.optString("sqliteUpgradeHelperClassName")?has_content> + new ${config.sqliteUpgradeHelperClassName}().onUpgrade(db, oldVersion, newVersion); + + } + + +} diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java b/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java old mode 100644 new mode 100755 index 51fa205..996a373 --- a/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/Constants.java @@ -25,6 +25,6 @@ package org.jraf.androidcontentprovidergenerator; public class Constants { - public static final int SYNTAX_VERSION = 3; + public static final int SYNTAX_VERSION = 4; public static final String TAG = ""; } diff --git a/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java b/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java old mode 100644 new mode 100755 index eb774b1..99068c9 --- a/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java +++ b/src/main/java/org/jraf/androidcontentprovidergenerator/Main.java @@ -68,6 +68,8 @@ public static class Json { public static final String PROJECT_PACKAGE_ID = "projectPackageId"; public static final String PROVIDER_JAVA_PACKAGE = "providerJavaPackage"; public static final String PROVIDER_CLASS_NAME = "providerClassName"; + public static final String PROVIDER_CALLBACKS_CLASS_NAME = "providerCallbacksClassName"; + public static final String SQLITE_OPEN_HELPER_CLASS_NAME = "sqliteOpenHelperClassName"; public static final String SQLITE_OPEN_HELPER_CALLBACKS_CLASS_NAME = "sqliteOpenHelperCallbacksClassName"; public static final String AUTHORITY = "authority"; @@ -75,6 +77,7 @@ public static class Json { public static final String DATABASE_VERSION = "databaseVersion"; public static final String ENABLE_FOREIGN_KEY = "enableForeignKeys"; public static final String USE_ANNOTATIONS = "useAnnotations"; + public static final String USE_ENCRYPTED_DATABASE = "useEncryptedDatabase"; } private Configuration mFreemarkerConfig; @@ -264,6 +267,9 @@ private void validateConfig() { ensureString(Json.PROJECT_PACKAGE_ID); ensureString(Json.PROVIDER_JAVA_PACKAGE); ensureString(Json.PROVIDER_CLASS_NAME); + if(ensureBoolean(Json.USE_ENCRYPTED_DATABASE)){ + ensureString(Json.PROVIDER_CALLBACKS_CLASS_NAME); + } ensureString(Json.SQLITE_OPEN_HELPER_CLASS_NAME); ensureString(Json.SQLITE_OPEN_HELPER_CALLBACKS_CLASS_NAME); ensureString(Json.AUTHORITY); @@ -271,6 +277,7 @@ private void validateConfig() { ensureInt(Json.DATABASE_VERSION); ensureBoolean(Json.ENABLE_FOREIGN_KEY); ensureBoolean(Json.USE_ANNOTATIONS); + } private void ensureString(String field) { @@ -281,9 +288,9 @@ private void ensureString(String field) { } } - private void ensureBoolean(String field) { + private boolean ensureBoolean(String field) { try { - mConfig.getBoolean(field); + return mConfig.getBoolean(field); } catch (JSONException e) { throw new IllegalArgumentException("Could not find '" + field + "' field in _config.json, which is mandatory and must be a boolean."); } @@ -455,6 +462,32 @@ private void generateContentProvider(Arguments arguments) throws IOException, JS template.process(root, out); } + + private void generateContentProviderCallbacks(Arguments arguments) throws IOException, JSONException, TemplateException { + JSONObject config = getConfig(arguments.inputDir); + if(config.optBoolean(Json.USE_ENCRYPTED_DATABASE,false)){ + Template template = getFreeMarkerConfig().getTemplate("contentprovidercallbacks.ftl"); + String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE); + File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/')); + providerDir.mkdirs(); + File outputFile = new File(providerDir, config.getString(Json.PROVIDER_CALLBACKS_CLASS_NAME) + ".java"); + if (outputFile.exists()) { + if (Config.LOGD) Log.d(TAG, "generateContentProviderCallbacks content provider callbacks class already exists: skip"); + return; + } + Writer out = new OutputStreamWriter(new FileOutputStream(outputFile)); + + Map root = new HashMap<>(); + root.put("config", config); + root.put("model", Model.get()); + root.put("header", Model.get().getHeader()); + + template.process(root, out); + } + } + + + private void generateSqliteOpenHelper(Arguments arguments) throws IOException, JSONException, TemplateException { Template template = getFreeMarkerConfig().getTemplate("sqliteopenhelper.ftl"); JSONObject config = getConfig(arguments.inputDir); @@ -527,6 +560,8 @@ private void go(String[] args) throws IOException, JSONException, TemplateExcept generateWrappers(arguments); generateModels(arguments); generateContentProvider(arguments); + + generateContentProviderCallbacks(arguments); generateSqliteOpenHelper(arguments); generateSqliteOpenHelperCallbacks(arguments); diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/basecontentprovider.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/basecontentprovider.ftl index be8f3ba..cebb492 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/basecontentprovider.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/basecontentprovider.ftl @@ -13,8 +13,13 @@ import android.content.ContentProviderResult; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteOpenHelper; +<#else> import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; + import android.net.Uri; import android.provider.BaseColumns; <#if config.useAnnotations> @@ -39,13 +44,17 @@ public abstract class BaseContentProvider extends ContentProvider { protected abstract QueryParams getQueryParams(Uri uri, String selection, String[] projection); protected abstract boolean hasDebug(); + <#if config.useEncryptedDatabase> + protected abstract String getPassword(); + protected abstract SQLiteOpenHelper createSqLiteOpenHelper(); protected SQLiteOpenHelper mSqLiteOpenHelper; + @Override - public final boolean onCreate() { + public boolean onCreate() { if (hasDebug()) { // Enable logging of SQL statements as they are executed. try { @@ -70,7 +79,11 @@ public abstract class BaseContentProvider extends ContentProvider { @Override public Uri insert(Uri uri, ContentValues values) { String table = uri.getLastPathSegment(); + <#if config.useEncryptedDatabase> + long rowId = mSqLiteOpenHelper.getWritableDatabase(getPassword()).insertOrThrow(table, null, values); + <#else> long rowId = mSqLiteOpenHelper.getWritableDatabase().insertOrThrow(table, null, values); + if (rowId == -1) return null; String notify; if (((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { @@ -82,7 +95,11 @@ public abstract class BaseContentProvider extends ContentProvider { @Override public int bulkInsert(Uri uri, ContentValues[] values) { String table = uri.getLastPathSegment(); + <#if config.useEncryptedDatabase> + SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(getPassword()); + <#else> SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); + int res = 0; db.beginTransaction(); try { @@ -108,7 +125,11 @@ public abstract class BaseContentProvider extends ContentProvider { @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { QueryParams queryParams = getQueryParams(uri, selection, null); + <#if config.useEncryptedDatabase> + int res = mSqLiteOpenHelper.getWritableDatabase(getPassword()).update(queryParams.table, values, queryParams.selection, selectionArgs); + <#else> int res = mSqLiteOpenHelper.getWritableDatabase().update(queryParams.table, values, queryParams.selection, selectionArgs); + String notify; if (res != 0 && ((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { getContext().getContentResolver().notifyChange(uri, null); @@ -119,7 +140,12 @@ public abstract class BaseContentProvider extends ContentProvider { @Override public int delete(Uri uri, String selection, String[] selectionArgs) { QueryParams queryParams = getQueryParams(uri, selection, null); + <#if config.useEncryptedDatabase> + int res = mSqLiteOpenHelper.getWritableDatabase(getPassword()).delete(queryParams.table, queryParams.selection, selectionArgs); + <#else> int res = mSqLiteOpenHelper.getWritableDatabase().delete(queryParams.table, queryParams.selection, selectionArgs); + + String notify; if (res != 0 && ((notify = uri.getQueryParameter(QUERY_NOTIFY)) == null || "true".equals(notify))) { getContext().getContentResolver().notifyChange(uri, null); @@ -134,8 +160,14 @@ public abstract class BaseContentProvider extends ContentProvider { String limit = uri.getQueryParameter(QUERY_LIMIT); QueryParams queryParams = getQueryParams(uri, selection, projection); projection = ensureIdIsFullyQualified(projection, queryParams.table, queryParams.idColumn); + <#if config.useEncryptedDatabase> + Cursor res = mSqLiteOpenHelper.getReadableDatabase(getPassword()).query(queryParams.tablesWithJoins, projection, queryParams.selection, selectionArgs, + groupBy, having, sortOrder == null ? queryParams.orderBy : sortOrder, limit); + <#else> Cursor res = mSqLiteOpenHelper.getReadableDatabase().query(queryParams.tablesWithJoins, projection, queryParams.selection, selectionArgs, groupBy, having, sortOrder == null ? queryParams.orderBy : sortOrder, limit); + + res.setNotificationUri(getContext().getContentResolver(), uri); return res; } @@ -159,7 +191,11 @@ public abstract class BaseContentProvider extends ContentProvider { for (ContentProviderOperation operation : operations) { urisToNotify.add(operation.getUri()); } + <#if config.useEncryptedDatabase> + SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(getPassword()); + <#else> SQLiteDatabase db = mSqLiteOpenHelper.getWritableDatabase(); + db.beginTransaction(); try { int numOperations = operations.size(); diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl index fcdd915..7bee6f8 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovider.ftl @@ -8,7 +8,11 @@ import java.util.Arrays; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; -import android.database.sqlite.SQLiteOpenHelper; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteOpenHelper; +<#else> +import android.database.sqlite.SQLiteDatabase; + import android.net.Uri; <#if config.useAnnotations> import android.support.annotation.NonNull; @@ -32,6 +36,11 @@ public class ${config.providerClassName} extends BaseContentProvider { public static final String AUTHORITY = "${config.authority}"; public static final String CONTENT_URI_BASE = "content://" + AUTHORITY; + <#if config.useEncryptedDatabase> + private ${config.providerCallbacksClassName} mProviderCallbacks; + + + <#assign i=0> <#list model.entities as entity> private static final int URI_TYPE_${entity.nameUpperCase} = ${i}; @@ -42,7 +51,7 @@ public class ${config.providerClassName} extends BaseContentProvider { - private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); + protected static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); static { <#list model.entities as entity> @@ -51,11 +60,30 @@ public class ${config.providerClassName} extends BaseContentProvider { } + protected ${config.sqliteOpenHelperClassName} m${config.sqliteOpenHelperClassName}; + <#if config.useEncryptedDatabase> + + @Override + public final boolean onCreate() { + super.onCreate(); + mProviderCallbacks = new ${config.providerCallbacksClassName}(); + return false; + } + + @Override + protected String getPassword(){ + return mProviderCallbacks.onPasswordRequested(); + } + + + @Override protected SQLiteOpenHelper createSqLiteOpenHelper() { return ${config.sqliteOpenHelperClassName}.getInstance(getContext()); } + + @Override protected boolean hasDebug() { return DEBUG; diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovidercallbacks.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovidercallbacks.ftl new file mode 100644 index 0000000..179e3e9 --- /dev/null +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/contentprovidercallbacks.ftl @@ -0,0 +1,30 @@ +<#if header??> +${header} + +package ${config.providerJavaPackage}; + +import android.content.Context; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteDatabase; +<#else> +import android.database.sqlite.SQLiteDatabase; + +import android.util.Log; + +import ${config.projectPackageId}.BuildConfig; + +/** + * Implement your custom database creation or upgrade code here. + * + * This file will not be overwritten if you re-run the content provider generator. + */ +public class ${config.providerCallbacksClassName} { + private static final String TAG = ${config.providerCallbacksClassName}.class.getSimpleName(); + + public String onPasswordRequested() { + if (BuildConfig.DEBUG) Log.d(TAG, "onPasswordRequested"); + //TODO Insert your own password provider + return "password"; + + } +} diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl index d968247..af79f53 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelper.ftl @@ -7,8 +7,13 @@ import android.annotation.TargetApi; import android.content.Context; import android.database.DatabaseErrorHandler; import android.database.DefaultDatabaseErrorHandler; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteOpenHelper; +<#else> import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; + import android.os.Build; import android.util.Log; @@ -70,19 +75,27 @@ public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { } private static ${config.sqliteOpenHelperClassName} newInstance(Context context) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { + + <#if config.useEncryptedDatabase> + return new ${config.sqliteOpenHelperClassName}(context); + <#else> + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return newInstancePreHoneycomb(context); } return newInstancePostHoneycomb(context); + + } + <#if config.useEncryptedDatabase == false> /* * Pre Honeycomb. */ private static ${config.sqliteOpenHelperClassName} newInstancePreHoneycomb(Context context) { return new ${config.sqliteOpenHelperClassName}(context); } + private ${config.sqliteOpenHelperClassName}(Context context) { super(context, DATABASE_FILE_NAME, null, DATABASE_VERSION); @@ -90,7 +103,7 @@ public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { mOpenHelperCallbacks = new ${config.sqliteOpenHelperCallbacksClassName}(); } - + <#if config.useEncryptedDatabase == false> /* * Post Honeycomb. */ @@ -105,6 +118,7 @@ public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { mContext = context; mOpenHelperCallbacks = new ${config.sqliteOpenHelperCallbacksClassName}(); } + @Override @@ -134,14 +148,20 @@ public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { } <#if config.enableForeignKeys > - private void setForeignKeyConstraintsEnabled(SQLiteDatabase db) { + private void setForeignKeyConstraintsEnabled(SQLiteDatabase db) { + <#if config.useEncryptedDatabase> + db.execSQL("PRAGMA foreign_keys=ON;"); + <#else> if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { setForeignKeyConstraintsEnabledPreJellyBean(db); } else { setForeignKeyConstraintsEnabledPostJellyBean(db); } + } + + <#if config.useEncryptedDatabase == false> private void setForeignKeyConstraintsEnabledPreJellyBean(SQLiteDatabase db) { db.execSQL("PRAGMA foreign_keys=ON;"); } @@ -150,10 +170,13 @@ public class ${config.sqliteOpenHelperClassName} extends SQLiteOpenHelper { private void setForeignKeyConstraintsEnabledPostJellyBean(SQLiteDatabase db) { db.setForeignKeyConstraintsEnabled(true); } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { mOpenHelperCallbacks.onUpgrade(mContext, db, oldVersion, newVersion); } + + } diff --git a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelpercallbacks.ftl b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelpercallbacks.ftl index 41a39e9..f162d5f 100644 --- a/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelpercallbacks.ftl +++ b/src/main/resources/org/jraf/androidcontentprovidergenerator/sqliteopenhelpercallbacks.ftl @@ -4,8 +4,11 @@ ${header} package ${config.providerJavaPackage}; import android.content.Context; +<#if config.useEncryptedDatabase> +import net.sqlcipher.database.SQLiteDatabase; +<#else> import android.database.sqlite.SQLiteDatabase; - + import android.util.Log; import ${config.projectPackageId}.BuildConfig; @@ -40,4 +43,6 @@ public class ${config.sqliteOpenHelperCallbacksClassName} { new ${config.sqliteUpgradeHelperClassName}().onUpgrade(db, oldVersion, newVersion); } + + }