From 07aa98a6cea2ccb09f601cfab4e8973f9da3a832 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Mon, 11 Apr 2016 16:54:32 +0300 Subject: [PATCH 01/14] gradle:2.0.0 --- ProviGenSample/build.gradle | 9 +++++++-- ProviGenSample/project.properties | 2 +- ProviGenTests/build.gradle | 4 ++-- build.gradle | 9 ++++----- gradle/wrapper/gradle-wrapper.properties | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/ProviGenSample/build.gradle b/ProviGenSample/build.gradle index d21758c..d83bc63 100644 --- a/ProviGenSample/build.gradle +++ b/ProviGenSample/build.gradle @@ -1,7 +1,12 @@ android { - buildToolsVersion "19.1.0" - compileSdkVersion 19 + buildToolsVersion "23.0.3" + compileSdkVersion 23 + + dependencies { + compile 'com.android.support:support-v4:23.3.0' + } + sourceSets { main { manifest.srcFile 'AndroidManifest.xml' diff --git a/ProviGenSample/project.properties b/ProviGenSample/project.properties index ed6ad53..a3fff05 100644 --- a/ProviGenSample/project.properties +++ b/ProviGenSample/project.properties @@ -11,5 +11,5 @@ #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt # Project target. -target=android-19 +target=android-23 diff --git a/ProviGenTests/build.gradle b/ProviGenTests/build.gradle index 2d1a49f..d9f30f9 100644 --- a/ProviGenTests/build.gradle +++ b/ProviGenTests/build.gradle @@ -1,6 +1,6 @@ android { - buildToolsVersion "19.1.0" - compileSdkVersion 19 + buildToolsVersion "23.0.3" + compileSdkVersion 23 sourceSets { main { diff --git a/build.gradle b/build.gradle index 9613b1f..907e98d 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.0' + classpath 'com.android.tools.build:gradle:2.0.0' } } @@ -16,7 +16,7 @@ allprojects { } project(":ProviGenLib") { - apply plugin: "java" + apply plugin: 'java' dependencies { compile 'com.google.android:android:4.1.1.4' @@ -24,16 +24,15 @@ project(":ProviGenLib") { } project(":ProviGenSample") { - apply plugin: "android" + apply plugin: 'com.android.application' dependencies { compile project(":ProviGenLib") - compile 'com.android.support:support-v4:21.0.3' } } project(":ProviGenTests") { - apply plugin: "android" + apply plugin: 'com.android.application' dependencies { compile project(":ProviGenLib") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7b6f538..1d4094d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip From 4f66cd5f86696d700b2e41f574b2ac88872721de Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Mon, 11 Apr 2016 18:52:45 +0300 Subject: [PATCH 02/14] Support for multiple databases + @Id autoincrement param added --- .../provigen/ProviGenBaseContract.java | 4 +- .../tjeannin/provigen/ProviGenOpenHelper.java | 7 +- .../tjeannin/provigen/ProviGenProvider.java | 227 +++++++++++++----- .../com/tjeannin/provigen/annotation/Id.java | 3 +- .../provigen/helper/ContractUtil.java | 37 +++ .../provigen/helper/ProviGenUriBuilder.java | 31 +++ .../provigen/helper/TableBuilder.java | 41 +++- .../com/tjeannin/provigen/model/Contract.java | 35 ++- .../provigen/sample/MainActivity.java | 35 ++- .../sample/SampleContentProvider.java | 49 ++-- .../provigen/sample/SampleContract.java | 110 +++++++++ 11 files changed, 474 insertions(+), 105 deletions(-) create mode 100644 ProviGenLib/src/com/tjeannin/provigen/helper/ContractUtil.java create mode 100644 ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java create mode 100644 ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenBaseContract.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenBaseContract.java index 267aca9..deef5ee 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenBaseContract.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenBaseContract.java @@ -13,8 +13,8 @@ public interface ProviGenBaseContract { /** * The unique ID for a row. */ - @Id + @Id(autoincrement = true) @Column(Type.INTEGER) - public static final String _ID = BaseColumns._ID; + String _ID = BaseColumns._ID; } diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenOpenHelper.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenOpenHelper.java index 5214b6f..7e6db3f 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenOpenHelper.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenOpenHelper.java @@ -42,9 +42,14 @@ public void onCreate(SQLiteDatabase database) { @Override public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) { + // create new tables if not exists + onCreate(database); + + // add missing columns if (newVersion > oldVersion) { - for (Class contract : contracts) + for (Class contract : contracts) { TableUpdater.addMissingColumns(database, contract); + } } } } diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java index 975019c..2d1c494 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java @@ -1,11 +1,18 @@ package com.tjeannin.provigen; -import android.content.*; +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.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; +import android.os.Build; import android.text.TextUtils; + import com.tjeannin.provigen.model.Contract; import java.util.ArrayList; @@ -16,15 +23,16 @@ */ public abstract class ProviGenProvider extends ContentProvider { - private List contracts = new ArrayList(); - - private UriMatcher uriMatcher; private static final int ITEM = 1; private static final int ITEM_ID = 2; - private SQLiteOpenHelper openHelper; + + private List contracts = new ArrayList<>(); + private UriMatcher uriMatcher; + private SQLiteOpenHelper[] openHelpers; /** * This method should return an instance of a {@link android.database.sqlite.SQLiteOpenHelper}. + * If you want to use multiple databases override {@link #openHelpers(Context)} and you may return null in this method. * It will be called only once, so you can safely create a new instance of the SQLiteOpenHelper on the method body. * * @param context A context to pass to the SQLiteOpenHelper instance while creating it. @@ -32,6 +40,15 @@ public abstract class ProviGenProvider extends ContentProvider { */ public abstract SQLiteOpenHelper openHelper(Context context); + /** + * Override if you want to use multiple databases. + * @param context A context to pass to the SQLiteOpenHelper instance while creating it. + * @return the SQLiteOpenHelper[] that the ProviGenProvider will use. + */ + public SQLiteOpenHelper [] openHelpers(Context context) { + return new SQLiteOpenHelper[] { openHelper(context) }; + } + /** * This method should return the list of contract classes that the ProviGenProvider will use. * It will be called only once. @@ -40,154 +57,219 @@ public abstract class ProviGenProvider extends ContentProvider { */ public abstract Class[] contractClasses(); + /** + * Override if you want to use multiple databases. + * This method should return the array of array contract classes that the ProviGenProvider will use. + * It will be called only once. + * + * @return an array of array of contract classes. + */ + public Class[][] contractClassesMultipleDb() { + return new Class[][] { contractClasses() }; + } + + /** + * Override for insert conflict resolver. By default return SQLiteDatabase.CONFLICT_REPLACE. + */ + public int conflictAlgorithm() { + return SQLiteDatabase.CONFLICT_REPLACE; + } + @Override public boolean onCreate() { + openHelpers = openHelpers(getContext()); - openHelper = openHelper(getContext()); - for (Class contract : contractClasses()) { - contracts.add(new Contract(contract)); + for(Class[] contractArray : contractClassesMultipleDb()) { + for (Class contract : contractArray) { + contracts.add(new Contract(contract)); + } } uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); for (Contract contract : contracts) { - uriMatcher.addURI(contract.getAuthority(), contract.getTable(), ITEM); - uriMatcher.addURI(contract.getAuthority(), contract.getTable() + "/#", ITEM_ID); + if(contract.getDbName() == null) { + uriMatcher.addURI(contract.getAuthority(), contract.getTable(), ITEM); + uriMatcher.addURI(contract.getAuthority(), contract.getTable() + "/#", ITEM_ID); + } else { + uriMatcher.addURI(contract.getAuthority(), contract.getDbName() + "/" + contract.getTable(), ITEM); + uriMatcher.addURI(contract.getAuthority(), contract.getDbName() + "/" + contract.getTable() + "/#", ITEM_ID); + } } return true; } @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - SQLiteDatabase database = openHelper.getWritableDatabase(); - - int numberOfRowsAffected = 0; + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); Contract contract = findMatchingContract(uri); + SQLiteDatabase db = openDatabase(contract, true); + Cursor cursor; switch (uriMatcher.match(uri)) { case ITEM: - numberOfRowsAffected = database.delete(contract.getTable(), selection, selectionArgs); + queryBuilder.setTables(contract.getTable()); + cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); break; + case ITEM_ID: String itemId = String.valueOf(ContentUris.parseId(uri)); - if (TextUtils.isEmpty(selection)) { - numberOfRowsAffected = database.delete(contract.getTable(), contract.getIdField() + " = ? ", new String[]{itemId}); + queryBuilder.setTables(contract.getTable()); + cursor = queryBuilder.query(db, projection, contract.getIdFields().get(0) + " = ? ", new String[]{itemId}, null, null, sortOrder); } else { - numberOfRowsAffected = database.delete(contract.getTable(), selection + " AND " + - contract.getIdField() + " = ? ", appendToStringArray(selectionArgs, itemId)); + queryBuilder.setTables(contract.getTable()); + cursor = queryBuilder.query(db, projection, selection + " AND " + contract.getIdFields().get(0) + " = ? ", appendToStringArray(selectionArgs, itemId), null, null, sortOrder); } break; + default: throw new IllegalArgumentException("Unknown uri " + uri); } - if (numberOfRowsAffected > 0) { - getContext().getContentResolver().notifyChange(uri, null); - } - return numberOfRowsAffected; + // Make sure that potential listeners are getting notified. + cursor.setNotificationUri(getContext().getContentResolver(), uri); + + return cursor; } @Override - public String getType(Uri uri) { - + public Uri insert(Uri uri, ContentValues values) { Contract contract = findMatchingContract(uri); + SQLiteDatabase db = openDatabase(contract, false); switch (uriMatcher.match(uri)) { case ITEM: - return "vnd.android.cursor.dir/vdn." + contract.getTable(); - case ITEM_ID: - return "vnd.android.cursor.item/vdn." + contract.getTable(); + long rowId = db.insertWithOnConflict(contract.getTable(), null, values, conflictAlgorithm()); + Uri rowUri = Uri.EMPTY; + if (rowId > 0) { + rowUri = ContentUris.withAppendedId(uri, rowId); + getContext().getContentResolver().notifyChange(rowUri, null); + } + + return rowUri; + default: throw new IllegalArgumentException("Unknown uri " + uri); } } @Override - public Uri insert(Uri uri, ContentValues values) { - SQLiteDatabase database = openHelper.getWritableDatabase(); - + public int bulkInsert(Uri uri, ContentValues[] values) { Contract contract = findMatchingContract(uri); + SQLiteDatabase db = openDatabase(contract, false); + switch (uriMatcher.match(uri)) { case ITEM: - long itemId = database.insert(contract.getTable(), null, values); - getContext().getContentResolver().notifyChange(uri, null); - return Uri.withAppendedPath(uri, String.valueOf(itemId)); + db.beginTransaction(); + try { + // вставляем + for (ContentValues cv : values) { + db.insertWithOnConflict(contract.getTable(), null, cv, conflictAlgorithm()); + } + db.setTransactionSuccessful(); + + Context context = getContext(); + if(context != null) { + context.getContentResolver().notifyChange(uri, null); + } + } finally { + db.endTransaction(); + } + return values.length; + default: throw new IllegalArgumentException("Unknown uri " + uri); } } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - SQLiteDatabase database = openHelper.getReadableDatabase(); - + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Contract contract = findMatchingContract(uri); - Cursor cursor = null; + SQLiteDatabase db = openDatabase(contract, false); + int numberOfRowsAffected; switch (uriMatcher.match(uri)) { case ITEM: - cursor = database.query(contract.getTable(), projection, selection, selectionArgs, "", "", sortOrder); + numberOfRowsAffected = db.update(contract.getTable(), values, selection, selectionArgs); break; + case ITEM_ID: String itemId = String.valueOf(ContentUris.parseId(uri)); if (TextUtils.isEmpty(selection)) { - cursor = database.query(contract.getTable(), projection, contract.getIdField() + " = ? ", new String[]{itemId}, "", "", sortOrder); + numberOfRowsAffected = db.update(contract.getTable(), values, contract.getIdFields().get(0) + " = ? ", new String[]{itemId}); } else { - cursor = database.query(contract.getTable(), projection, selection + " AND " + contract.getIdField() + " = ? ", - appendToStringArray(selectionArgs, itemId), "", "", sortOrder); + numberOfRowsAffected = db.update(contract.getTable(), values, selection + " AND " + contract.getIdFields().get(0) + " = ? ", + appendToStringArray(selectionArgs, itemId)); } break; + default: throw new IllegalArgumentException("Unknown uri " + uri); } - - // Make sure that potential listeners are getting notified. - cursor.setNotificationUri(getContext().getContentResolver(), uri); - - return cursor; + getContext().getContentResolver().notifyChange(uri, null); + return numberOfRowsAffected; } @Override - public int update(Uri uri, ContentValues values, String selection, - String[] selectionArgs) { - SQLiteDatabase database = openHelper.getWritableDatabase(); - + public int delete(Uri uri, String selection, String[] selectionArgs) { Contract contract = findMatchingContract(uri); - int numberOfRowsAffected = 0; + SQLiteDatabase db = openDatabase(contract, false); + int numberOfRowsAffected; switch (uriMatcher.match(uri)) { case ITEM: - numberOfRowsAffected = database.update(contract.getTable(), values, selection, selectionArgs); + numberOfRowsAffected = db.delete(contract.getTable(), selection, selectionArgs); break; + case ITEM_ID: String itemId = String.valueOf(ContentUris.parseId(uri)); - if (TextUtils.isEmpty(selection)) { - numberOfRowsAffected = database.update(contract.getTable(), values, contract.getIdField() + " = ? ", new String[]{itemId}); + numberOfRowsAffected = db.delete(contract.getTable(), contract.getIdFields().get(0) + " = ? ", new String[]{itemId}); } else { - numberOfRowsAffected = database.update(contract.getTable(), values, selection + " AND " + contract.getIdField() + " = ? ", - appendToStringArray(selectionArgs, itemId)); + numberOfRowsAffected = db.delete(contract.getTable(), selection + " AND " + + contract.getIdFields().get(0) + " = ? ", appendToStringArray(selectionArgs, itemId)); } break; + default: throw new IllegalArgumentException("Unknown uri " + uri); } - if (numberOfRowsAffected > 0) { - getContext().getContentResolver().notifyChange(uri, null); - } + getContext().getContentResolver().notifyChange(uri, null); return numberOfRowsAffected; } + @Override + public String getType(Uri uri) { + Contract contract = findMatchingContract(uri); + + switch (uriMatcher.match(uri)) { + case ITEM: + return "vnd.android.cursor.dir/vdn." + contract.getTable(); + + case ITEM_ID: + return "vnd.android.cursor.item/vdn." + contract.getTable(); + + default: + throw new IllegalArgumentException("Unknown uri " + uri); + } + } + /** * @param uri The {@link Uri} to be matched. * @return A {@link com.tjeannin.provigen.model.Contract} matching the given {@link Uri}. */ public Contract findMatchingContract(Uri uri) { for (Contract contract : contracts) { - if (contract.getTable().equals(uri.getPathSegments().get(0))) { - return contract; + if(contract.getDbName() == null) { + if (contract.getTable().equals(uri.getPathSegments().get(0))) { + return contract; + } + } else { + if (contract.getDbName().equals(uri.getPathSegments().get(0)) && contract.getTable().equals(uri.getPathSegments().get(1))) { + return contract; + } } } return null; @@ -211,4 +293,25 @@ private static String[] appendToStringArray(String[] array, String element) { } } + /** + * Open database + * @param contract Contract class + * @param readOnly Open database only for read + * @return SQLiteDatabase object + */ + private SQLiteDatabase openDatabase(Contract contract, boolean readOnly) { + if(contract.getDbName() == null) { + return readOnly ? openHelpers[0].getReadableDatabase() : openHelpers[0].getWritableDatabase(); + } + else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + for (SQLiteOpenHelper helper : openHelpers) { + if (contract.getDbName().equals(helper.getDatabaseName())) { + return readOnly ? helper.getReadableDatabase() : helper.getWritableDatabase(); + } + } + } + return readOnly ? openHelpers[1].getReadableDatabase() : openHelpers[1].getWritableDatabase(); + } + } } diff --git a/ProviGenLib/src/com/tjeannin/provigen/annotation/Id.java b/ProviGenLib/src/com/tjeannin/provigen/annotation/Id.java index c2326de..11e5234 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/annotation/Id.java +++ b/ProviGenLib/src/com/tjeannin/provigen/annotation/Id.java @@ -7,9 +7,10 @@ /** * Identifies a field of a contract class that should be used as a primary key in the database.
- * This annotation should be used alongside with a {@link Column} annotation. + * This annotation should be used alongside with a {@link Column} annotation. The autoincrement attribute should be true or false (by default). */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Id { + boolean autoincrement() default false; } diff --git a/ProviGenLib/src/com/tjeannin/provigen/helper/ContractUtil.java b/ProviGenLib/src/com/tjeannin/provigen/helper/ContractUtil.java new file mode 100644 index 0000000..82e4b0a --- /dev/null +++ b/ProviGenLib/src/com/tjeannin/provigen/helper/ContractUtil.java @@ -0,0 +1,37 @@ +package com.tjeannin.provigen.helper; + +/** + * Created by Dre on 08.04.2016. + * + */ +public class ContractUtil { + + /** + * Return full name of field + * @param table Table + * @param field Field + * @return Full name of field + */ + public static String fullName(String table, String field) { + return table + "." + field; + } + + /** + * Return name of field after join + * @param table Table + * @param field Field + * @return Name of field after join + */ + public static String joinName(String table, String field) { + if(table.endsWith("_") && field.startsWith("_")) { + return table + field.substring(1 , field.length()); + } + else if(table.endsWith("_") || field.startsWith("_")) { + return table + field; + } + else { + return table + "_" + field; + } + } + +} diff --git a/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java b/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java new file mode 100644 index 0000000..0bf1df8 --- /dev/null +++ b/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java @@ -0,0 +1,31 @@ +package com.tjeannin.provigen.helper; + +import android.net.Uri; + +/** + * Created by Dre on 07.04.2016. + * + */ +public class ProviGenUriBuilder { + + /** + * Create content URI for contract class + * @param authority authority + * @param tableName tableName + * @return Content URI + */ + public static Uri contentUri(String authority, String tableName) { + return contentUri(authority, tableName, null); + } + + /** + * Create content URI with database name for contract class + * @param authority authority + * @param tableName tableName + * @param dbName database name + * @return Content URI + */ + public static Uri contentUri(String authority, String tableName, String dbName) { + return Uri.parse("content://" + authority + "/" + (dbName != null ? dbName + "/" : "") + tableName); + } +} diff --git a/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java b/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java index 7677c30..f02839a 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java +++ b/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java @@ -1,6 +1,8 @@ package com.tjeannin.provigen.helper; import android.database.sqlite.SQLiteDatabase; + +import com.tjeannin.provigen.annotation.Column; import com.tjeannin.provigen.model.Constraint; import com.tjeannin.provigen.model.Contract; import com.tjeannin.provigen.model.ContractField; @@ -21,7 +23,7 @@ public class TableBuilder { */ public TableBuilder(Class contractClass) { contract = new Contract(contractClass); - constraints = new ArrayList(); + constraints = new ArrayList<>(); } /** @@ -55,23 +57,52 @@ public TableBuilder addConstraint(String columnName, String constraintType, Stri */ public String getSQL() { - StringBuilder builder = new StringBuilder("CREATE TABLE "); + StringBuilder builder = new StringBuilder("CREATE TABLE IF NOT EXISTS "); builder.append(contract.getTable()).append(" ( "); for (ContractField field : contract.getFields()) { builder.append(" ").append(field.name).append(" ").append(field.type); - if (field.name.equals(contract.getIdField())) { - builder.append(" PRIMARY KEY AUTOINCREMENT "); + + // single PRIMARY KEY + if(contract.getIdFields().size() == 1) { + if (field.name.equals(contract.getIdFields().get(0))) { + builder.append(" PRIMARY KEY "); + + if (field.type.equals(Column.Type.INTEGER)) { + if(contract.isAutoincrement(field.name)) { + builder.append(" AUTOINCREMENT "); + } + } + } } + for (Constraint constraint : constraints) { if (constraint.targetColumn.equals(field.name)) { builder.append(" ").append(constraint.type).append(" ON CONFLICT ").append(constraint.conflictClause); } } + builder.append(", "); } - builder.deleteCharAt(builder.length() - 2); + + // composite PRIMARY KEY + if(contract.getIdFields().size() > 1) { + builder.append(" PRIMARY KEY ( "); + // пробегаем по полям, помеченными как @Id + for(int i = 0; i < contract.getIdFields().size(); i++) { + builder.append(contract.getIdFields().get(i)); + if(i < contract.getIdFields().size() - 1) { + builder.append(", "); + } + } + builder.append(" ) "); + } + else { + // delete ',' in the end + builder.deleteCharAt(builder.length() - 2); + } builder.append(" ) "); + return builder.toString(); } diff --git a/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java b/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java index 44e6d98..1c43bf5 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java +++ b/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java @@ -7,19 +7,23 @@ import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class Contract { private String authority; - private String idField; + private String dbName; private String tableName; private List contractFields; + private Map idFieldMap; @SuppressWarnings({"rawtypes", "unchecked"}) public Contract(Class contractClass) { - contractFields = new ArrayList(); + idFieldMap = new HashMap<>(); + contractFields = new ArrayList<>(); Field[] fields = contractClass.getFields(); for (Field field : fields) { @@ -30,15 +34,22 @@ public Contract(Class contractClass) { Uri uri = (Uri) field.get(null); authority = uri.getAuthority(); tableName = uri.getLastPathSegment(); + + List pathSegments = uri.getPathSegments(); + if(pathSegments.size() == 2) { + dbName = pathSegments.get(0); + } + } catch (Exception e) { e.printStackTrace(); } } - Id id = field.getAnnotation(Id.class); + Id id = field.getAnnotation(Id.class); if (id != null) { try { - idField = (String) field.get(null); + String idField = (String) field.get(null); + idFieldMap.put(idField, id.autoincrement()); } catch (Exception e) { e.printStackTrace(); } @@ -59,15 +70,23 @@ public String getAuthority() { return authority; } - public String getIdField() { - return idField; - } - public String getTable() { return tableName; } + public String getDbName() { + return dbName; + } + + public List getIdFields() { + return new ArrayList<>(idFieldMap.keySet()); + } + public List getFields() { return contractFields; } + + public boolean isAutoincrement(String field) { + return idFieldMap.get(field); + } } diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java index b882fef..fcde7c0 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java @@ -15,6 +15,8 @@ public class MainActivity extends FragmentActivity implements LoaderCallbacks, OnClickListener { + private static final String[] NAMES = {"David", "Stephanie", "John", "Anna", "Thomas", "Natalie", "Andrew", "Sofia", "Richard", "Alexandra"}; + private static final String[] SPECIALTIES = {"Android Developer", "iOS Developer", "Backend Developer", "Frontend Developer", "Team Lead", "Project Manager", "CEO"}; private SimpleCursorAdapter adapter; @Override @@ -23,8 +25,8 @@ public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); String[] columns = new String[]{ - SampleContentProvider.Person.AGE, - SampleContentProvider.Person.NAME}; + SampleContract.Person.AGE, + SampleContract.Person.NAME}; int[] ids = { R.id.person_age, @@ -46,9 +48,27 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } + private void addPerson() { + ContentValues values = new ContentValues(); + values.put(SampleContract.Person.AGE, (int) (Math.random() * 40 + 20)); + values.put(SampleContract.Person.NAME, NAMES[(int) (Math.random() * NAMES.length)]); + getContentResolver().insert(SampleContract.Person.CONTENT_URI, values); + } + + private void addSpecialty() { + ContentValues values = new ContentValues(); + values.put(SampleContract.Specialty._ID, (int) (Math.random() * SPECIALTIES.length)); + values.put(SampleContract.Specialty.NAME, SPECIALTIES[(int) (Math.random() * SPECIALTIES.length)]); + getContentResolver().insert(SampleContract.Person.CONTENT_URI, values); + } + + private void clearAll() { + getContentResolver().delete(SampleContract.Person.CONTENT_URI, null, null); + } + @Override public Loader onCreateLoader(int id, Bundle args) { - return new CursorLoader(this, SampleContentProvider.Person.CONTENT_URI, null, "", null, ""); + return new CursorLoader(this, SampleContract.Person.CONTENT_URI, null, null, null, null); } @Override @@ -66,17 +86,14 @@ public void onClick(View view) { switch (view.getId()) { case R.id.add: - ContentValues values = new ContentValues(); - values.put(SampleContentProvider.Person.AGE, 20); - values.put(SampleContentProvider.Person.NAME, "Some Name"); - getContentResolver().insert(SampleContentProvider.Person.CONTENT_URI, values); + addPerson(); break; case R.id.delete: - getContentResolver().delete(SampleContentProvider.Person.CONTENT_URI, "", null); + clearAll(); break; - default: + default: break; } diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java index f141dd6..de79883 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java @@ -2,36 +2,51 @@ import android.content.Context; import android.database.sqlite.SQLiteOpenHelper; -import android.net.Uri; -import com.tjeannin.provigen.ProviGenBaseContract; + import com.tjeannin.provigen.ProviGenOpenHelper; import com.tjeannin.provigen.ProviGenProvider; -import com.tjeannin.provigen.annotation.Column; -import com.tjeannin.provigen.annotation.Column.Type; -import com.tjeannin.provigen.annotation.ContentUri; public class SampleContentProvider extends ProviGenProvider { + public static final String AUTHORITY = "com.tjeannin.provigen.sample"; + + public static final String DB_NAME = "ProviGenDatabase"; + public static final int DB_VERSION = 1; + + public static final String SECOND_DB_NAME = "ProviGenDatabaseSecond"; + public static final int SECOND_DB_VERSION = 1; + + private static final Class[] CONTRACTS = new Class[] { + SampleContract.Person.class, + SampleContract.Specialty.class, + SampleContract.Passport.class + }; + + private static final Class[] CONTRACTS_SECOND = new Class[] { + SampleContract.PersonSecondDb.class + }; + @Override public SQLiteOpenHelper openHelper(Context context) { - return new ProviGenOpenHelper(getContext(), "ProviGenDatabase", null, 1, new Class[]{Person.class}); + return null; } @Override - public Class[] contractClasses() { - return new Class[]{Person.class}; + public SQLiteOpenHelper[] openHelpers(Context context) { + return new SQLiteOpenHelper[] { + new ProviGenOpenHelper(getContext(), DB_NAME, null, DB_VERSION, CONTRACTS), // first db + new ProviGenOpenHelper(getContext(), SECOND_DB_NAME, null, SECOND_DB_VERSION, CONTRACTS_SECOND) // second db + }; } - public static interface Person extends ProviGenBaseContract { - - @Column(Type.INTEGER) - public static final String AGE = "int"; - - @Column(Type.TEXT) - public static final String NAME = "string"; + @Override + public Class[] contractClasses() { + return null; + } - @ContentUri - public static final Uri CONTENT_URI = Uri.parse("content://com.tjeannin.provigen.sample/persons"); + @Override + public Class[][] contractClassesMultipleDb() { + return new Class[][] {CONTRACTS, CONTRACTS_SECOND}; } } diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java new file mode 100644 index 0000000..709c7c0 --- /dev/null +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java @@ -0,0 +1,110 @@ +package com.tjeannin.provigen.sample; + +import android.net.Uri; + +import com.tjeannin.provigen.ProviGenBaseContract; +import com.tjeannin.provigen.annotation.Column; +import com.tjeannin.provigen.annotation.ContentUri; +import com.tjeannin.provigen.annotation.Id; +import com.tjeannin.provigen.helper.ContractUtil; +import com.tjeannin.provigen.helper.ProviGenUriBuilder; + +/** + * Created by Dre on 11.04.2016. + * + */ +public class SampleContract { + + public static class Person implements ProviGenBaseContract { + + public static final String TABLE_NAME = "person"; + + @Column(Column.Type.INTEGER) + public static final String AGE = "age"; + + @Column(Column.Type.TEXT) + public static final String NAME = "name"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + + public static final String[] DEFAULT_PROJECTION = new String[] { + _ID, + AGE, + NAME + }; + + public static final String[] JOIN_PROJECTION = new String[] { + _ID, + AGE, + NAME, + ContractUtil.fullName(Specialty.TABLE_NAME, Specialty.NAME) + }; + } + + // not implement ProviGenBaseContract interface (because non-autoincrement primary key) + public static class Specialty { + + public static final String TABLE_NAME = "specialty"; + + @Id(autoincrement = false) + @Column(Column.Type.TEXT) + public static final String _ID = "_id"; + + @Column(Column.Type.INTEGER) + public static final String NAME = "name"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + + public static final String[] DEFAULT_PROJECTION = new String[] { + _ID, + NAME + }; + } + + // not implement ProviGenBaseContract interface (because non-autoincrement composite primary key) + public static class Passport { + + public static final String TABLE_NAME = "passport"; + + @Id + @Column(Column.Type.TEXT) + public static final String SERIES = "series"; + + @Id + @Column(Column.Type.INTEGER) + public static final String NUMBER = "number"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + + public static final String[] DEFAULT_PROJECTION = new String[] { + SERIES, + NUMBER + }; + } + + /** + * Contract class for second database + */ + public static class PersonSecondDb implements ProviGenBaseContract { + + public static final String TABLE_NAME = "persons_second"; + + @Column(Column.Type.INTEGER) + public static final String AGE = "age"; + + @Column(Column.Type.TEXT) + public static final String NAME = "name"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME, SampleContentProvider.SECOND_DB_NAME); // add name of second database + + public static final String[] DEFAULT_PROJECTION = new String[] { + _ID, + AGE, + NAME + }; + } +} From 3f82327298e853f312357dcfb1036857f7c1f67e Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Tue, 12 Apr 2016 12:30:26 +0300 Subject: [PATCH 03/14] Inner join, left outer join and cross join support. Composite primary key support. Upgrade sample app. --- .../tjeannin/provigen/ProviGenProvider.java | 121 ++++++++++++++++-- .../provigen/helper/ProviGenUriBuilder.java | 57 +++++++++ .../tjeannin/provigen/model/JoinEntity.java | 73 +++++++++++ ProviGenSample/res/layout/person_item.xml | 39 +++--- .../provigen/sample/MainActivity.java | 49 +++++-- .../provigen/sample/SampleContract.java | 26 ++-- 6 files changed, 311 insertions(+), 54 deletions(-) create mode 100644 ProviGenLib/src/com/tjeannin/provigen/model/JoinEntity.java diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java index 2d1c494..55eddd2 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java @@ -12,11 +12,15 @@ import android.net.Uri; import android.os.Build; import android.text.TextUtils; +import android.util.Log; +import com.tjeannin.provigen.helper.ContractUtil; import com.tjeannin.provigen.model.Contract; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** * Behaves as a {@link ContentProvider} for the given contract class. @@ -25,6 +29,9 @@ public abstract class ProviGenProvider extends ContentProvider { private static final int ITEM = 1; private static final int ITEM_ID = 2; + private static final int INNER_JOIN = 3; + private static final int LEFT_OUTER_JOIN = 4; + private static final int CROSS_JOIN = 5; private List contracts = new ArrayList<>(); private UriMatcher uriMatcher; @@ -75,6 +82,14 @@ public int conflictAlgorithm() { return SQLiteDatabase.CONFLICT_REPLACE; } + /** + * Get max number of join entities (by default 10) + * @return Max number of join entities + */ + public int maxJoinEntities() { + return 10; + } + @Override public boolean onCreate() { openHelpers = openHelpers(getContext()); @@ -87,12 +102,33 @@ public boolean onCreate() { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); for (Contract contract : contracts) { + StringBuilder innerJoinPathSegment; + StringBuilder leftOuterJoinPathSegment; + StringBuilder crossJoinPathSegment; + if(contract.getDbName() == null) { uriMatcher.addURI(contract.getAuthority(), contract.getTable(), ITEM); uriMatcher.addURI(contract.getAuthority(), contract.getTable() + "/#", ITEM_ID); + + innerJoinPathSegment = new StringBuilder(contract.getTable()).append("/inner_join/*/*"); + leftOuterJoinPathSegment = new StringBuilder(contract.getTable()).append("/left_outer_join/*/*"); + crossJoinPathSegment = new StringBuilder(contract.getTable()).append("/cross_join/*/*"); } else { uriMatcher.addURI(contract.getAuthority(), contract.getDbName() + "/" + contract.getTable(), ITEM); uriMatcher.addURI(contract.getAuthority(), contract.getDbName() + "/" + contract.getTable() + "/#", ITEM_ID); + + innerJoinPathSegment = new StringBuilder(contract.getDbName()).append("/").append(contract.getTable()).append("/inner_join/*/*"); + leftOuterJoinPathSegment = new StringBuilder(contract.getDbName()).append("/").append(contract.getTable()).append("/left_outer_join/*/*"); + crossJoinPathSegment = new StringBuilder(contract.getDbName()).append("/").append(contract.getTable()).append("/cross_join/*/*"); + } + + for(int i = 0; i < maxJoinEntities(); i++) { + uriMatcher.addURI(contract.getAuthority(), innerJoinPathSegment.toString(), INNER_JOIN); + uriMatcher.addURI(contract.getAuthority(), leftOuterJoinPathSegment.toString(), LEFT_OUTER_JOIN); + uriMatcher.addURI(contract.getAuthority(), crossJoinPathSegment.toString(), CROSS_JOIN); + innerJoinPathSegment.append("/*/*"); + leftOuterJoinPathSegment.append("/*/*"); + crossJoinPathSegment.append("/*/*"); } } @@ -123,6 +159,19 @@ public Cursor query(Uri uri, String[] projection, String selection, String[] sel } break; + // JOINs + case INNER_JOIN: + case LEFT_OUTER_JOIN: + case CROSS_JOIN: + Map joinProjectionMap = new LinkedHashMap<>(); + for(String field : projection) { + joinProjectionMap.put(field, field.contains(".") ? field + " AS " + ContractUtil.joinName(field.substring(0, field.indexOf(".")), field.substring(field.indexOf(".") + 1)) : field); + } + queryBuilder.setProjectionMap(joinProjectionMap); + queryBuilder.setTables(parseJoinUri(uri, contract.getDbName() != null)); + cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); + break; + default: throw new IllegalArgumentException("Unknown uri " + uri); } @@ -144,9 +193,8 @@ public Uri insert(Uri uri, ContentValues values) { Uri rowUri = Uri.EMPTY; if (rowId > 0) { rowUri = ContentUris.withAppendedId(uri, rowId); - getContext().getContentResolver().notifyChange(rowUri, null); } - + getContext().getContentResolver().notifyChange(uri, null); return rowUri; default: @@ -163,16 +211,12 @@ public int bulkInsert(Uri uri, ContentValues[] values) { case ITEM: db.beginTransaction(); try { - // вставляем for (ContentValues cv : values) { db.insertWithOnConflict(contract.getTable(), null, cv, conflictAlgorithm()); } db.setTransactionSuccessful(); - Context context = getContext(); - if(context != null) { - context.getContentResolver().notifyChange(uri, null); - } + getContext().getContentResolver().notifyChange(uri, null); } finally { db.endTransaction(); } @@ -219,6 +263,9 @@ public int delete(Uri uri, String selection, String[] selectionArgs) { switch (uriMatcher.match(uri)) { case ITEM: + case INNER_JOIN: + case LEFT_OUTER_JOIN: + case CROSS_JOIN: numberOfRowsAffected = db.delete(contract.getTable(), selection, selectionArgs); break; @@ -244,12 +291,17 @@ public int delete(Uri uri, String selection, String[] selectionArgs) { public String getType(Uri uri) { Contract contract = findMatchingContract(uri); + Log.i(getClass().getSimpleName(), "getType() -> contract.getTable() = " + contract.getTable()); + switch (uriMatcher.match(uri)) { case ITEM: - return "vnd.android.cursor.dir/vdn." + contract.getTable(); + case INNER_JOIN: + case LEFT_OUTER_JOIN: + case CROSS_JOIN: + return "vnd.android.cursor.dir/vdn." + contract.getAuthority() + '.' + contract.getTable(); case ITEM_ID: - return "vnd.android.cursor.item/vdn." + contract.getTable(); + return "vnd.android.cursor.item/vdn." + contract.getAuthority() + '.' + contract.getTable(); default: throw new IllegalArgumentException("Unknown uri " + uri); @@ -314,4 +366,55 @@ private SQLiteDatabase openDatabase(Contract contract, boolean readOnly) { return readOnly ? openHelpers[1].getReadableDatabase() : openHelpers[1].getWritableDatabase(); } } + + /** + * Return join expression as string from URI + * @param uri Join URI + * @param hasDbName True if URI has database name + * @return Join expression + */ + private String parseJoinUri(Uri uri, boolean hasDbName) { + List pathSegments = uri.getPathSegments(); + if(pathSegments.size() > 3) { + StringBuilder builder = new StringBuilder(); + String table1 = pathSegments.get(hasDbName ? 1 : 0); + String joinType; + switch (pathSegments.get(hasDbName ? 2 : 1)) { + case "inner_join": + joinType = " INNER JOIN "; + break; + + case "left_outer_join": + joinType = " LEFT OUTER JOIN "; + break; + + case "cross_join": + joinType = " CROSS JOIN "; + break; + + default: + joinType = ""; + } + builder.append(table1); + + List subList = pathSegments.subList(hasDbName ? 3 : 2, pathSegments.size()); // ex: {"doctor", "doctor_id:id", ...} + for(String tableOrFields : subList) { + // fields separate by ":" + if(tableOrFields.contains(":")) { + builder.append(" ON ") + .append(tableOrFields.substring(0, tableOrFields.indexOf(":"))) + .append(" = ") + .append(tableOrFields.substring(tableOrFields.indexOf(":") + 1)); + } + // table name + else { + builder.append(joinType).append(" ").append(tableOrFields); + } + } + + return builder.toString(); + } else { + throw new IllegalArgumentException("Illegal join URI: " + uri); + } + } } diff --git a/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java b/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java index 0bf1df8..954259c 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java +++ b/ProviGenLib/src/com/tjeannin/provigen/helper/ProviGenUriBuilder.java @@ -2,12 +2,18 @@ import android.net.Uri; +import com.tjeannin.provigen.model.JoinEntity; + /** * Created by Dre on 07.04.2016. * */ public class ProviGenUriBuilder { + public enum JoinType { + INNER_JOIN, LEFT_OUTER_JOIN, CROSS_JOIN + } + /** * Create content URI for contract class * @param authority authority @@ -28,4 +34,55 @@ public static Uri contentUri(String authority, String tableName) { public static Uri contentUri(String authority, String tableName, String dbName) { return Uri.parse("content://" + authority + "/" + (dbName != null ? dbName + "/" : "") + tableName); } + + /** + * Create URI for [inner, left outer, cross] join query + * + * @param joinType Type of join - INNER_JOIN, LEFT_OUTER_JOIN or CROSS_JOIN + * @param uriTable1 URI of left table + * @param joinEntities List of join entities (right table) + * @return URI + */ + public static Uri joinUri(JoinType joinType, Uri uriTable1, JoinEntity ... joinEntities) { + if(joinEntities.length == 0) { + throw new IllegalArgumentException("joinEntities is empty"); + } + + boolean isCrossJoin = false; + String joinPath; + switch (joinType) { + case INNER_JOIN: + joinPath = "inner_join/"; + break; + + case LEFT_OUTER_JOIN: + joinPath = "left_outer_join/"; + break; + + case CROSS_JOIN: + joinPath = "cross_join/"; + isCrossJoin = true; + break; + + default: + joinPath = null; + break; + } + + Uri joinUri = Uri.withAppendedPath(uriTable1, joinPath); + for(int i = 0; i < joinEntities.length; i++) { + String pathSegment; + if(isCrossJoin) { + pathSegment = joinEntities[i].getTableName(); + } else { + pathSegment = joinEntities[i].getTableName() + "/" + joinEntities[i].getLeftField() + ":" + joinEntities[i].getRightField(); + } + if(i < joinEntities.length - 1) { + pathSegment += "/"; + } + joinUri = Uri.withAppendedPath(joinUri, pathSegment); + } + + return joinUri; + } } diff --git a/ProviGenLib/src/com/tjeannin/provigen/model/JoinEntity.java b/ProviGenLib/src/com/tjeannin/provigen/model/JoinEntity.java new file mode 100644 index 0000000..d778259 --- /dev/null +++ b/ProviGenLib/src/com/tjeannin/provigen/model/JoinEntity.java @@ -0,0 +1,73 @@ +package com.tjeannin.provigen.model; + +import android.net.Uri; + +/** + * This model needed to execute a join-query + * + * Created by Dre on 07.04.2016. + */ +public class JoinEntity { + + private Uri contentUri; + private String leftField; + private String rightField; + + public JoinEntity() { } + + /** + * For cross join. + * @param contentUri URI of right join table + */ + public JoinEntity(Uri contentUri) { + this.contentUri = contentUri; + } + + /** + * @param contentUri URI of right join table + * @param leftField Name of left field + * @param rightField Name of right field + */ + public JoinEntity(Uri contentUri, String leftField, String rightField) { + this.contentUri = contentUri; + this.leftField = leftField; + this.rightField = rightField; + } + + public Uri getContentUri() { + return contentUri; + } + + public void setContentUri(Uri contentUri) { + this.contentUri = contentUri; + } + + public String getLeftField() { + return leftField; + } + + public void setLeftField(String leftField) { + this.leftField = leftField; + } + + public String getRightField() { + return rightField; + } + + public void setRightField(String rightField) { + this.rightField = rightField; + } + + public String getTableName() { + return contentUri.getLastPathSegment(); + } + + @Override + public String toString() { + return "JoinEntity{" + + "contentUri=" + contentUri + + ", leftField='" + leftField + '\'' + + ", rightField='" + rightField + '\'' + + '}'; + } +} diff --git a/ProviGenSample/res/layout/person_item.xml b/ProviGenSample/res/layout/person_item.xml index 7953f35..402acf3 100644 --- a/ProviGenSample/res/layout/person_item.xml +++ b/ProviGenSample/res/layout/person_item.xml @@ -1,20 +1,29 @@ - + + android:id="@+id/person_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="90dp" + android:textStyle="bold" + android:padding="10dp" + android:text="Name"/> + android:id="@+id/person_age" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="10dp" + android:text="25"/> - \ No newline at end of file + + + \ No newline at end of file diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java index fcde7c0..9460c12 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java @@ -2,6 +2,7 @@ import android.content.ContentValues; import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager.LoaderCallbacks; @@ -13,10 +14,14 @@ import android.view.View.OnClickListener; import android.widget.ListView; +import com.tjeannin.provigen.helper.ContractUtil; +import com.tjeannin.provigen.helper.ProviGenUriBuilder; +import com.tjeannin.provigen.model.JoinEntity; + public class MainActivity extends FragmentActivity implements LoaderCallbacks, OnClickListener { - private static final String[] NAMES = {"David", "Stephanie", "John", "Anna", "Thomas", "Natalie", "Andrew", "Sofia", "Richard", "Alexandra"}; - private static final String[] SPECIALTIES = {"Android Developer", "iOS Developer", "Backend Developer", "Frontend Developer", "Team Lead", "Project Manager", "CEO"}; + private static final String[] NAMES = {"David", "Stephanie", "John", "Anna", "Thomas", "Natalie", "Andrew", "Sofia", "Richard", "Alexandra", "Matthew", "Grace", "Christian", "Mary"}; + private static final String[] SPECIALTIES = {"Android Developer", "iOS Developer", "Backend Developer", "Frontend Developer", "Web Developer", "Software Tester", "System Administrator", "Graphic Designer", "Project Manager", "Team Lead", "CEO"}; private SimpleCursorAdapter adapter; @Override @@ -26,11 +31,15 @@ public void onCreate(Bundle savedInstanceState) { String[] columns = new String[]{ SampleContract.Person.AGE, - SampleContract.Person.NAME}; + ContractUtil.joinName(SampleContract.Person.TABLE_NAME, SampleContract.Person.NAME), + ContractUtil.joinName(SampleContract.Specialty.TABLE_NAME, SampleContract.Specialty.NAME) + }; int[] ids = { R.id.person_age, - R.id.person_name}; + R.id.person_name, + R.id.person_spec + }; adapter = new SimpleCursorAdapter(this, R.layout.person_item, null, columns, ids, 0); @@ -39,6 +48,7 @@ public void onCreate(Bundle savedInstanceState) { findViewById(R.id.add).setOnClickListener(this); findViewById(R.id.delete).setOnClickListener(this); + initSpecialties(); getSupportLoaderManager().initLoader(0, null, this); } @@ -48,27 +58,38 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } + private void initSpecialties() { + ContentValues[] valuesArray = new ContentValues[SPECIALTIES.length]; + for(int i = 0; i < SPECIALTIES.length; i++) { + ContentValues values = new ContentValues(); + values.put(SampleContract.Specialty.ID, i); + values.put(SampleContract.Specialty.NAME, SPECIALTIES[i]); + valuesArray[i] = values; + } + getContentResolver().bulkInsert(SampleContract.Specialty.CONTENT_URI, valuesArray); + } + private void addPerson() { ContentValues values = new ContentValues(); values.put(SampleContract.Person.AGE, (int) (Math.random() * 40 + 20)); values.put(SampleContract.Person.NAME, NAMES[(int) (Math.random() * NAMES.length)]); + values.put(SampleContract.Person.SPECIALTY_ID, (int) (Math.random() * SPECIALTIES.length)); getContentResolver().insert(SampleContract.Person.CONTENT_URI, values); } - private void addSpecialty() { - ContentValues values = new ContentValues(); - values.put(SampleContract.Specialty._ID, (int) (Math.random() * SPECIALTIES.length)); - values.put(SampleContract.Specialty.NAME, SPECIALTIES[(int) (Math.random() * SPECIALTIES.length)]); - getContentResolver().insert(SampleContract.Person.CONTENT_URI, values); - } - - private void clearAll() { + private void clearPerson() { getContentResolver().delete(SampleContract.Person.CONTENT_URI, null, null); } @Override public Loader onCreateLoader(int id, Bundle args) { - return new CursorLoader(this, SampleContract.Person.CONTENT_URI, null, null, null, null); + Uri innerJoinUri = ProviGenUriBuilder.joinUri( + ProviGenUriBuilder.JoinType.INNER_JOIN, + SampleContract.Person.CONTENT_URI, + new JoinEntity(SampleContract.Specialty.CONTENT_URI, SampleContract.Person.SPECIALTY_ID , SampleContract.Specialty.ID) + ); + + return new CursorLoader(this, innerJoinUri, SampleContract.Person.JOIN_PROJECTION, null, null, SampleContract.Person.DEFAULT_SORT_ORDER); } @Override @@ -90,7 +111,7 @@ public void onClick(View view) { break; case R.id.delete: - clearAll(); + clearPerson(); break; default: diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java index 709c7c0..e2ac237 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java @@ -25,19 +25,20 @@ public static class Person implements ProviGenBaseContract { @Column(Column.Type.TEXT) public static final String NAME = "name"; + @Column(Column.Type.INTEGER) + public static final String SPECIALTY_ID = "specialty_id"; + @ContentUri public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); - public static final String[] DEFAULT_PROJECTION = new String[] { - _ID, - AGE, - NAME - }; + public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; + + public static final String[] DEFAULT_PROJECTION = new String[] { _ID, AGE, NAME }; public static final String[] JOIN_PROJECTION = new String[] { _ID, AGE, - NAME, + ContractUtil.fullName(TABLE_NAME, NAME), ContractUtil.fullName(Specialty.TABLE_NAME, Specialty.NAME) }; } @@ -49,7 +50,7 @@ public static class Specialty { @Id(autoincrement = false) @Column(Column.Type.TEXT) - public static final String _ID = "_id"; + public static final String ID = "id"; @Column(Column.Type.INTEGER) public static final String NAME = "name"; @@ -57,10 +58,7 @@ public static class Specialty { @ContentUri public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); - public static final String[] DEFAULT_PROJECTION = new String[] { - _ID, - NAME - }; + public static final String[] DEFAULT_PROJECTION = new String[] { ID, NAME }; } // not implement ProviGenBaseContract interface (because non-autoincrement composite primary key) @@ -101,10 +99,6 @@ public static class PersonSecondDb implements ProviGenBaseContract { @ContentUri public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME, SampleContentProvider.SECOND_DB_NAME); // add name of second database - public static final String[] DEFAULT_PROJECTION = new String[] { - _ID, - AGE, - NAME - }; + public static final String[] DEFAULT_PROJECTION = new String[] { _ID, AGE, NAME }; } } From ab74e475415dc07fc07ef7ccb352fb84b95b05c1 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Tue, 12 Apr 2016 17:21:11 +0300 Subject: [PATCH 04/14] Bug fixes. README updated. --- ProviGenSample/AndroidManifest.xml | 31 +-- .../provigen/sample/MainActivity.java | 4 +- .../sample/SampleContentProvider.java | 4 +- .../provigen/sample/SampleContract.java | 12 +- README.md | 187 ++++++++++++++++++ 5 files changed, 215 insertions(+), 23 deletions(-) diff --git a/ProviGenSample/AndroidManifest.xml b/ProviGenSample/AndroidManifest.xml index 46f3175..d4110c3 100644 --- a/ProviGenSample/AndroidManifest.xml +++ b/ProviGenSample/AndroidManifest.xml @@ -1,32 +1,33 @@ + package="com.tjeannin.provigen.sample" + android:versionCode="1" + android:versionName="1.0"> + android:minSdkVersion="15" + android:targetSdkVersion="23"/> + android:allowBackup="false" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme"> + + android:name=".MainActivity" + android:label="@string/app_name"> - + android:name="com.tjeannin.provigen.sample.SampleContentProvider" + android:authorities="com.tjeannin.provigen.sample" + android:exported="false"> + \ No newline at end of file diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java index 9460c12..5c4eef0 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/MainActivity.java @@ -31,6 +31,7 @@ public void onCreate(Bundle savedInstanceState) { String[] columns = new String[]{ SampleContract.Person.AGE, + // important for correct obtaining of the columns ContractUtil.joinName(SampleContract.Person.TABLE_NAME, SampleContract.Person.NAME), ContractUtil.joinName(SampleContract.Specialty.TABLE_NAME, SampleContract.Specialty.NAME) }; @@ -83,13 +84,14 @@ private void clearPerson() { @Override public Loader onCreateLoader(int id, Bundle args) { + // create special join URI and execute the query Uri innerJoinUri = ProviGenUriBuilder.joinUri( ProviGenUriBuilder.JoinType.INNER_JOIN, SampleContract.Person.CONTENT_URI, new JoinEntity(SampleContract.Specialty.CONTENT_URI, SampleContract.Person.SPECIALTY_ID , SampleContract.Specialty.ID) ); - return new CursorLoader(this, innerJoinUri, SampleContract.Person.JOIN_PROJECTION, null, null, SampleContract.Person.DEFAULT_SORT_ORDER); + return new CursorLoader(this, innerJoinUri, SampleContract.Person.JOIN_PROJECTION, null, null, null); } @Override diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java index de79883..ee764d3 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java @@ -29,7 +29,7 @@ public class SampleContentProvider extends ProviGenProvider { @Override public SQLiteOpenHelper openHelper(Context context) { - return null; + return null; // you can return null } @Override @@ -42,7 +42,7 @@ public SQLiteOpenHelper[] openHelpers(Context context) { @Override public Class[] contractClasses() { - return null; + return null; // you can return null } @Override diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java index e2ac237..3e6b993 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java @@ -31,13 +31,12 @@ public static class Person implements ProviGenBaseContract { @ContentUri public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); - public static final String DEFAULT_SORT_ORDER = _ID + " DESC"; - public static final String[] DEFAULT_PROJECTION = new String[] { _ID, AGE, NAME }; public static final String[] JOIN_PROJECTION = new String[] { _ID, AGE, + // ContractUtil.fullName() used to avoid ambiguous column name ContractUtil.fullName(TABLE_NAME, NAME), ContractUtil.fullName(Specialty.TABLE_NAME, Specialty.NAME) }; @@ -49,10 +48,10 @@ public static class Specialty { public static final String TABLE_NAME = "specialty"; @Id(autoincrement = false) - @Column(Column.Type.TEXT) + @Column(Column.Type.INTEGER) public static final String ID = "id"; - @Column(Column.Type.INTEGER) + @Column(Column.Type.TEXT) public static final String NAME = "name"; @ContentUri @@ -97,7 +96,10 @@ public static class PersonSecondDb implements ProviGenBaseContract { public static final String NAME = "name"; @ContentUri - public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME, SampleContentProvider.SECOND_DB_NAME); // add name of second database + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri( + SampleContentProvider.AUTHORITY, + TABLE_NAME, + SampleContentProvider.SECOND_DB_NAME); // name of second database public static final String[] DEFAULT_PROJECTION = new String[] { _ID, AGE, NAME }; } diff --git a/README.md b/README.md index 4504eea..77bc0df 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,193 @@ new TableBuilder(MyContract.class) .createTable(database); ``` +### Primary key + +Autoincrement or non-autoincrement primary key. + +```java +@Id(autoincrement = true) +@Column(Column.Type.INTEGER) +public static final String ID = "id"; +``` + +Composite primary key support. + +```java +public class Passport { + + public static final String TABLE_NAME = "passport"; + + @Id + @Column(Column.Type.TEXT) + public static final String SERIES = "series"; + + @Id + @Column(Column.Type.INTEGER) + public static final String NUMBER = "number"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + +} +``` + +### Multiple databases support + +Add a name of the second database in your ContractClass. + +```java +public class PersonSecondDb implements ProviGenBaseContract { + + public static final String TABLE_NAME = "persons_second"; + + @Column(Column.Type.INTEGER) + public static final String AGE = "age"; + + @Column(Column.Type.TEXT) + public static final String NAME = "name"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri( + SampleContentProvider.AUTHORITY, + TABLE_NAME, + SampleContentProvider.SECOND_DB_NAME); // name of second database +} +``` + +Override openHelpers(Context context) and contractClassesMultipleDb() methods in your content provider. + +```java +public class SampleContentProvider extends ProviGenProvider { + + public static final String AUTHORITY = "com.tjeannin.provigen.sample"; + + public static final String DB_NAME = "ProviGenDatabase"; + public static final int DB_VERSION = 1; + + public static final String SECOND_DB_NAME = "ProviGenDatabaseSecond"; + public static final int SECOND_DB_VERSION = 1; + + private static final Class[] CONTRACTS = new Class[] { + SampleContract.Person.class, + SampleContract.Specialty.class, + SampleContract.Passport.class + }; + + private static final Class[] CONTRACTS_SECOND = new Class[] { + SampleContract.PersonSecondDb.class + }; + + @Override + public SQLiteOpenHelper openHelper(Context context) { + return null; // you can return null + } + + @Override + public SQLiteOpenHelper[] openHelpers(Context context) { + return new SQLiteOpenHelper[] { + new ProviGenOpenHelper(getContext(), DB_NAME, null, DB_VERSION, CONTRACTS), // first db + new ProviGenOpenHelper(getContext(), SECOND_DB_NAME, null, SECOND_DB_VERSION, CONTRACTS_SECOND) // second db + }; + } + + @Override + public Class[] contractClasses() { + return null; // you can return null + } + + @Override + public Class[][] contractClassesMultipleDb() { + return new Class[][] {CONTRACTS, CONTRACTS_SECOND}; + } +} +``` + +### Joins support + +Inner join, left outer join and cross join support. +Your ContractClasses. + +```java +public static class Person implements ProviGenBaseContract { + + public static final String TABLE_NAME = "person"; + + @Column(Column.Type.INTEGER) + public static final String AGE = "age"; + + @Column(Column.Type.TEXT) + public static final String NAME = "name"; + + @Column(Column.Type.INTEGER) + public static final String SPECIALTY_ID = "specialty_id"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + + public static final String[] DEFAULT_PROJECTION = new String[] { _ID, AGE, NAME }; + + // projection for join-query + public static final String[] JOIN_PROJECTION = new String[] { + _ID, + AGE, + // ContractUtil.fullName() used to avoid ambiguous column name + ContractUtil.fullName(TABLE_NAME, NAME), + ContractUtil.fullName(Specialty.TABLE_NAME, Specialty.NAME) + }; +} + +public static class Specialty { + + public static final String TABLE_NAME = "specialty"; + + @Id + @Column(Column.Type.INTEGER) + public static final String ID = "id"; + + @Column(Column.Type.TEXT) + public static final String NAME = "name"; + + @ContentUri + public static final Uri CONTENT_URI = ProviGenUriBuilder.contentUri(SampleContentProvider.AUTHORITY, TABLE_NAME); + + public static final String[] DEFAULT_PROJECTION = new String[] { ID, NAME }; +} +``` + +Use the method ContractUtil.joinName() for correct obtaining of the columns. + +```java +String[] columns = new String[]{ + SampleContract.Person.AGE, + // important for correct obtaining of the columns + ContractUtil.joinName(SampleContract.Person.TABLE_NAME, SampleContract.Person.NAME), + ContractUtil.joinName(SampleContract.Specialty.TABLE_NAME, SampleContract.Specialty.NAME) +}; + +int[] ids = { + R.id.person_age, + R.id.person_name, + R.id.person_spec +}; + +SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_item, null, columns, ids, 0); +``` + +Create special join URI using the method ProviGenUriBuilder.joinUri() and execute the query. + +```java +@Override +public Loader onCreateLoader(int id, Bundle args) { + Uri innerJoinUri = ProviGenUriBuilder.joinUri( + ProviGenUriBuilder.JoinType.INNER_JOIN, // inner, left outer or cross join + SampleContract.Person.CONTENT_URI, + new JoinEntity(SampleContract.Specialty.CONTENT_URI, SampleContract.Person.SPECIALTY_ID , SampleContract.Specialty.ID)); + + return new CursorLoader(this, innerJoinUri, SampleContract.Person.JOIN_PROJECTION, null, null, null); +} +``` + ## License This content is released under the MIT License. From 9fa3add76fa5e98cac4b4e70f7d90bd2576cfefa Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Tue, 12 Apr 2016 19:16:36 +0300 Subject: [PATCH 05/14] bug fixes : SimpleContentProviderTest.testGetMimeType() --- .../provigen/test/basis/SimpleContentProvider.java | 8 ++++++-- .../provigen/test/basis/SimpleContentProviderTest.java | 6 ++++-- .../test/multiple/MultipleContractContentProvider.java | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProvider.java b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProvider.java index dd1918a..5ad7830 100644 --- a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProvider.java +++ b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProvider.java @@ -24,15 +24,19 @@ public Class[] contractClasses() { public interface ContractOne extends ProviGenBaseContract { + String TABLE_NAME = "table_name_simple"; + @Column(Type.INTEGER) String MY_INT = "int"; @ContentUri - Uri CONTENT_URI = Uri.parse("content://com.test.simple/table_name_simple"); + Uri CONTENT_URI = Uri.parse("content://com.test.simple/" + TABLE_NAME); } public interface ContractTwo extends ProviGenBaseContract { + String TABLE_NAME = "table_name_simple"; + @Column(Type.INTEGER) String MY_INT = "int"; @@ -43,6 +47,6 @@ public interface ContractTwo extends ProviGenBaseContract { String MY_REAL = "real"; @ContentUri - Uri CONTENT_URI = Uri.parse("content://com.test.simple/table_name_simple"); + Uri CONTENT_URI = Uri.parse("content://com.test.simple/" + TABLE_NAME); } } diff --git a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java index 83abd37..c6bb712 100644 --- a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java +++ b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java @@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.test.mock.MockContentResolver; + import com.tjeannin.provigen.test.ExtendedProviderTestCase; import com.tjeannin.provigen.test.basis.SimpleContentProvider.ContractOne; import com.tjeannin.provigen.test.basis.SimpleContentProvider.ContractTwo; @@ -16,10 +17,11 @@ public class SimpleContentProviderTest extends ExtendedProviderTestCase { + private static final String PROVIDER_AUTHORITY = "com.test.simple"; private MockContentResolver contentResolver; public SimpleContentProviderTest() { - super(SimpleContentProvider.class, "com.test.simple"); + super(SimpleContentProvider.class, PROVIDER_AUTHORITY); } @Override @@ -167,6 +169,6 @@ private List getTableFields(final Uri contentUri) { public void testGetMimeType() { final String mimeType = getProvider().getType(ContractOne.CONTENT_URI); - assertEquals("vnd.android.cursor.dir/vdn.table_name_simple", mimeType); + assertEquals("vnd.android.cursor.dir/vdn." + PROVIDER_AUTHORITY + '.' + ContractOne.TABLE_NAME, mimeType); } } diff --git a/ProviGenTests/src/com/tjeannin/provigen/test/multiple/MultipleContractContentProvider.java b/ProviGenTests/src/com/tjeannin/provigen/test/multiple/MultipleContractContentProvider.java index cb9e0e9..d189e50 100644 --- a/ProviGenTests/src/com/tjeannin/provigen/test/multiple/MultipleContractContentProvider.java +++ b/ProviGenTests/src/com/tjeannin/provigen/test/multiple/MultipleContractContentProvider.java @@ -26,11 +26,13 @@ public Class[] contractClasses() { public static interface ContractOne extends ProviGenBaseContract { + String TABLE_NAME = "table_name_simple"; + @Column(Type.INTEGER) public static final String MY_INT = "int"; @ContentUri - public static final Uri CONTENT_URI = Uri.parse("content://com.test.simple/table_name_simple"); + public static final Uri CONTENT_URI = Uri.parse("content://com.test.simple/" + TABLE_NAME); } public static interface ContractTwo extends ProviGenBaseContract { From 49c697a667bb5aa64cb81a0d270f1a5028a5dc51 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 11:34:21 +0300 Subject: [PATCH 06/14] ProviGenProvider.conflictAlgorithm() return SQLiteDatabase.CONFLICT_NONE by default --- .../tjeannin/provigen/ProviGenProvider.java | 18 ++++++++++++++---- .../provigen/sample/SampleContentProvider.java | 6 ++++++ .../test/ExtendedProviderTestCase.java | 12 ++++++++---- .../test/basis/SimpleContentProviderTest.java | 10 +++++----- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java index 55eddd2..fd8e1b4 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java @@ -76,10 +76,10 @@ public Class[][] contractClassesMultipleDb() { } /** - * Override for insert conflict resolver. By default return SQLiteDatabase.CONFLICT_REPLACE. + * Override for insert conflict resolver. By default return SQLiteDatabase.CONFLICT_NONE. */ public int conflictAlgorithm() { - return SQLiteDatabase.CONFLICT_REPLACE; + return SQLiteDatabase.CONFLICT_NONE; } /** @@ -189,7 +189,13 @@ public Uri insert(Uri uri, ContentValues values) { switch (uriMatcher.match(uri)) { case ITEM: - long rowId = db.insertWithOnConflict(contract.getTable(), null, values, conflictAlgorithm()); + long rowId; + if(conflictAlgorithm() == SQLiteDatabase.CONFLICT_NONE) { + rowId = db.insert(contract.getTable(), null, values); + } else { + rowId = db.insertWithOnConflict(contract.getTable(), null, values, conflictAlgorithm()); + } + Uri rowUri = Uri.EMPTY; if (rowId > 0) { rowUri = ContentUris.withAppendedId(uri, rowId); @@ -212,7 +218,11 @@ public int bulkInsert(Uri uri, ContentValues[] values) { db.beginTransaction(); try { for (ContentValues cv : values) { - db.insertWithOnConflict(contract.getTable(), null, cv, conflictAlgorithm()); + if(conflictAlgorithm() == SQLiteDatabase.CONFLICT_NONE) { + db.insert(contract.getTable(), null, cv); + } else { + db.insertWithOnConflict(contract.getTable(), null, cv, conflictAlgorithm()); + } } db.setTransactionSuccessful(); diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java index ee764d3..ae33243 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContentProvider.java @@ -1,6 +1,7 @@ package com.tjeannin.provigen.sample; import android.content.Context; +import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import com.tjeannin.provigen.ProviGenOpenHelper; @@ -49,4 +50,9 @@ public Class[] contractClasses() { public Class[][] contractClassesMultipleDb() { return new Class[][] {CONTRACTS, CONTRACTS_SECOND}; } + + @Override + public int conflictAlgorithm() { + return SQLiteDatabase.CONFLICT_REPLACE; + } } diff --git a/ProviGenTests/src/com/tjeannin/provigen/test/ExtendedProviderTestCase.java b/ProviGenTests/src/com/tjeannin/provigen/test/ExtendedProviderTestCase.java index b93a09e..60fd086 100644 --- a/ProviGenTests/src/com/tjeannin/provigen/test/ExtendedProviderTestCase.java +++ b/ProviGenTests/src/com/tjeannin/provigen/test/ExtendedProviderTestCase.java @@ -2,8 +2,10 @@ import android.content.ContentValues; import android.database.Cursor; +import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.test.ProviderTestCase2; + import com.tjeannin.provigen.ProviGenOpenHelper; import com.tjeannin.provigen.ProviGenProvider; import com.tjeannin.provigen.annotation.Column; @@ -61,11 +63,13 @@ protected void resetContractClasses(Class[] contractClasses) { getSuperClassField(getProvider(), "contracts").set(getProvider(), contracts); // Replace the open helper. - Field openHelperField = getSuperClassField(getProvider(), "openHelper"); - ProviGenOpenHelper openHelper = (ProviGenOpenHelper) openHelperField.get(getProvider()); + Field openHelpersField = getSuperClassField(getProvider(), "openHelpers"); + ProviGenOpenHelper openHelper = (ProviGenOpenHelper) ((SQLiteOpenHelper[]) openHelpersField.get(getProvider()))[0]; int version = getSuperClassField(openHelper, "mNewVersion").getInt(openHelper); - openHelperField.set(getProvider(), new ProviGenOpenHelper( - getProvider().getContext(), "ProviGenDatabase", null, version + 1, contractClasses)); + openHelpersField.set( + getProvider(), + new SQLiteOpenHelper[] {new ProviGenOpenHelper(getProvider().getContext(), "ProviGenDatabase", null, version + 1, contractClasses)} + ); } catch (IllegalAccessException e) { e.printStackTrace(); diff --git a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java index c6bb712..d883b81 100644 --- a/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java +++ b/ProviGenTests/src/com/tjeannin/provigen/test/basis/SimpleContentProviderTest.java @@ -43,16 +43,16 @@ public void testInsertQueryUpdateDelete() { // Update final ContentValues contentValues = new ContentValues(2); contentValues.put(ContractOne.MY_INT, 15); - contentResolver.update(ContractOne.CONTENT_URI, contentValues, "", null); + contentResolver.update(ContractOne.CONTENT_URI, contentValues, null, null); // Query - final Cursor cursor = contentResolver.query(ContractOne.CONTENT_URI, null, "", null, ""); + final Cursor cursor = contentResolver.query(ContractOne.CONTENT_URI, null, null, null, null); cursor.moveToFirst(); assertEquals(cursor.getInt(cursor.getColumnIndex(ContractOne.MY_INT)), 15); cursor.close(); // Delete - contentResolver.delete(Uri.withAppendedPath(ContractOne.CONTENT_URI, String.valueOf(1)), "", null); + contentResolver.delete(Uri.withAppendedPath(ContractOne.CONTENT_URI, String.valueOf(1)), null, null); assertEquals(0, getRowCount(ContractOne.CONTENT_URI)); } @@ -101,7 +101,7 @@ public void testAutoIncrement() { contentResolver.insert(ContractOne.CONTENT_URI, getContentValues(ContractOne.class)); contentResolver.insert(ContractOne.CONTENT_URI, getContentValues(ContractOne.class)); - contentResolver.delete(Uri.withAppendedPath(ContractOne.CONTENT_URI, String.valueOf(3)), "", null); + contentResolver.delete(Uri.withAppendedPath(ContractOne.CONTENT_URI, String.valueOf(3)), null, null); contentResolver.insert(ContractOne.CONTENT_URI, getContentValues(ContractOne.class)); @@ -157,7 +157,7 @@ private List getTableFields(final Uri contentUri) { final SQLiteDatabase sqLiteDatabase = getMockContext().openOrCreateDatabase("ProviGenDatabase", Context.MODE_PRIVATE, null); final String tableName = contentUri.getLastPathSegment(); final Cursor rawQuery = sqLiteDatabase.rawQuery("PRAGMA table_info(" + tableName + ')', null); - final List result = new ArrayList(rawQuery.getCount()); + final List result = new ArrayList<>(rawQuery.getCount()); if (rawQuery.moveToFirst()) { do { result.add(rawQuery.getString(rawQuery.getColumnIndex("name"))); From a3470fa25e791f4f0bbf2a828d3ad7317bc4e8c1 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 12:14:54 +0300 Subject: [PATCH 07/14] .travis.yml updated --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 447d4fd..96e7f1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,22 +9,25 @@ env: - ANDROID_TARGET=android-18 - ANDROID_TARGET=android-19 - ANDROID_TARGET=android-21 + - ANDROID_TARGET=android-23 android: components: - - build-tools-19.1.0 + - build-tools-23.0.3 - android-15 - android-16 - android-17 - android-18 - android-19 - android-21 + - android-23 - sys-img-armeabi-v7a-android-15 - sys-img-armeabi-v7a-android-16 - sys-img-armeabi-v7a-android-17 - sys-img-armeabi-v7a-android-18 - sys-img-armeabi-v7a-android-19 - sys-img-armeabi-v7a-android-21 + - sys-img-armeabi-v7a-android-23 - extra-android-support - extra-android-m2repository - extra-google-m2repository From 2e86031f95634071291491065906a31433046e6c Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 12:38:56 +0300 Subject: [PATCH 08/14] .travis.yml updated [2] --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 96e7f1a..905e18d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ env: android: components: + - platform-tools + - tools - build-tools-23.0.3 - android-15 - android-16 From e0a448d7b40384ee238d91ec9188d77561cd30d3 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 13:31:58 +0300 Subject: [PATCH 09/14] .travis.yml updated [3] --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 905e18d..5a869ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,6 @@ android: - sys-img-armeabi-v7a-android-18 - sys-img-armeabi-v7a-android-19 - sys-img-armeabi-v7a-android-21 - - sys-img-armeabi-v7a-android-23 - extra-android-support - extra-android-m2repository - extra-google-m2repository From 970f7e3205b932b4595fc556a5dc39743089be97 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 13:40:59 +0300 Subject: [PATCH 10/14] .travis.yml updated [4] --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a869ed..2079b06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ env: - ANDROID_TARGET=android-18 - ANDROID_TARGET=android-19 - ANDROID_TARGET=android-21 - - ANDROID_TARGET=android-23 android: components: @@ -22,7 +21,6 @@ android: - android-18 - android-19 - android-21 - - android-23 - sys-img-armeabi-v7a-android-15 - sys-img-armeabi-v7a-android-16 - sys-img-armeabi-v7a-android-17 From 4acb96c13df83d218b855c547f25200681bdd753 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Wed, 13 Apr 2016 13:46:34 +0300 Subject: [PATCH 11/14] .travis.yml updated [5] --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2079b06..e1c8002 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ android: - android-18 - android-19 - android-21 + - android-23 - sys-img-armeabi-v7a-android-15 - sys-img-armeabi-v7a-android-16 - sys-img-armeabi-v7a-android-17 From 513d0668f8f06f39f39b26d1ad5d54b2775f02ef Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Thu, 14 Apr 2016 18:52:15 +0300 Subject: [PATCH 12/14] Foreign key support added. Sample and readme updated. --- .../provigen/annotation/ForeignKey.java | 19 ++++++++ .../provigen/helper/TableBuilder.java | 31 ++++++++----- .../com/tjeannin/provigen/model/Contract.java | 24 +++++++++- .../provigen/model/ForeignKeyConstraint.java | 44 +++++++++++++++++++ .../provigen/sample/SampleContract.java | 2 + README.md | 10 ++++- 6 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 ProviGenLib/src/com/tjeannin/provigen/annotation/ForeignKey.java create mode 100644 ProviGenLib/src/com/tjeannin/provigen/model/ForeignKeyConstraint.java diff --git a/ProviGenLib/src/com/tjeannin/provigen/annotation/ForeignKey.java b/ProviGenLib/src/com/tjeannin/provigen/annotation/ForeignKey.java new file mode 100644 index 0000000..2d512b5 --- /dev/null +++ b/ProviGenLib/src/com/tjeannin/provigen/annotation/ForeignKey.java @@ -0,0 +1,19 @@ +package com.tjeannin.provigen.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Identifies a field of a contract class that should be used as a foreign key in the database.
+ * This annotation should be used alongside with a {@link Column} annotation.

+ * table - The name of the table on which the foreign key is referenced.
+ * column - The name of the column on which the foreign key is referenced.
+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ForeignKey { + String table(); + String column(); +} diff --git a/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java b/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java index f02839a..d2661e5 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java +++ b/ProviGenLib/src/com/tjeannin/provigen/helper/TableBuilder.java @@ -6,6 +6,7 @@ import com.tjeannin.provigen.model.Constraint; import com.tjeannin.provigen.model.Contract; import com.tjeannin.provigen.model.ContractField; +import com.tjeannin.provigen.model.ForeignKeyConstraint; import java.util.ArrayList; import java.util.List; @@ -63,31 +64,41 @@ public String getSQL() { for (ContractField field : contract.getFields()) { builder.append(" ").append(field.name).append(" ").append(field.type); + boolean isSinglePrimaryKey = false; // single PRIMARY KEY if(contract.getIdFields().size() == 1) { if (field.name.equals(contract.getIdFields().get(0))) { - builder.append(" PRIMARY KEY "); + isSinglePrimaryKey = true; + builder.append(" NOT NULL PRIMARY KEY "); if (field.type.equals(Column.Type.INTEGER)) { if(contract.isAutoincrement(field.name)) { - builder.append(" AUTOINCREMENT "); + builder.append(" AUTOINCREMENT "); // 123 456 789 } } } } - for (Constraint constraint : constraints) { - if (constraint.targetColumn.equals(field.name)) { - builder.append(" ").append(constraint.type).append(" ON CONFLICT ").append(constraint.conflictClause); + if(!isSinglePrimaryKey) { + for (Constraint constraint : constraints) { + if (constraint.targetColumn.equals(field.name)) { + builder.append(" ").append(constraint.type).append(" ON CONFLICT ").append(constraint.conflictClause); + } } } builder.append(", "); } - // composite PRIMARY KEY + // foreign keys + for(ForeignKeyConstraint foreignKey : contract.getForeignKeys()) { + builder.append("FOREIGN KEY (").append(foreignKey.getColumn()).append(") references ") + .append(foreignKey.getTableReferenced()).append("(").append(foreignKey.getColumnReferenced()).append("), "); + } + + // composite primary key if(contract.getIdFields().size() > 1) { - builder.append(" PRIMARY KEY ( "); + builder.append("PRIMARY KEY ("); // пробегаем по полям, помеченными как @Id for(int i = 0; i < contract.getIdFields().size(); i++) { builder.append(contract.getIdFields().get(i)); @@ -95,13 +106,13 @@ public String getSQL() { builder.append(", "); } } - builder.append(" ) "); + builder.append(")"); } else { // delete ',' in the end builder.deleteCharAt(builder.length() - 2); } - builder.append(" ) "); + builder.append(")"); return builder.toString(); } @@ -114,4 +125,4 @@ public String getSQL() { public void createTable(SQLiteDatabase database) { database.execSQL(getSQL()); } -} +} \ No newline at end of file diff --git a/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java b/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java index 1c43bf5..7137ad7 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java +++ b/ProviGenLib/src/com/tjeannin/provigen/model/Contract.java @@ -3,6 +3,7 @@ import android.net.Uri; import com.tjeannin.provigen.annotation.Column; import com.tjeannin.provigen.annotation.ContentUri; +import com.tjeannin.provigen.annotation.ForeignKey; import com.tjeannin.provigen.annotation.Id; import java.lang.reflect.Field; @@ -18,12 +19,14 @@ public class Contract { private String tableName; private List contractFields; private Map idFieldMap; + private List foreignKeys; @SuppressWarnings({"rawtypes", "unchecked"}) public Contract(Class contractClass) { idFieldMap = new HashMap<>(); contractFields = new ArrayList<>(); + foreignKeys = new ArrayList<>(); Field[] fields = contractClass.getFields(); for (Field field : fields) { @@ -45,7 +48,7 @@ public Contract(Class contractClass) { } } - Id id = field.getAnnotation(Id.class); + Id id = field.getAnnotation(Id.class); if (id != null) { try { String idField = (String) field.get(null); @@ -55,6 +58,21 @@ public Contract(Class contractClass) { } } + ForeignKey foreignKey = field.getAnnotation(ForeignKey.class); + if(foreignKey != null) { + try { + ForeignKeyConstraint foreignKeyConstraint = null; + if(foreignKey.table() != null && foreignKey.column() != null) { + foreignKeyConstraint = new ForeignKeyConstraint((String) field.get(null), foreignKey.table(), foreignKey.column()); + } + if(foreignKeyConstraint != null) { + foreignKeys.add(foreignKeyConstraint); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + Column column = field.getAnnotation(Column.class); if (column != null) { try { @@ -89,4 +107,8 @@ public List getFields() { public boolean isAutoincrement(String field) { return idFieldMap.get(field); } + + public List getForeignKeys() { + return foreignKeys; + } } diff --git a/ProviGenLib/src/com/tjeannin/provigen/model/ForeignKeyConstraint.java b/ProviGenLib/src/com/tjeannin/provigen/model/ForeignKeyConstraint.java new file mode 100644 index 0000000..04d04ed --- /dev/null +++ b/ProviGenLib/src/com/tjeannin/provigen/model/ForeignKeyConstraint.java @@ -0,0 +1,44 @@ +package com.tjeannin.provigen.model; + +/** + * Created by Dre on 14.04.2016. + * + */ +public class ForeignKeyConstraint { + + private String column; + private String tableReferenced; + private String columnReferenced; + + public ForeignKeyConstraint() {} + + public ForeignKeyConstraint(String column, String tableReferenced, String columnReferenced) { + this.column = column; + this.tableReferenced = tableReferenced; + this.columnReferenced = columnReferenced; + } + + public String getColumn() { + return column; + } + + public void setColumn(String column) { + this.column = column; + } + + public String getTableReferenced() { + return tableReferenced; + } + + public void setTableReferenced(String tableReferenced) { + this.tableReferenced = tableReferenced; + } + + public String getColumnReferenced() { + return columnReferenced; + } + + public void setColumnReferenced(String columnReferenced) { + this.columnReferenced = columnReferenced; + } +} diff --git a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java index 3e6b993..6d05c91 100644 --- a/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java +++ b/ProviGenSample/src/com/tjeannin/provigen/sample/SampleContract.java @@ -5,6 +5,7 @@ import com.tjeannin.provigen.ProviGenBaseContract; import com.tjeannin.provigen.annotation.Column; import com.tjeannin.provigen.annotation.ContentUri; +import com.tjeannin.provigen.annotation.ForeignKey; import com.tjeannin.provigen.annotation.Id; import com.tjeannin.provigen.helper.ContractUtil; import com.tjeannin.provigen.helper.ProviGenUriBuilder; @@ -25,6 +26,7 @@ public static class Person implements ProviGenBaseContract { @Column(Column.Type.TEXT) public static final String NAME = "name"; + @ForeignKey(table = Specialty.TABLE_NAME, column = Specialty.ID) @Column(Column.Type.INTEGER) public static final String SPECIALTY_ID = "specialty_id"; diff --git a/README.md b/README.md index 77bc0df..d117304 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ new TableBuilder(MyContract.class) .createTable(database); ``` -### Primary key +### Primary key and foreign key Autoincrement or non-autoincrement primary key. @@ -163,6 +163,14 @@ public class Passport { } ``` +Foreign key support. + +```java +@ForeignKey(table = Specialty.TABLE_NAME, column = Specialty.ID) +@Column(Column.Type.INTEGER) +public static final String SPECIALTY_ID = "specialty_id"; +``` + ### Multiple databases support Add a name of the second database in your ContractClass. From 90831fce29d05f336e731f9fb211c197326b9f86 Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Mon, 18 Apr 2016 13:49:56 +0300 Subject: [PATCH 13/14] gradle task generateSourcesJar added --- ProviGenLib/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ProviGenLib/build.gradle b/ProviGenLib/build.gradle index 34867f5..7b9651a 100644 --- a/ProviGenLib/build.gradle +++ b/ProviGenLib/build.gradle @@ -9,4 +9,9 @@ sourceSets { srcDir 'src' } } +} + +task generateSourcesJar(type: Jar) { + from sourceSets.main.java.srcDirs + classifier 'sources' } \ No newline at end of file From 37640d1d8ef6eec33c9d656113c3a1f9499d87ef Mon Sep 17 00:00:00 2001 From: sidorovnin Date: Thu, 21 Apr 2016 10:08:23 +0300 Subject: [PATCH 14/14] insertNotifyChange(), updateNotifyChange(), deleteNotifyChange() added --- .../tjeannin/provigen/ProviGenProvider.java | 52 +++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java index fd8e1b4..379dca8 100644 --- a/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java +++ b/ProviGenLib/src/com/tjeannin/provigen/ProviGenProvider.java @@ -1,10 +1,6 @@ package com.tjeannin.provigen; -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.UriMatcher; +import android.content.*; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -12,8 +8,6 @@ import android.net.Uri; import android.os.Build; import android.text.TextUtils; -import android.util.Log; - import com.tjeannin.provigen.helper.ContractUtil; import com.tjeannin.provigen.model.Contract; @@ -82,6 +76,30 @@ public int conflictAlgorithm() { return SQLiteDatabase.CONFLICT_NONE; } + /** + * Whether to call the getContentResolver().notifyChange(uri, null) method after insert. + * @return true or false + */ + public boolean insertNotifyChange() { + return true; + } + + /** + * Whether to call the getContentResolver().notifyChange(uri, null) method after update. + * @return true or false + */ + public boolean updateNotifyChange() { + return true; + } + + /** + * Whether to call the getContentResolver().notifyChange(uri, null) method after delete. + * @return true or false + */ + public boolean deleteNotifyChange() { + return true; + } + /** * Get max number of join entities (by default 10) * @return Max number of join entities @@ -200,7 +218,9 @@ public Uri insert(Uri uri, ContentValues values) { if (rowId > 0) { rowUri = ContentUris.withAppendedId(uri, rowId); } - getContext().getContentResolver().notifyChange(uri, null); + if(insertNotifyChange()) { + getContext().getContentResolver().notifyChange(uri, null); + } return rowUri; default: @@ -226,7 +246,9 @@ public int bulkInsert(Uri uri, ContentValues[] values) { } db.setTransactionSuccessful(); - getContext().getContentResolver().notifyChange(uri, null); + if(insertNotifyChange()) { + getContext().getContentResolver().notifyChange(uri, null); + } } finally { db.endTransaction(); } @@ -261,7 +283,9 @@ public int update(Uri uri, ContentValues values, String selection, String[] sele default: throw new IllegalArgumentException("Unknown uri " + uri); } - getContext().getContentResolver().notifyChange(uri, null); + if(updateNotifyChange()) { + getContext().getContentResolver().notifyChange(uri, null); + } return numberOfRowsAffected; } @@ -292,17 +316,15 @@ public int delete(Uri uri, String selection, String[] selectionArgs) { default: throw new IllegalArgumentException("Unknown uri " + uri); } - - getContext().getContentResolver().notifyChange(uri, null); + if(deleteNotifyChange()) { + getContext().getContentResolver().notifyChange(uri, null); + } return numberOfRowsAffected; } @Override public String getType(Uri uri) { Contract contract = findMatchingContract(uri); - - Log.i(getClass().getSimpleName(), "getType() -> contract.getTable() = " + contract.getTable()); - switch (uriMatcher.match(uri)) { case ITEM: case INNER_JOIN: