com.stanfy.enroscar.content.loader.ContentLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of enroscar-content Show documentation
Show all versions of enroscar-content Show documentation
Helper classes and extended abstractions for Android loaders, content resolvers, and DB access.
package com.stanfy.enroscar.content.loader;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
/**
* Loads data via content provider.
*/
public class ContentLoader extends BaseAsyncTaskLoader> {
/** Parameters. */
private final Params params;
/** Factory instance. */
private DataInstanceFactory factory;
/** Content observer. */
private Observer contentObserver;
/** Processor. */
private PostProcessor postProcessor;
/** Error handler. */
private ErrorHandler errorHandler;
/** Error instance. */
private Throwable error;
ContentLoader(final Context context, final Params params) {
super(context);
this.params = params;
}
public static Builder of(final DataInstanceFactory factory) {
return new Builder(factory);
}
@Override
public ResponseData loadInBackground() {
ContentResolver resolver = getContext().getContentResolver();
Cursor cursor = resolver.query(params.uri, params.projection, params.selection, params.selectionArgs, params.sort);
if (cursor == null) {
throw new IllegalStateException("Content provider hasn't responded to " + params.uri);
}
synchronized (this) {
if (contentObserver == null) {
contentObserver = new Observer();
resolver.registerContentObserver(params.uri, params.observeDescendentsChanges, contentObserver);
}
}
try {
T data = factory.createWithCursor(cursor);
if (postProcessor != null) {
data = postProcessor.process(getContext(), data);
}
return new ResponseData<>(data);
} catch (SQLiteException e) {
this.error = e;
return new ResponseData<>(params.errorCode, params.errorMessage);
} finally {
cursor.close();
}
}
@Override
public void deliverResult(final ResponseData data) {
if (isReset()) {
ensureContentObserverUnregistered();
return;
}
if (error != null && errorHandler != null) {
errorHandler.handleError(error);
}
super.deliverResult(data);
}
@Override
protected void onReset() {
super.onReset();
ensureContentObserverUnregistered();
}
private void ensureContentObserverUnregistered() {
synchronized (this) {
if (contentObserver != null) {
getContext().getContentResolver().unregisterContentObserver(contentObserver);
contentObserver = null;
}
}
}
/** Content observer. */
private class Observer extends ContentObserver {
public Observer() {
super(new Handler(Looper.getMainLooper()));
}
@Override
public boolean deliverSelfNotifications() {
return true;
}
@Override
public void onChange(final boolean selfChange) {
onContentChanged();
}
}
/** Converts cursor representation to some other model. */
public static interface DataInstanceFactory {
T createWithCursor(Cursor cursor);
}
/** Loader builder. */
public static class Builder {
/** Instance. */
private final Params params = new Params();
/** Factory. */
private final DataInstanceFactory factory;
/** 'After' processor. */
private PostProcessor after;
/** Error handler. */
private ErrorHandler errorHandler;
Builder(final DataInstanceFactory factory) {
if (factory == null) { throw new NullPointerException(); }
this.factory = factory;
}
public Builder uri(final Uri uri) {
params.uri = uri;
return this;
}
public Builder projection(final String[] projection) {
params.projection = projection;
return this;
}
public Builder selection(final String selection) {
params.selection = selection;
return this;
}
public Builder selectionArgs(final String[] selectionArgs) {
params.selectionArgs = selectionArgs;
return this;
}
public Builder sort(final String sort) {
params.sort = sort;
return this;
}
public Builder observeDescendents(final boolean value) {
params.observeDescendentsChanges = value;
return this;
}
/**
* Set error code that should be set to ResponseData if error happens.
*/
public Builder errorCode(final int errorCode) {
params.errorCode = errorCode;
return this;
}
/**
* Set error message that should be set to ResponseData if error happens.
*/
public Builder errorMessage(final String message) {
params.errorMessage = message;
return this;
}
public Builder after(final PostProcessor processor) {
this.after = processor;
return this;
}
public Builder after(final ErrorHandler handler) {
this.errorHandler = handler;
return this;
}
public ContentLoader get(final Context context) {
if (params.uri == null) {
throw new IllegalArgumentException("URI is not specified");
}
ContentLoader loader = new ContentLoader(context, params);
loader.factory = factory;
loader.postProcessor = after;
loader.errorHandler = errorHandler;
return loader;
}
}
/** Loader params. */
static class Params {
/** Request URI. */
Uri uri;
/** Projection. */
String[] projection;
/** Selection. */
String selection;
/** Selection arguments. */
String[] selectionArgs;
/** Sort order. */
String sort;
/** Error message. */
String errorMessage;
/** Error code. */
int errorCode = -1;
/** Whether to observe changes in descendent URIs. */
boolean observeDescendentsChanges;
}
/** Post loading processor (for background thread). */
public interface PostProcessor {
T process(Context context, T data);
}
/** Error handler (invoked in the main thread). */
public interface ErrorHandler {
void handleError(Throwable e);
}
}