-
Notifications
You must be signed in to change notification settings - Fork 601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loaders and cursors for DBFlow #1091
base: develop
Are you sure you want to change the base?
Changes from all commits
91326fb
50382fc
a1ff768
35567d3
a92a9d1
de30375
bf4b415
50e409b
6dfd5d6
f3935e1
0947f8c
118204a
2a450a2
36b7fa7
f98ebc2
9431b13
e483520
1e05a47
ccdb83b
49d3993
25403c8
e3b8ecb
536a5bb
36b144d
cff3e40
09f1be7
956fc1b
63fe220
008dc04
a7105b4
1508e00
464ba50
f5a5b13
d983e5f
9632cd8
7d79b78
aa85b79
f0ab254
344a9a5
dea07b7
a1b9485
56392ad
34acd05
0212982
d1a52cb
fd72253
0554ec4
8d72965
d019191
aff9722
a613727
d7d188e
c97f1b3
83ac02d
4cd5e39
0067c1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <TModel> | ||
*/ | ||
public abstract class FlowCursorAdapter <TModel extends Model> extends CursorAdapter { | ||
private final ModelAdapter<TModel> modelAdapter; | ||
|
||
public FlowCursorAdapter(Context context, Class<TModel> modelClass, Cursor c, boolean autoRequery) { | ||
super(context, c, autoRequery); | ||
|
||
this.modelAdapter = FlowManager.getModelAdapter(modelClass); | ||
} | ||
|
||
@TargetApi(11) | ||
public FlowCursorAdapter(Context context, Class<TModel> 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Cursor> { | ||
/// Models to be observed for changes. | ||
private final HashSet<Class<? extends Model>> 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<? extends Model> 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<Class<? extends Model>> getModels() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #req rename to something like |
||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #? what about the other two, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is How is |
||
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(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <TModel> | ||
*/ | ||
public class FlowSimpleCursorAdapter <TModel extends Model> extends SimpleCursorAdapter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #? not sure the difference here between this and the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Adapter is specialization of the SimpleCursorAdapter in the Android SDK designed for DBFlow. The In some cases, however, the data you result set will be a homogenous data types, and the mapping between columns and views is straightforward text populations. In this case, you only need a single layout, and a mapping between columns and views. The |
||
private final Class<TModel> mModel; | ||
private final ModelAdapter<TModel> mModelAdapter; | ||
|
||
@TargetApi(11) | ||
public FlowSimpleCursorAdapter(Context context, Class<TModel> 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <TModel> | ||
*/ | ||
@TargetApi(11) | ||
public class FlowModelLoader <TModel extends Model> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #req no model restriction anywhere please. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #? How do you ensure the class is only parameterized by DBFlow |
||
extends FlowSingleModelLoader<TModel> { | ||
|
||
public FlowModelLoader(Context context, Class<TModel> model, Queriable queriable) { | ||
super(context, model, FlowManager.getModelAdapter(model), queriable); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <TModel> | ||
*/ | ||
@TargetApi(11) | ||
public class FlowModelViewLoader <TModel extends Model, TModelView extends BaseModelView> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #req no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #? How do you ensure the class is only parameterized by DBFlow |
||
extends FlowSingleModelLoader<TModel> { | ||
|
||
@SuppressWarnings("unchecked") | ||
public FlowModelViewLoader(Context context, Class<TModel> model, Class<TModelView> modelView, Queriable queriable) { | ||
super(context, model, (InstanceAdapter<TModel>) FlowManager.getModelViewAdapter(modelView), queriable); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#? what is this supposed to do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This forces the cursor to load the load the count.