mirror of https://github.com/tasks/tasks
Remove some Astrid content provider functionality
parent
ff3936bf0d
commit
6a440cd91e
@ -1,376 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.provider;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
||||
import com.todoroo.andlib.data.Property.StringProperty;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.StoreObject;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.test.DatabaseTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.tasks.injection.TestComponent;
|
||||
|
||||
import static android.support.test.InstrumentationRegistry.getTargetContext;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotSame;
|
||||
import static junit.framework.Assert.fail;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class Astrid3ProviderTests extends DatabaseTestCase {
|
||||
|
||||
String[] PROJECTION = new String[] {
|
||||
Task.ID.name,
|
||||
Task.TITLE.name,
|
||||
};
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
// set up database
|
||||
Astrid3ContentProvider.setDatabaseOverride(database);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(TestComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
/** Test CRUD over tasks with the ALL ITEMS cursor */
|
||||
@Test
|
||||
public void testAllItemsCrud() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
|
||||
// fetch all tasks, get nothing
|
||||
Uri uri = Task.CONTENT_URI;
|
||||
Cursor cursor = resolver.query(uri, PROJECTION, "1", null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
// insert a task
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Task.TITLE.name, "mf doom?");
|
||||
resolver.insert(uri, values);
|
||||
|
||||
// fetch all tasks, get something
|
||||
cursor = resolver.query(uri, PROJECTION, "1", null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("mf doom?", cursor.getString(1));
|
||||
cursor.close();
|
||||
|
||||
// update all tasks
|
||||
values.put(Task.TITLE.name, "mf grimm?");
|
||||
resolver.update(uri, values, "1", null);
|
||||
|
||||
// fetch all tasks, get something
|
||||
cursor = resolver.query(uri, PROJECTION, "1", null, null);
|
||||
cursor.moveToFirst();
|
||||
assertEquals("mf grimm?", cursor.getString(1));
|
||||
cursor.close();
|
||||
|
||||
// delete a task
|
||||
assertEquals(1, resolver.delete(uri, "1", null));
|
||||
|
||||
// fetch all tasks, get nothing
|
||||
cursor = resolver.query(uri, PROJECTION, null, null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/** Test selecting data */
|
||||
@Test
|
||||
public void testSelection() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
Uri uri = Task.CONTENT_URI;
|
||||
|
||||
// insert some tasks
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Task.TITLE.name, "tujiko noriko");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_MUST_DO);
|
||||
resolver.insert(uri, values);
|
||||
|
||||
values.clear();
|
||||
values.put(Task.TITLE.name, "miho asahi");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_NONE);
|
||||
resolver.insert(uri, values);
|
||||
|
||||
// fetch all tasks with various selection parameters
|
||||
Cursor cursor = resolver.query(uri, PROJECTION, "1", null, null);
|
||||
assertEquals(2, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
cursor = resolver.query(uri, PROJECTION, null, null, null);
|
||||
assertEquals(2, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
cursor = resolver.query(uri, PROJECTION, Task.IMPORTANCE + "=" +
|
||||
Task.IMPORTANCE_MUST_DO, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("tujiko noriko", cursor.getString(1));
|
||||
cursor.close();
|
||||
|
||||
cursor = resolver.query(uri, PROJECTION, Task.IMPORTANCE + ">" +
|
||||
Task.IMPORTANCE_MUST_DO, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("miho asahi", cursor.getString(1));
|
||||
cursor.close();
|
||||
|
||||
cursor = resolver.query(uri, PROJECTION, Task.IMPORTANCE + "=" +
|
||||
Task.IMPORTANCE_DO_OR_DIE, null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/** Test updating */
|
||||
@Test
|
||||
public void testUpdating() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
|
||||
// insert some tasks
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Task.TITLE.name, "carlos silva");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_SHOULD_DO);
|
||||
|
||||
Uri carlosUri = resolver.insert(Task.CONTENT_URI, values);
|
||||
|
||||
values.clear();
|
||||
values.put(Task.TITLE.name, "felix hernandez");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_MUST_DO);
|
||||
resolver.insert(Task.CONTENT_URI, values);
|
||||
|
||||
String[] projection = new String[] {
|
||||
Task.ID.name,
|
||||
Task.TITLE.name,
|
||||
Task.IMPORTANCE.name,
|
||||
};
|
||||
|
||||
// test updating with single item URI
|
||||
Cursor cursor = resolver.query(Task.CONTENT_URI, projection,
|
||||
Task.TITLE.eq("carlos who?").toString(), null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
values.clear();
|
||||
values.put(Task.TITLE.name, "carlos who?");
|
||||
assertEquals(1, resolver.update(carlosUri, values, null, null));
|
||||
|
||||
cursor = resolver.query(Task.CONTENT_URI, projection, Task.TITLE.eq("carlos who?").toString(), null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
// test updating with all items uri
|
||||
cursor = resolver.query(Task.CONTENT_URI, PROJECTION,
|
||||
Task.IMPORTANCE.eq(Task.IMPORTANCE_NONE).toString(), null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
values.clear();
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_NONE);
|
||||
assertEquals(1, resolver.update(Task.CONTENT_URI, values,
|
||||
Task.IMPORTANCE.eq(Task.IMPORTANCE_SHOULD_DO).toString(), null));
|
||||
|
||||
cursor = resolver.query(Task.CONTENT_URI, PROJECTION,
|
||||
Task.IMPORTANCE.eq(Task.IMPORTANCE_NONE).toString(), null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
// test updating with group by uri
|
||||
try {
|
||||
Uri groupByUri = Uri.withAppendedPath(Task.CONTENT_URI,
|
||||
AstridApiConstants.GROUP_BY_URI + Task.TITLE.name);
|
||||
resolver.update(groupByUri, values, null, null);
|
||||
fail("Able to update using groupby uri");
|
||||
} catch (Exception e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/** Test deleting */
|
||||
@Test
|
||||
public void testDeleting() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
Uri allItemsUri = Task.CONTENT_URI;
|
||||
|
||||
// insert some tasks
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Task.TITLE.name, "modest mouse");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_DO_OR_DIE);
|
||||
Uri modestMouse = resolver.insert(allItemsUri, values);
|
||||
|
||||
values.clear();
|
||||
values.put(Task.TITLE.name, "death cab");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_MUST_DO);
|
||||
resolver.insert(allItemsUri, values);
|
||||
|
||||
values.clear();
|
||||
values.put(Task.TITLE.name, "murder city devils");
|
||||
values.put(Task.IMPORTANCE.name, Task.IMPORTANCE_SHOULD_DO);
|
||||
resolver.insert(allItemsUri, values);
|
||||
|
||||
// test deleting with single URI
|
||||
Cursor cursor = resolver.query(allItemsUri, PROJECTION, Task.TITLE.name +
|
||||
" = 'modest mouse'", null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
assertEquals(1, resolver.delete(modestMouse, null, null));
|
||||
|
||||
cursor = resolver.query(allItemsUri, PROJECTION, Task.TITLE.name +
|
||||
" = 'modest mouse'", null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
// test updating with all items uri
|
||||
cursor = resolver.query(allItemsUri, PROJECTION, Task.TITLE.name +
|
||||
" = 'murder city devils'", null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
assertEquals(1, resolver.delete(allItemsUri, Task.IMPORTANCE.name +
|
||||
">" + Task.IMPORTANCE_MUST_DO, null));
|
||||
|
||||
cursor = resolver.query(allItemsUri, PROJECTION, Task.TITLE.name +
|
||||
" = 'murder city devils'", null, null);
|
||||
assertEquals(0, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
// test with group by uri
|
||||
try {
|
||||
Uri groupByUri = Uri.withAppendedPath(Task.CONTENT_URI,
|
||||
AstridApiConstants.GROUP_BY_URI + Task.TITLE.name);
|
||||
resolver.delete(groupByUri, null, null);
|
||||
fail("Able to delete using groupby uri");
|
||||
} catch (Exception e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
/** Test CRUD over SINGLE ITEM uri */
|
||||
@Test
|
||||
public void testSingleItemCrud() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
|
||||
Uri uri = StoreObject.CONTENT_URI;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(StoreObject.TYPE.name, "rapper");
|
||||
values.put(StoreObject.ITEM.name, "mf doom?");
|
||||
Uri firstUri = resolver.insert(uri, values);
|
||||
|
||||
values.put(StoreObject.ITEM.name, "gm grimm!");
|
||||
Uri secondUri = resolver.insert(uri, values);
|
||||
assertNotSame(firstUri, secondUri);
|
||||
|
||||
String[] storeProjection = new String[] {
|
||||
StoreObject.ITEM.name,
|
||||
};
|
||||
|
||||
|
||||
Cursor cursor = resolver.query(uri, storeProjection, null, null, null);
|
||||
assertEquals(2, cursor.getCount());
|
||||
cursor.close();
|
||||
|
||||
cursor = resolver.query(firstUri, storeProjection, null, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("mf doom?", cursor.getString(0));
|
||||
cursor.close();
|
||||
|
||||
values.put(StoreObject.ITEM.name, "danger mouse.");
|
||||
resolver.update(firstUri, values, null, null);
|
||||
cursor = resolver.query(firstUri, storeProjection, null, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("danger mouse.", cursor.getString(0));
|
||||
cursor.close();
|
||||
|
||||
assertEquals(1, resolver.delete(firstUri, null, null));
|
||||
|
||||
cursor = resolver.query(uri, storeProjection, null, null, null);
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/** Test GROUP BY uri */
|
||||
@Test
|
||||
public void testGroupByCrud() {
|
||||
ContentResolver resolver = getTargetContext().getContentResolver();
|
||||
Uri uri = Task.CONTENT_URI;
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Task.TITLE.name, "catwoman");
|
||||
resolver.insert(uri, values);
|
||||
|
||||
values.put(Task.TITLE.name, "the joker");
|
||||
resolver.insert(uri, values);
|
||||
resolver.insert(uri, values);
|
||||
resolver.insert(uri, values);
|
||||
|
||||
values.put(Task.TITLE.name, "deep freeze");
|
||||
resolver.insert(uri, values);
|
||||
resolver.insert(uri, values);
|
||||
|
||||
Uri groupByUri = Uri.withAppendedPath(Task.CONTENT_URI,
|
||||
AstridApiConstants.GROUP_BY_URI + Task.TITLE.name);
|
||||
Cursor cursor = resolver.query(groupByUri, PROJECTION, null, null, Task.TITLE.name);
|
||||
assertEquals(3, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
assertEquals("catwoman", cursor.getString(1));
|
||||
cursor.moveToNext();
|
||||
assertEquals("deep freeze", cursor.getString(1));
|
||||
cursor.moveToNext();
|
||||
assertEquals("the joker", cursor.getString(1));
|
||||
cursor.close();
|
||||
|
||||
// test "group-by" with metadata
|
||||
IntegerProperty age = new IntegerProperty(Metadata.TABLE, Metadata.VALUE1.name);
|
||||
StringProperty size = Metadata.VALUE2;
|
||||
|
||||
uri = Metadata.CONTENT_URI;
|
||||
|
||||
values.clear();
|
||||
values.put(Metadata.TASK.name, 1);
|
||||
values.put(Metadata.KEY.name, "sizes");
|
||||
values.put(age.name, 50);
|
||||
values.put(size.name, "large");
|
||||
resolver.insert(uri, values);
|
||||
|
||||
values.put(age.name, 40);
|
||||
values.put(size.name, "large");
|
||||
resolver.insert(uri, values);
|
||||
|
||||
values.put(age.name, 20);
|
||||
values.put(size.name, "small");
|
||||
resolver.insert(uri, values);
|
||||
|
||||
String[] metadataProjection = new String[] { "AVG(" + age + ")" };
|
||||
|
||||
Uri groupBySizeUri = Uri.withAppendedPath(Metadata.CONTENT_URI,
|
||||
AstridApiConstants.GROUP_BY_URI + size.name);
|
||||
cursor = resolver.query(groupBySizeUri, metadataProjection, null, null, size.name);
|
||||
assertEquals(2, cursor.getCount());
|
||||
|
||||
cursor.moveToFirst();
|
||||
assertEquals(45, cursor.getInt(0));
|
||||
cursor.moveToNext();
|
||||
assertEquals(20, cursor.getInt(0));
|
||||
}
|
||||
|
||||
}
|
@ -1,403 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.provider;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.DatabaseDao;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.StoreObject;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
|
||||
import org.tasks.BuildConfig;
|
||||
import org.tasks.analytics.Tracker;
|
||||
import org.tasks.analytics.Tracking;
|
||||
import org.tasks.injection.ContentProviderComponent;
|
||||
import org.tasks.injection.InjectingContentProvider;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
* Astrid 3 Content Provider. There are two ways to use this content provider:
|
||||
* <ul>
|
||||
* <li>access it directly just like any other content provider
|
||||
* <li>use the DAO classes from the Astrid API library
|
||||
* </ul>
|
||||
* <p>
|
||||
* The following base URI's are supported:
|
||||
* <ul>
|
||||
* <li>content://com.todoroo.astrid/tasks - task data ({@link Task})
|
||||
* <li>content://com.todoroo.astrid/metadata - task metadata ({@link Metadata})
|
||||
* <li>content://com.todoroo.astrid/store - non-task store data ({@link StoreObject})
|
||||
* </ul>
|
||||
* <p>
|
||||
* Each URI supports the following components:
|
||||
* <ul>
|
||||
* <li>/ - operate on all items (insert, delete, update, query)
|
||||
* <li>/123 - operate on item id #123 (delete, update, query)
|
||||
* <li>/groupby/title - query with SQL "group by" (query)
|
||||
* </ul>
|
||||
* <p>
|
||||
* If you are writing a third-party application to access this data, you may
|
||||
* also consider using one of the Api DAO objects like TaskApiDao.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class Astrid3ContentProvider extends InjectingContentProvider {
|
||||
|
||||
/** URI for making a request over all items */
|
||||
private static final int URI_DIR = 1;
|
||||
|
||||
/** URI for making a request over a single item by id */
|
||||
private static final int URI_ITEM = 2;
|
||||
|
||||
/** URI for making a request over all items grouped by some field */
|
||||
private static final int URI_GROUP = 3;
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Database databaseOverride;
|
||||
|
||||
// --- instance variables
|
||||
|
||||
private boolean open;
|
||||
@Inject Lazy<Database> database;
|
||||
@Inject Lazy<Tracker> tracker;
|
||||
|
||||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
for(Uri uri : new Uri[] { Task.CONTENT_URI, Metadata.CONTENT_URI, StoreObject.CONTENT_URI, UserActivity.CONTENT_URI }) {
|
||||
String authority = BuildConfig.APPLICATION_ID;
|
||||
|
||||
String table = uri.toString();
|
||||
table = table.substring(table.indexOf('/', 11) + 1);
|
||||
|
||||
uriMatcher.addURI(authority, table, URI_DIR);
|
||||
uriMatcher.addURI(authority, table + "/#", URI_ITEM);
|
||||
uriMatcher.addURI(authority, table +
|
||||
AstridApiConstants.GROUP_BY_URI + "*", URI_GROUP);
|
||||
}
|
||||
}
|
||||
|
||||
public Astrid3ContentProvider() {
|
||||
setReadPermission(AstridApiConstants.PERMISSION_READ);
|
||||
setWritePermission(AstridApiConstants.PERMISSION_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case URI_DIR:
|
||||
case URI_GROUP:
|
||||
return "vnd.android.cursor.dir/vnd.astrid";
|
||||
case URI_ITEM:
|
||||
return "vnd.android.cursor/vnd.astrid.item";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported URI " + uri + " (" + uriMatcher.match(uri) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(ContentProviderComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ========================================================== helpers ===
|
||||
* ====================================================================== */
|
||||
|
||||
private class UriHelper<TYPE extends AbstractModel> {
|
||||
|
||||
/** empty model. used for insert */
|
||||
public TYPE model;
|
||||
|
||||
/** dao */
|
||||
public DatabaseDao<TYPE> dao;
|
||||
|
||||
/** creates from given model */
|
||||
public boolean create() {
|
||||
return dao.createNew(model);
|
||||
}
|
||||
|
||||
/** updates from given model */
|
||||
public void update() {
|
||||
dao.saveExisting(model);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private UriHelper<?> generateHelper(Uri uri, boolean populateModel) {
|
||||
tracker.get().reportEvent(Tracking.Events.ASTRID_3_CP);
|
||||
final Database db = getDatabase();
|
||||
if(uri.toString().startsWith(Task.CONTENT_URI.toString())) {
|
||||
UriHelper<Task> helper = new UriHelper<>();
|
||||
helper.model = populateModel ? new Task() : null;
|
||||
helper.dao = new RemoteModelDao<>(db, Task.class);
|
||||
return helper;
|
||||
} else if(uri.toString().startsWith(Metadata.CONTENT_URI.toString())) {
|
||||
UriHelper<Metadata> helper = new UriHelper<>();
|
||||
helper.model = populateModel ? new Metadata() : null;
|
||||
helper.dao = new DatabaseDao<>(db, Metadata.class);
|
||||
return helper;
|
||||
} else if(uri.toString().startsWith(StoreObject.CONTENT_URI.toString())) {
|
||||
UriHelper<StoreObject> helper = new UriHelper<>();
|
||||
helper.model = populateModel ? new StoreObject() : null;
|
||||
helper.dao = new DatabaseDao<>(db, StoreObject.class);
|
||||
return helper;
|
||||
} else if(uri.toString().startsWith(UserActivity.CONTENT_URI.toString())) {
|
||||
UriHelper<UserActivity> helper = new UriHelper<>();
|
||||
helper.model = populateModel ? new UserActivity() : null;
|
||||
helper.dao = new RemoteModelDao<>(db, UserActivity.class);
|
||||
return helper;
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("Unknown URI " + uri);
|
||||
}
|
||||
|
||||
public static void setDatabaseOverride(Database override) {
|
||||
databaseOverride = override;
|
||||
}
|
||||
|
||||
private Database getDatabase() {
|
||||
if (!open) {
|
||||
database.get().openForWriting();
|
||||
open = true;
|
||||
}
|
||||
if(databaseOverride != null) {
|
||||
return databaseOverride;
|
||||
}
|
||||
return database.get();
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== delete ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Delete from given table
|
||||
* @return number of rows deleted
|
||||
*/
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
UriHelper<?> helper = generateHelper(uri, false);
|
||||
switch (uriMatcher.match(uri)) {
|
||||
|
||||
// illegal operations
|
||||
|
||||
case URI_GROUP:
|
||||
throw new IllegalArgumentException("Only the / or /# URI is valid"
|
||||
+ " for deletion.");
|
||||
|
||||
// valid operations
|
||||
|
||||
case URI_ITEM: {
|
||||
String itemSelector = String.format("%s = '%s'",
|
||||
AbstractModel.ID_PROPERTY, uri.getPathSegments().get(1));
|
||||
if(TextUtils.isEmpty(selection)) {
|
||||
selection = itemSelector;
|
||||
} else {
|
||||
selection = itemSelector + " AND " + selection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case URI_DIR:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + uriMatcher.match(uri) + ")");
|
||||
}
|
||||
|
||||
return getDatabase().delete(helper.dao.getTable().name, selection, selectionArgs);
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== insert ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Insert key/value pairs into given table
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||
UriHelper<?> helper = generateHelper(uri, true);
|
||||
switch (uriMatcher.match(uri)) {
|
||||
|
||||
// illegal operations
|
||||
|
||||
case URI_ITEM:
|
||||
case URI_GROUP:
|
||||
throw new IllegalArgumentException("Only the / URI is valid"
|
||||
+ " for insertion.");
|
||||
|
||||
// valid operations
|
||||
|
||||
case URI_DIR: {
|
||||
helper.model.mergeWith(values);
|
||||
readTransitoriesFromModelContentValues(helper.model);
|
||||
if(!helper.create()) {
|
||||
throw new SQLException("Could not insert row into database (constraint failed?)");
|
||||
}
|
||||
|
||||
Uri newUri = ContentUris.withAppendedId(uri, helper.model.getId());
|
||||
getContext().getContentResolver().notifyChange(newUri, null);
|
||||
return newUri;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== update ===
|
||||
* ====================================================================== */
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues values, String selection,
|
||||
String[] selectionArgs) {
|
||||
UriHelper<?> helper = generateHelper(uri, true);
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
|
||||
// illegal operations
|
||||
|
||||
case URI_GROUP:
|
||||
throw new IllegalArgumentException("Only the / or /# URI is valid"
|
||||
+ " for update.");
|
||||
|
||||
// valid operations
|
||||
|
||||
case URI_ITEM: {
|
||||
String itemSelector = String.format("%s = '%s'",
|
||||
AbstractModel.ID_PROPERTY, uri.getPathSegments().get(1));
|
||||
if(TextUtils.isEmpty(selection)) {
|
||||
selection = itemSelector;
|
||||
} else {
|
||||
selection = itemSelector + " AND " + selection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case URI_DIR:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + uriMatcher.match(uri) + ")");
|
||||
}
|
||||
|
||||
Cursor cursor = query(uri, new String[] { AbstractModel.ID_PROPERTY.name },
|
||||
selection, selectionArgs, null);
|
||||
try {
|
||||
for(cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||
long id = cursor.getLong(0);
|
||||
helper.model.mergeWith(values);
|
||||
readTransitoriesFromModelContentValues(helper.model);
|
||||
helper.model.setId(id);
|
||||
helper.update();
|
||||
helper.model.clear();
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return cursor.getCount();
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void readTransitoriesFromModelContentValues(AbstractModel model) {
|
||||
ContentValues setValues = model.getSetValues();
|
||||
if (setValues != null) {
|
||||
Set<Entry<String, Object>> entries = setValues.valueSet();
|
||||
Set<String> keysToRemove = new HashSet<>();
|
||||
for (Entry<String, Object> entry: entries) {
|
||||
String key = entry.getKey();
|
||||
if (key.startsWith(AbstractModel.RETAIN_TRANSITORY_PREFIX)) {
|
||||
String newKey = key.substring(AbstractModel.RETAIN_TRANSITORY_PREFIX.length());
|
||||
Object value = setValues.get(key);
|
||||
model.putTransitory(newKey, value);
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : keysToRemove) {
|
||||
setValues.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================ query ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Query by task.
|
||||
* <p>
|
||||
* Note that the "sortOrder" field actually can be used to append any
|
||||
* sort of clause to your SQL query as long as it is not also the
|
||||
* name of a column
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
|
||||
String groupBy = null;
|
||||
|
||||
UriHelper<?> helper = generateHelper(uri, false);
|
||||
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
|
||||
builder.setTables(helper.dao.getTable().name);
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case URI_GROUP:
|
||||
groupBy = uri.getPathSegments().get(2);
|
||||
case URI_DIR:
|
||||
break;
|
||||
case URI_ITEM:
|
||||
String itemSelector = String.format("%s = '%s'",
|
||||
AbstractModel.ID_PROPERTY, uri.getPathSegments().get(1));
|
||||
builder.appendWhere(itemSelector);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + uriMatcher.match(uri) + ")");
|
||||
}
|
||||
|
||||
Cursor cursor = builder.query(getDatabase().getDatabase(), projection, selection, selectionArgs, groupBy, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
// --- change listeners
|
||||
|
||||
public static void notifyDatabaseModification(Context context) {
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
cr.notifyChange(Task.CONTENT_URI, null, false);
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.provider;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.UriMatcher;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.dao.Database;
|
||||
|
||||
import org.tasks.BuildConfig;
|
||||
import org.tasks.analytics.Tracker;
|
||||
import org.tasks.analytics.Tracking;
|
||||
import org.tasks.injection.ContentProviderComponent;
|
||||
import org.tasks.injection.InjectingContentProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
* Non-public-API SQL content provider.
|
||||
*
|
||||
* This provider is dangerous and unsupported, use at your own risk. It allows
|
||||
* full SQL queries, which means LIMIT and JOIN in queries. Only SELECT is
|
||||
* currently supported.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public class SqlContentProvider extends InjectingContentProvider {
|
||||
|
||||
// --- instance variables
|
||||
|
||||
private static final UriMatcher uriMatcher;
|
||||
|
||||
static {
|
||||
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
|
||||
uriMatcher.addURI(BuildConfig.APPLICATION_ID + ".private",
|
||||
"sql", 0);
|
||||
}
|
||||
|
||||
private boolean open;
|
||||
@Inject Lazy<Database> database;
|
||||
@Inject Lazy<Tracker> tracker;
|
||||
|
||||
public SqlContentProvider() {
|
||||
setReadPermission(AstridApiConstants.PERMISSION_READ);
|
||||
setWritePermission(AstridApiConstants.PERMISSION_WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case 0:
|
||||
return "vnd.android.cursor.dir/vnd.astrid";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported URI " + uri + " (" + uriMatcher.match(uri) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== delete ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Delete from given table
|
||||
* @return number of rows deleted
|
||||
*/
|
||||
@Override
|
||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== insert ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Insert key/value pairs into given table
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* =========================================================== update ===
|
||||
* ====================================================================== */
|
||||
|
||||
@Override
|
||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("unimplemented");
|
||||
}
|
||||
|
||||
/* ======================================================================
|
||||
* ============================================================ query ===
|
||||
* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Query by task.
|
||||
* <p>
|
||||
* Note that the "sortOrder" field actually can be used to append any
|
||||
* sort of clause to your SQL query as long as it is not also the
|
||||
* name of a column
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
|
||||
String[] selectionArgs, String sortOrder) {
|
||||
tracker.get().reportEvent(Tracking.Events.ASTRID_SQL_CP);
|
||||
return getDatabase().rawQuery(selection);
|
||||
}
|
||||
|
||||
private Database getDatabase() {
|
||||
if (!open) {
|
||||
database.get().openForWriting();
|
||||
open = true;
|
||||
}
|
||||
|
||||
return database.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void inject(ContentProviderComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue