diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt index 7584ac698..08e173095 100644 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt +++ b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyExtensions.kt @@ -2,7 +2,6 @@ package com.raizlabs.android.dbflow.kotlinextensions import com.raizlabs.android.dbflow.sql.language.property.* import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable -import com.raizlabs.android.dbflow.structure.Model /** * Description: Provides some very nice Property class extensions. diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt index ab56b20fa..a46cf68be 100644 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt +++ b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/PropertyMethodExtensions.kt @@ -9,7 +9,6 @@ import com.raizlabs.android.dbflow.structure.Model /** * Description: Provides property methods in via infix functions. */ - infix fun Property.eq(value: T) = this.eq(value) infix fun Property.`is`(value: T) = this.`is`(value) diff --git a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt index 4675c72ae..303a4be26 100644 --- a/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt +++ b/dbflow-kotlinextensions/src/main/java/com/raizlabs/android/dbflow/kotlinextensions/QueryExtensions.kt @@ -131,7 +131,6 @@ fun update(modelClass: KClass): Update = SQLite.update(modelClas infix fun Update.set(sqlCondition: SQLCondition) = set(sqlCondition) - // delete inline fun delete() = SQLite.delete(T::class.java) diff --git a/dbflow-processor/build.gradle b/dbflow-processor/build.gradle index c3d68a184..6121a172f 100644 --- a/dbflow-processor/build.gradle +++ b/dbflow-processor/build.gradle @@ -13,7 +13,6 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" testCompile 'junit:junit:4.12' - } apply from: '../java-artifacts.gradle' diff --git a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/DBFlowProcessor.java b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/DBFlowProcessor.java index 1adeb3694..8c5f457e3 100644 --- a/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/DBFlowProcessor.java +++ b/dbflow-processor/src/main/java/com/raizlabs/android/dbflow/processor/DBFlowProcessor.java @@ -29,6 +29,14 @@ public class DBFlowProcessor extends AbstractProcessor { private ProcessorManager manager; + @Override + public Set getSupportedOptions() { + Set supportedOptions = new LinkedHashSet<>(); + supportedOptions.add("targetModuleName"); + + return supportedOptions; + } + /** * If the processor class is annotated with {@link * javax.annotation.processing.SupportedAnnotationTypes}, return an unmodifiable set with the diff --git a/dbflow/build.gradle b/dbflow/build.gradle index 11c6d72bd..64f47bc4a 100644 --- a/dbflow/build.gradle +++ b/dbflow/build.gradle @@ -26,8 +26,6 @@ android { dependencies { compile project("${dbflow_project_prefix}dbflow-core") compile "com.android.support:support-annotations:25.0.0" - - } apply from: '../android-artifacts.gradle' diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorAdapter.java new file mode 100644 index 000000000..4c056837c --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorAdapter.java @@ -0,0 +1,39 @@ +package com.raizlabs.android.dbflow.list; + +import android.annotation.TargetApi; +import android.content.Context; +import android.database.Cursor; +import android.widget.CursorAdapter; + +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.ModelAdapter; + +/** + * Specialization of CursorAdapter for DBFLow models. The getItem() method + * returns a model element instead of a Cursor object. + * + * @param + */ +public abstract class FlowCursorAdapter extends CursorAdapter { + private final ModelAdapter modelAdapter; + + public FlowCursorAdapter(Context context, Class modelClass, Cursor c, boolean autoRequery) { + super(context, c, autoRequery); + + this.modelAdapter = FlowManager.getModelAdapter(modelClass); + } + + @TargetApi(11) + public FlowCursorAdapter(Context context, Class modelClass, Cursor c, int flags) { + super(context, c, flags); + + this.modelAdapter = FlowManager.getModelAdapter(modelClass); + } + + @Override + public TModel getItem(int position) { + Cursor cursor = (Cursor) super.getItem(position); + return cursor != null ? this.modelAdapter.loadFromCursor(cursor) : null; + } +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorLoader.java new file mode 100644 index 000000000..25b2f90d9 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowCursorLoader.java @@ -0,0 +1,198 @@ +package com.raizlabs.android.dbflow.list; + +import android.annotation.TargetApi; +import android.content.AsyncTaskLoader; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.raizlabs.android.dbflow.runtime.FlowContentObserver; +import com.raizlabs.android.dbflow.sql.language.SQLCondition; +import com.raizlabs.android.dbflow.sql.queriable.Queriable; +import com.raizlabs.android.dbflow.structure.BaseModel; +import com.raizlabs.android.dbflow.structure.Model; + +import java.util.Collection; +import java.util.HashSet; + +/** + * Specialization of AsyncTaskLoader for Cursor objects in DBFlow. + */ +@TargetApi(11) +public class FlowCursorLoader extends AsyncTaskLoader { + /// Models to be observed for changes. + private final HashSet> models = new HashSet<>(); + + /// Queriable operation that the loader executes. + private Queriable queriable; + + /// Cursor for the loader. + private Cursor cursor; + + /// The observer that triggers the loader to reload anytime if it receives + /// notification of a change. + private final ForceLoadContentObserver observer = new ForceLoadContentObserver(); + + private boolean listening = false; + + /** + * Creates a fully-specified CursorLoader. See {@link android.content.ContentResolver#query(Uri, + * String[], String, String[], String) ContentResolver.query()} for documentation on the meaning + * of the parameters. These will be passed as-is to that call. + */ + public FlowCursorLoader(Context context, Queriable queriable) { + super(context); + + this.queriable = queriable; + } + + @Override + public Cursor loadInBackground() { + Cursor cursor = this.queriable.query(); + + if (cursor != null) { + cursor.getCount(); + } + + return cursor; + } + + @Override + public void deliverResult(Cursor cursor) { + if (this.isReset()) { + // An async query came in while the loader is stopped + if (cursor != null) { + cursor.close(); + } + + return; + } + + Cursor oldCursor = this.cursor; + this.cursor = cursor; + + if (this.isStarted()) { + super.deliverResult(cursor); + } + + if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { + oldCursor.close(); + } + + // Now that the result has been delivered, start listening for changes + // to the target models. Doing this at anytime earlier runs the risk of + // listening for changes while we are still loading content. + this.startListeningForChanges(); + } + + /** + * Register the loader for changes to a Flow model. When changes to the model are + * detected, then the loader will automatically reload the content. + * + * @param model + */ + public void registerForContentChanges(Class model) { + if (this.models.contains(model)) { + return; + } + + this.models.add(model); + this.observer.registerForContentChanges(this.getContext(), model); + } + + @Override + protected void onStartLoading() { + if (this.cursor != null) { + this.deliverResult(this.cursor); + } + + if (this.takeContentChanged() || this.cursor == null) { + this.forceLoad(); + } + } + + @Override + protected void onStopLoading() { + // Make sure the loading has stopped. + this.cancelLoad(); + } + + @Override + public void onCanceled(Cursor cursor) { + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + + this.stopListeningForChanges(); + } + + @Override + protected void onReset() { + super.onReset(); + + this.startLoading(); + + if (cursor != null && !cursor.isClosed()) { + cursor.close(); + } + + cursor = null; + + this.observer.unregisterForContentChanges(this.getContext()); + } + + private void startListeningForChanges() { + if (!this.listening) { + this.observer.addModelChangeListener(this.observer); + this.listening = true; + } + } + + private void stopListeningForChanges() { + if (this.listening) { + this.observer.removeModelChangeListener(this.observer); + this.listening = false; + } + } + + public Collection> getModels() { + return this.models; + } + + public FlowContentObserver getContentObserver() { + return this.observer; + } + + private final class ForceLoadContentObserver extends FlowContentObserver + implements FlowContentObserver.OnModelStateChangedListener { + private boolean endOfTransaction = false; + + @Override + public boolean deliverSelfNotifications() { + return false; + } + + @Override + public void onModelStateChanged(@Nullable Class table, BaseModel.Action action, @NonNull SQLCondition[] primaryKeyValues) { + if (!this.endOfTransaction) { + if (action == BaseModel.Action.INSERT || action == BaseModel.Action.DELETE || action == BaseModel.Action.UPDATE) { + onContentChanged(); + } + } + } + + @Override + public void endTransactionAndNotify() { + // Mark this as the end of a transactions, and pass control to the base class + // to perform the notifications. + this.endOfTransaction = true; + super.endTransactionAndNotify(); + + // Notify the observer the content has changed. + this.endOfTransaction = false; + onContentChanged(); + } + } +} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowSimpleCursorAdapter.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowSimpleCursorAdapter.java new file mode 100644 index 000000000..eb783f7a7 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/list/FlowSimpleCursorAdapter.java @@ -0,0 +1,36 @@ +package com.raizlabs.android.dbflow.list; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.database.Cursor; +import android.widget.SimpleCursorAdapter; + +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.ModelAdapter; + +/** + * Specialization of SimpleCursorAdapter designed for DBFlow. The getItem() method + * return a model element instead of a Cursor element. + * + * @param + */ +public class FlowSimpleCursorAdapter extends SimpleCursorAdapter { + private final Class mModel; + private final ModelAdapter mModelAdapter; + + @TargetApi(11) + public FlowSimpleCursorAdapter(Context context, Class modelClass, int layout, Cursor c, String[] from, int[] to, int flags) { + super(context, layout, c, from, to, flags); + + this.mModel = modelClass; + this.mModelAdapter = FlowManager.getModelAdapter(modelClass); + } + + @Override + public TModel getItem(int position) { + Cursor cursor = (Cursor) super.getItem(position); + return cursor != null ? this.mModelAdapter.loadFromCursor(cursor) : null; + } +} diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelLoader.java new file mode 100644 index 000000000..3cd47e806 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelLoader.java @@ -0,0 +1,22 @@ +package com.raizlabs.android.dbflow.single; + +import android.annotation.TargetApi; +import android.content.Context; + +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.sql.queriable.Queriable; +import com.raizlabs.android.dbflow.structure.Model; + +/** + * Load a single model from the database. + * + * @param + */ +@TargetApi(11) +public class FlowModelLoader + extends FlowSingleModelLoader { + + public FlowModelLoader(Context context, Class model, Queriable queriable) { + super(context, model, FlowManager.getModelAdapter(model), queriable); + } +} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelViewLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelViewLoader.java new file mode 100644 index 000000000..35d280215 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowModelViewLoader.java @@ -0,0 +1,25 @@ +package com.raizlabs.android.dbflow.single; + +import android.annotation.TargetApi; +import android.content.Context; + +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.sql.queriable.Queriable; +import com.raizlabs.android.dbflow.structure.BaseModelView; +import com.raizlabs.android.dbflow.structure.InstanceAdapter; +import com.raizlabs.android.dbflow.structure.Model; + +/** + * Load a single DBFlow model from a ViewModel. + * + * @param + */ +@TargetApi(11) +public class FlowModelViewLoader + extends FlowSingleModelLoader { + + @SuppressWarnings("unchecked") + public FlowModelViewLoader(Context context, Class model, Class modelView, Queriable queriable) { + super(context, model, (InstanceAdapter) FlowManager.getModelViewAdapter(modelView), queriable); + } +} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowQueryModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowQueryModelLoader.java new file mode 100644 index 000000000..33302b5b3 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowQueryModelLoader.java @@ -0,0 +1,24 @@ +package com.raizlabs.android.dbflow.single; + +import android.annotation.TargetApi; +import android.content.Context; + +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.sql.queriable.Queriable; +import com.raizlabs.android.dbflow.structure.BaseQueryModel; + +/** + * Load a single DBFlow model from a QueryModel. + * + * @param + */ +@TargetApi(11) +public class FlowQueryModelLoader + extends FlowSingleModelLoader { + + public FlowQueryModelLoader(Context context, Class model, Queriable queriable) { + super(context, model, FlowManager.getQueryModelAdapter(model), queriable); + + this.setObserveModel(false); + } +} \ No newline at end of file diff --git a/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowSingleModelLoader.java b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowSingleModelLoader.java new file mode 100644 index 000000000..0ebf30051 --- /dev/null +++ b/dbflow/src/main/java/com/raizlabs/android/dbflow/single/FlowSingleModelLoader.java @@ -0,0 +1,161 @@ +package com.raizlabs.android.dbflow.single; + +import android.annotation.TargetApi; +import android.content.AsyncTaskLoader; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +import com.raizlabs.android.dbflow.runtime.FlowContentObserver; +import com.raizlabs.android.dbflow.sql.queriable.Queriable; +import com.raizlabs.android.dbflow.structure.InstanceAdapter; +import com.raizlabs.android.dbflow.structure.Model; + +import java.util.HashSet; + +/** + * Abstract class for all DBFlow Loader classes that return a single + * model element. + * + * @param + */ +@TargetApi(11) +public abstract class FlowSingleModelLoader + extends AsyncTaskLoader { + /// Model type being loaded. + private final Class model; + + /// Adapter for converting cursor into target model. + private final InstanceAdapter adapter; + + /// Queriable operation that the loader executes. + private Queriable queriable; + + /// Cursor for the loader. + private TModel result; + + /// Observe changes to the model. + private boolean observeModel = true; + + /// Collection of models to be observed. + private final HashSet> models = new HashSet<>(); + + private class ForceLoadContentObserver extends FlowContentObserver { + @Override + public boolean deliverSelfNotifications() { + return true; + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + onContentChanged(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + + onContentChanged(); + } + } + + private final FlowContentObserver mObserver = new ForceLoadContentObserver(); + + protected FlowSingleModelLoader(Context context, Class model, InstanceAdapter adapter, Queriable queriable) { + super(context); + + this.queriable = queriable; + this.model = model; + this.adapter = adapter; + } + + /* Runs on a worker thread */ + @Override + public TModel loadInBackground() { + Cursor cursor = this.queriable.query(); + + if (cursor == null || !cursor.moveToFirst()) { + return null; + } + + TModel model = this.adapter.newInstance(); + this.adapter.loadFromCursor(cursor, model); + + return model; + } + + /* Runs on the UI thread */ + @Override + public void deliverResult(TModel result) { + this.result = result; + + if (this.isStarted()) { + super.deliverResult(result); + } + } + + /** + * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks + * will be called on the UI thread. If a previous load has been completed and is still valid the + * result may be passed to the callbacks immediately. + *

+ * Must be called from the UI thread + */ + @Override + protected void onStartLoading() { + if (result != null) { + this.deliverResult(result); + } + + // Start watching for changes to the model. + if (this.observeModel) { + this.registerForContentChanges(this.model); + } + + if (this.takeContentChanged() || this.result == null) { + this.forceLoad(); + } + } + + /** + * Must be called from the UI thread + */ + @Override + protected void onStopLoading() { + this.cancelLoad(); + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + this.onStopLoading(); + + if (this.result != null) { + this.result = null; + } + + // Unregister the loader for content changes. + this.mObserver.unregisterForContentChanges(this.getContext()); + } + + public Class getModel() { + return this.model; + } + + public void setObserveModel(boolean observeModel) { + this.observeModel = observeModel; + } + + public void registerForContentChanges(Class model) { + if (this.models.contains(model)) { + return; + } + + this.models.add(model); + this.mObserver.registerForContentChanges(this.getContext(), model); + } +} \ No newline at end of file