/* * Copyright 2013 Robotoworks Limited * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.robotoworks.mechanoid.db; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Map; import android.annotation.TargetApi; import android.content.AsyncQueryHandler; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Build; import android.text.TextUtils; import com.robotoworks.mechanoid.Mechanoid; import com.robotoworks.mechanoid.util.Closeables; /** * <p>Construct content provider/database queries using a fluent API.</p> * * <p>To create a new SQuery instance use the factory method {@link SQuery#newQuery()}</p> * * <p>Expressions can be chained using the {@link expr()} overloads, by default expressions are AND'd together * unless an explicit call is made to {@link or()} or {@link and()} between each expression, eg:-</p> * * <pre><code>Cursor cursor = SQuery.newQuery() * .expr(Books.TITLE, Op.LIKE, "A%") * .or() * .expr(Books.TITLE, Op.LIKE, "B%") * .select(Books.CONTENT_URI, * new String[] { * Books._ID, * Books.TITLE * });</code></pre> * * <p>If a query ends with either {@link or()} or {@link and()} they will not be included when executing the query * since it is an error to end a query without the right operand of an expression.</p> */ public class SQuery { /** * Used to represent Sqlite literal types used in expressions. * * @see SQuery#NULL * @see SQuery#CURRENT_TIME * @see SQuery#CURRENT_DATE * @see SQuery#CURRENT_TIMESTAMP * */ public static final class Literal { protected final String value; public Literal(String value) { this.value = value; } } /** * Represents the Sqlite literal NULL */ public static final Literal NULL = new Literal("NULL"); /** * Represents the Sqlite literal CURRENT_TIME */ public static final Literal CURRENT_TIME = new Literal("CURRENT_TIME"); /** * Represents the Sqlite literal CURRENT_DATE */ public static final Literal CURRENT_DATE = new Literal("CURRENT_DATE"); /** * Represents the Sqlite literal CURRENT_TIMESTAMP */ public static final Literal CURRENT_TIMESTAMP = new Literal("CURRENT_TIMESTAMP"); /** * <p>Comparison operator constants used in SQuery expressions.</p> * * <h2>Example</h2> * <pre><code>BooksRecord record = SQuery.newQuery() * .expr(Books.TITLE, Op.EQ, "Musashi") * .selectFirst(Books.CONTENT_URI); * </code></pre> * * @see SQuery#expr(String, String, boolean) * @see SQuery#expr(String, String, double) * @see SQuery#expr(String, String, float) * @see SQuery#expr(String, String, int) * @see SQuery#expr(String, String, long) * @see SQuery#expr(String, String, String) * @see SQuery#expr(String, String, Literal) */ public interface Op { /** * The equals (=) operator */ String EQ = " = "; /** * The not equal (!=) operator */ String NEQ = " != "; /** * The greater than (>) operator */ String GT = " > "; /** * The less than (<) operator */ String LT = " < "; /** * The greater than or equal (>=) operator */ String GTEQ = " >= "; /** * The less than or equal (<=) operator */ String LTEQ = " <= "; /** * The LIKE operator */ String LIKE = " LIKE "; /** * The IS operator */ String IS = " IS "; /** * The IS NOT operator */ String ISNOT = " IS NOT "; /** * The REGEXP operator */ String REGEXP = " REGEXP "; } /** * The AND operator */ private static final String AND = " AND "; /** * The OR operator */ private static final String OR = " OR "; private StringBuilder mBuilder; private List<String> mArgs = new ArrayList<String>(); private String mNextOp = null; private MechanoidContentProvider mProvider; /** * @return A list of expression arguments added so far */ public List<String> getArgs() { return mArgs; } /** * @return An array of expression arguments added so far */ public String[] getArgsArray() { return mArgs.toArray(new String[mArgs.size()]); } private SQuery() { mBuilder = new StringBuilder(); } public SQuery(MechanoidContentProvider provider) { this(); mProvider = provider; } public static SQuery newQuery() { return new SQuery(); } /** * <p>Force this query to use the given content provider when * doing active record queries, ie:- {@link #select(Uri)}, {@link #selectFirst(Uri)}, etc.</p> * * @param provider The provider to use when performing active record * queries */ public static SQuery newQuery(MechanoidContentProvider provider) { return new SQuery(provider); } /** * <p>Add an expression to the end of the currently added expressions, if * no previous boolean operator has been given ({@link and()} or {@link or()}) then * AND will be used by default when appending this expression.</p> * @param column Usually the column on the left side of the expression * @param op The operator, see {@link Op} for available operators * @param arg An argument for the right side of the expression, this will be added to * an array of expressions to be added as a bind argument. * @return */ public SQuery expr(String column, String op, String arg) { ensureOp(); mBuilder.append(column).append(op).append("?"); mArgs.add(arg); mNextOp = null; return this; } /** * <p>Add an expression to the end of the currently added expressions, if * no previous boolean operator has been given ({@link and()} or {@link or()}) then * AND will be used by default when appending this expression.</p> * @param column Usually the column on the left side of the expression * @param op The operator, see {@link Op} for available operators * @param arg An argument of type {@link Literal}, can be {@link SQuery#NULL}, * {@link SQuery#CURRENT_TIME}, {@link SQuery#CURRENT_DATE} or {@link SQuery#CURRENT_TIMESTAMP}. * @return */ public SQuery expr(String column, String op, Literal arg) { ensureOp(); mBuilder.append(column).append(op).append(" ").append(arg.value); mNextOp = null; return this; } /** * <p>An ISNULL expression on the given column name, ie:- column ISNULL</p> * @param column Usually the column name * @return */ public SQuery exprIsNull(String column) { ensureOp(); mBuilder.append(column).append(" ISNULL"); mNextOp = null; return this; } /** * A NOTNULL expression on the given column name, ie:- NOTNULL * @param column Usually the colum name * @return */ public SQuery exprNotNull(String column) { ensureOp(); mBuilder.append(column).append(" NOTNULL"); mNextOp = null; return this; } /** * Add a sub-expression to this expression, the sub-expression will be enclosed in brackets, ie:- * a=? AND (b=?) AND c=? where the expression contained in the brackets is the sub-expression * @param builder A query to use as a sub-expression * @return */ public SQuery expr(SQuery query) { List<String> args = query.getArgs(); ensureOp(); mBuilder.append("(").append(query).append(")"); if(args.size() > 0) { mArgs.addAll(args); } mNextOp = null; return this; } /** * @see SQuery#expr(String, String, String) */ public SQuery expr(String column, String op, boolean arg) { return expr(column, op, arg ? "1" : "0"); } /** * @see SQuery#expr(String, String, String) */ public SQuery expr(String column, String op, int arg) { return expr(column, op, String.valueOf(arg)); } /** * @see SQuery#expr(String, String, String) */ public SQuery expr(String column, String op, long arg) { return expr(column, op, String.valueOf(arg)); } /** * @see SQuery#expr(String, String, String) */ public SQuery expr(String column, String op, float arg) { return expr(column, op, String.valueOf(arg)); } /** * @see SQuery#expr(String, String, String) */ public SQuery expr(String column, String op, double arg) { return expr(column, op, String.valueOf(arg)); } public SQuery opt(String column, String op, String arg) { if(arg == null) { return this; } return expr(column, op, arg); } public SQuery opt(String column, String op, int arg) { if(arg == 0) { return this; } return expr(column, op, String.valueOf(arg)); } public SQuery opt(String column, String op, boolean arg) { if(!arg) { return this; } return expr(column, op, arg); } public SQuery opt(String column, String op, long arg) { if(arg == 0) { return this; } return expr(column, op, arg); } public SQuery opt(String column, String op, float arg) { if(arg == 0) { return this; } return expr(column, op, arg); } public SQuery opt(String column, String op, double arg) { if(arg == 0) { return this; } return expr(column, op, arg); } public SQuery append(String query, String... args) { if(query != null && query.length() > 0) { ensureOp(); mBuilder.append(query); if(args != null && args.length > 0) { for(String arg : args) { mArgs.add(arg); } } mNextOp = null; } return this; } public SQuery and() { mNextOp = AND; return this; } public SQuery or() { mNextOp = OR; return this; } private void ensureOp() { if(mBuilder.length() == 0) { return; } if(mNextOp == null) { mBuilder.append(AND); } else { mBuilder.append(mNextOp); mNextOp = null; } } @Override public String toString() { return mBuilder.toString(); } public Cursor query(SQLiteDatabase db, String table, String[] projection, String orderBy, String groupBy) { return db.query(table, projection, mBuilder.toString(), getArgsArray(), groupBy, null, orderBy); } public int firstInt(SQLiteDatabase db, String table, String column) { return firstInt(db, table, column, null); } public int firstInt(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; int value = 0; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getInt(0); } } finally { Closeables.closeSilently(cursor); } return value; } public long firstLong(SQLiteDatabase db, String table, String column) { return firstLong(db, table, column, null); } public long firstLong(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; long value = 0; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getLong(0); } } finally { Closeables.closeSilently(cursor); } return value; } public double firstDouble(SQLiteDatabase db, String table, String column) { return firstDouble(db, table, column, null); } public double firstDouble(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; double value = 0; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getDouble(0); } } finally { Closeables.closeSilently(cursor); } return value; } public float firstFloat(SQLiteDatabase db, String table, String column) { return firstFloat(db, table, column, null); } public float firstFloat(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; float value = 0; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getFloat(0); } } finally { Closeables.closeSilently(cursor); } return value; } public short firstShort(SQLiteDatabase db, String table, String column) { return firstShort(db, table, column, null); } public short firstShort(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; short value = 0; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getShort(0); } } finally { Closeables.closeSilently(cursor); } return value; } public byte[] firstBlob(SQLiteDatabase db, String table, String column) { return firstBlob(db, table, column, null); } public byte[] firstBlob(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; byte[] value = null; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getBlob(0); } } finally { Closeables.closeSilently(cursor); } return value; } public boolean firstBoolean(SQLiteDatabase db, String table, String column) { return firstBoolean(db, table, column, null); } public boolean firstBoolean(SQLiteDatabase db, String table, String column, String orderBy) { return firstShort(db, table, column, orderBy) > 0; } public String firstString(SQLiteDatabase db, String table, String column) { return firstString(db, table, column, null); } public String firstString(SQLiteDatabase db, String table, String column, String orderBy) { Cursor cursor = null; String value = null; try { cursor = query(db, table, new String[] { column }, orderBy, null); if(cursor.moveToFirst()) { value = cursor.getString(0); } } finally { Closeables.closeSilently(cursor); } return value; } public int update(SQLiteDatabase db, String table, ContentValues values) { return db.update(table, values, mBuilder.toString(), getArgsArray()); } public int delete(SQLiteDatabase db, String table) { return db.delete(table, mBuilder.toString(), getArgsArray()); } /** * <p>Select records using this query</p> * @param uri The ContentProvider Uri to query for * @param sortOrder The order by clause * @return The results as active records */ public <T extends ActiveRecord> List<T> select(Uri uri) { MechanoidContentProvider provider = getContentProvider(uri); List<T> records = provider.selectRecords(uri, this, null); return records; } /** * <p>Select records using this query</p> * @param uri The ContentProvider Uri to query for * @param sortOrder The order by clause * @return The results as active records */ public <T extends ActiveRecord> List<T> select(Uri uri, String sortOrder) { MechanoidContentProvider provider = getContentProvider(uri); List<T> records = provider.selectRecords(uri, this, sortOrder); return records; } /** * <p>Select records using this query</p> * @param uri The ContentProvider Uri to query for * @param groupBy The columns or expression to group by * @return The results as active records */ public <T extends ActiveRecord> List<T> select(Uri uri, String sortOrder, String... groupBy) { uri = appendGroupByToUri(uri, groupBy); MechanoidContentProvider provider = getContentProvider(uri); List<T> records = provider.selectRecords(uri, this, sortOrder); return records; } private Uri appendGroupByToUri(Uri uri, String... groupBy) { if(groupBy == null || groupBy.length == 0) return uri; StringBuilder builder = new StringBuilder(); for(int i = 0; i < groupBy.length; i++) { if(TextUtils.isEmpty(groupBy[i])) { continue; } builder.append(groupBy[i]); if(i < groupBy.length - 1) { builder.append(", "); } } return uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_GROUP_BY, builder.toString()).build(); } /** * Select records based on this query * @param uri The ContentProvider Uri to query for * @param keyColumnName The column that will be used as the key for the resulting map * @return A map of results using the value of keyColumnName as the map key */ public <T extends ActiveRecord> Map<String, T> selectMap(Uri uri, String keyColumnName, String... groupBy) { uri = appendGroupByToUri(uri, groupBy); MechanoidContentProvider provider = getContentProvider(uri); Map<String, T> records = provider.selectRecordMap(uri, this, keyColumnName); return records; } /** * <p>Select the first record from the results of using this query</p> * @param uri The ContentProvider Uri to query for * @param sortOrder The order by clause * @return The results as active records */ public <T extends ActiveRecord> T selectFirst(Uri uri, String sortOrder) { MechanoidContentProvider provider = getContentProvider(uri); List<T> records = provider.selectRecords(uri, this, sortOrder); if(records.size() > 0) { return records.get(0); } else { return null; } } /** * <p>Select the first record from the results of using this query</p> * @param uri The ContentProvider Uri to query for * @return The results as active records */ public <T extends ActiveRecord> T selectFirst(Uri uri) { MechanoidContentProvider provider = getContentProvider(uri); List<T> records = provider.selectRecords(uri, this, null); if(records.size() > 0) { return records.get(0); } else { return null; } } private MechanoidContentProvider getContentProvider(Uri uri) { if(mProvider != null) { return mProvider; } ContentResolver resolver = Mechanoid.getContentResolver(); ContentProviderClient client = resolver.acquireContentProviderClient(uri); MechanoidContentProvider provider = (MechanoidContentProvider) client.getLocalContentProvider(); return provider; } public Cursor select(Uri uri, String[] projection, String sortOrder) { ContentResolver resolver = Mechanoid.getContentResolver(); return resolver.query(uri, projection, toString(), getArgsArray(), sortOrder); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, String sortOrder) { return mAsync.startQuery(callback, uri, projection, toString(), getArgsArray(), sortOrder); } public Cursor select(Uri uri, String[] projection, String sortOrder, String... groupBy) { ContentResolver resolver = Mechanoid.getContentResolver(); uri = appendGroupByToUri(uri, groupBy); return resolver.query(uri, projection, toString(), getArgsArray(), sortOrder); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, String sortOrder, String... groupBy) { uri = appendGroupByToUri(uri, groupBy); return mAsync.startQuery(callback, uri, projection, toString(), getArgsArray(), sortOrder); } public Cursor select(Uri uri, String[] projection, String sortOrder, boolean enableNotifications) { ContentResolver resolver = Mechanoid.getContentResolver(); uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); return resolver.query(uri, projection, toString(), getArgsArray(), sortOrder); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, String sortOrder, boolean enableNotifications) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); return mAsync.startQuery(callback, uri, projection, toString(), getArgsArray(), sortOrder); } public Cursor select(Uri uri, String[] projection, String sortOrder, boolean enableNotifications, String... groupBy) { ContentResolver resolver = Mechanoid.getContentResolver(); uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return resolver.query(uri, projection, toString(), getArgsArray(), sortOrder); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, String sortOrder, boolean enableNotifications, String... groupBy) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return mAsync.startQuery(callback, uri, projection, toString(), getArgsArray(), sortOrder); } public Cursor select(Uri uri, String[] projection) { return select(uri, projection, (String) null); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection) { return selectAsync(callback, uri, projection, (String) null); } public Cursor select(Uri uri, String[] projection, String... groupBy) { return select(uri, projection, null, groupBy); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, String... groupBy) { return selectAsync(callback, uri, projection, null, groupBy); } public Cursor select(Uri uri, String[] projection, boolean enableNotifications) { return select(uri, projection, null, enableNotifications); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, boolean enableNotifications) { return selectAsync(callback, uri, projection, null, enableNotifications); } public Cursor select(Uri uri, String[] projection, boolean enableNotifications, String... groupBy) { return select(uri, projection, null, enableNotifications, groupBy); } public AsyncQuery selectAsync(AsyncQueryCallback callback, Uri uri, String[] projection, boolean enableNotifications, String... groupBy) { return selectAsync(callback, uri, projection, null, enableNotifications, groupBy); } public Cursor selectFirst(Uri uri, String[] projection) { Uri uriWithLimit = uri.buildUpon() .appendQueryParameter(MechanoidContentProvider.PARAM_LIMIT, "1") .appendQueryParameter(MechanoidContentProvider.PARAM_OFFSET, "0") .build(); return select(uriWithLimit, projection, (String) null); } public AsyncQuery selectFirstAsync(AsyncQueryCallback callback, Uri uri, String[] projection) { Uri uriWithLimit = uri.buildUpon() .appendQueryParameter(MechanoidContentProvider.PARAM_LIMIT, "1") .appendQueryParameter(MechanoidContentProvider.PARAM_OFFSET, "0") .build(); return selectAsync(callback, uriWithLimit, projection, (String) null); } public Cursor selectWithLimit(Uri uri, String[] projection, int limit, int offset) { Uri uriWithLimit = uri.buildUpon() .appendQueryParameter(MechanoidContentProvider.PARAM_LIMIT, String.valueOf(limit)) .appendQueryParameter(MechanoidContentProvider.PARAM_OFFSET, String.valueOf(offset)) .build(); return select(uriWithLimit, projection, (String) null); } public AsyncQuery selectWithLimitAsync(AsyncQueryCallback callback, Uri uri, String[] projection, int limit, int offset) { Uri uriWithLimit = uri.buildUpon() .appendQueryParameter(MechanoidContentProvider.PARAM_LIMIT, String.valueOf(limit)) .appendQueryParameter(MechanoidContentProvider.PARAM_OFFSET, String.valueOf(offset)) .build(); return selectAsync(callback, uriWithLimit, projection, (String) null); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection, String sortOrder) { if(Build.VERSION.SDK_INT < 11) { return null; } return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection, String sortOrder, String... groupBy) { if(Build.VERSION.SDK_INT < 11) { return null; } uri = appendGroupByToUri(uri, groupBy); return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection, String sortOrder, boolean enableNotifications) { if(Build.VERSION.SDK_INT < 11) { return null; } uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection, String sortOrder, boolean enableNotifications, String... groupBy) { if(Build.VERSION.SDK_INT < 11) { return null; } uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection) { if(Build.VERSION.SDK_INT < 11) { return null; } return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), null); } @TargetApi(11) public android.content.CursorLoader createLoader(Uri uri, String[] projection, boolean enableNotifications, String... groupBy) { if(Build.VERSION.SDK_INT < 11) { return null; } uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return new android.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), null); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, String sortOrder) { return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, String sortOrder, String... groupBy) { uri = appendGroupByToUri(uri, groupBy); return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, String sortOrder, boolean enableNotifications) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, String sortOrder, boolean enableNotifications, String... groupBy) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), sortOrder); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection) { return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), null); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, boolean enableNotifications) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), null); } public android.support.v4.content.CursorLoader createSupportLoader(Uri uri, String[] projection, boolean enableNotifications, String... groupBy) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(enableNotifications)).build(); uri = appendGroupByToUri(uri, groupBy); return new android.support.v4.content.CursorLoader(Mechanoid.getApplicationContext(), uri, projection, toString(), getArgsArray(), null); } public int firstInt(Uri uri, String column) { return firstInt(uri, column, null); } public int firstInt(Uri uri, String column, String orderBy) { Cursor cursor = null; int value = 0; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getInt(0); } } finally { Closeables.closeSilently(cursor); } return value; } public long firstLong(Uri uri, String column) { return firstLong(uri, column, null); } public long firstLong(Uri uri, String column, String orderBy) { Cursor cursor = null; long value = 0; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getLong(0); } } finally { Closeables.closeSilently(cursor); } return value; } public double firstDouble(Uri uri, String column) { return firstDouble(uri, column, null); } public double firstDouble(Uri uri, String column, String orderBy) { Cursor cursor = null; double value = 0; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getDouble(0); } } finally { Closeables.closeSilently(cursor); } return value; } public float firstFloat(Uri uri, String column) { return firstFloat(uri, column, null); } public float firstFloat(Uri uri, String column, String orderBy) { Cursor cursor = null; float value = 0; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getFloat(0); } } finally { Closeables.closeSilently(cursor); } return value; } public short firstShort(Uri uri, String column) { return firstShort(uri, column, null); } public short firstShort(Uri uri, String column, String orderBy) { Cursor cursor = null; short value = 0; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getShort(0); } } finally { Closeables.closeSilently(cursor); } return value; } public byte[] firstBlob(Uri uri, String column) { return firstBlob(uri, column, null); } public byte[] firstBlob(Uri uri, String column, String orderBy) { Cursor cursor = null; byte[] value = null; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getBlob(0); } } finally { Closeables.closeSilently(cursor); } return value; } public boolean firstBoolean(Uri uri, String column) { return firstBoolean(uri, column, null); } public boolean firstBoolean(Uri uri, String column, String orderBy) { return firstShort(uri, column, orderBy) > 0; } public String firstString(Uri uri, String column) { return firstString(uri, column, null); } public String firstString(Uri uri, String column, String orderBy) { Cursor cursor = null; String value = null; try { cursor = select(uri, new String[] { column }, orderBy, false); if(cursor.moveToFirst()) { value = cursor.getString(0); } } finally { Closeables.closeSilently(cursor); } return value; } public int[] selectIntArray(Uri uri, String column) { return selectIntArray(uri, column, null); } public int[] selectIntArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); int[] array = new int[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getInt(0); } return array; } finally { Closeables.closeSilently(cursor); } } public long[] selectLongArray(Uri uri, String column) { return selectLongArray(uri, column, null); } public long[] selectLongArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); long[] array = new long[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getLong(0); } return array; } finally { Closeables.closeSilently(cursor); } } public double[] selectDoubleArray(Uri uri, String column) { return selectDoubleArray(uri, column, null); } public double[] selectDoubleArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); double[] array = new double[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getDouble(0); } return array; } finally { Closeables.closeSilently(cursor); } } public float[] selectFloatArray(Uri uri, String column) { return selectFloatArray(uri, column, null); } public float[] selectFloatArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); float[] array = new float[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getFloat(0); } return array; } finally { Closeables.closeSilently(cursor); } } public short[] selectShortArray(Uri uri, String column) { return selectShortArray(uri, column, null); } public short[] selectShortArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); short[] array = new short[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getShort(0); } return array; } finally { Closeables.closeSilently(cursor); } } public byte[][] selectBlobArray(Uri uri, String column) { return selectBlobArray(uri, column, null); } public byte[][] selectBlobArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); byte[][] array = new byte[cursor.getCount()][]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getBlob(0); } return array; } finally { Closeables.closeSilently(cursor); } } public boolean[] selectBooleanArray(Uri uri, String column) { return selectBooleanArray(uri, column, null); } public boolean[] selectBooleanArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); boolean[] array = new boolean[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getLong(0) > 0; } return array; } finally { Closeables.closeSilently(cursor); } } public String[] selectStringArray(Uri uri, String column) { return selectStringArray(uri, column, null); } public String[] selectStringArray(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); String[] array = new String[cursor.getCount()]; for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); array[i] = cursor.getString(0); } return array; } finally { Closeables.closeSilently(cursor); } } public List<Integer> selectIntegerList(Uri uri, String column) { return selectIntegerList(uri, column, null); } public List<Integer> selectIntegerList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Integer> list = new ArrayList<Integer>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getInt(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<Long> selectLongList(Uri uri, String column) { return selectLongList(uri, column, null); } public List<Long> selectLongList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Long> list = new ArrayList<Long>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getLong(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<Double> selectDoubleList(Uri uri, String column) { return selectDoubleList(uri, column, null); } public List<Double> selectDoubleList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Double> list = new ArrayList<Double>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getDouble(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<Float> selectFloatList(Uri uri, String column) { return selectFloatList(uri, column, null); } public List<Float> selectFloatList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Float> list = new ArrayList<Float>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getFloat(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<Short> selectShortList(Uri uri, String column) { return selectShortList(uri, column, null); } public List<Short> selectShortList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Short> list = new ArrayList<Short>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getShort(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<byte[]> selectBlobList(Uri uri, String column) { return selectBlobList(uri, column, null); } public List<byte[]> selectBlobList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<byte[]> list = new ArrayList<byte[]>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getBlob(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public List<Boolean> selectBooleanList(Uri uri, String column) { return selectBooleanList(uri, column, null); } public List<Boolean> selectBooleanList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<Boolean> list = new ArrayList<Boolean>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getLong(0) > 0); } return list; } finally { Closeables.closeSilently(cursor); } } public List<String> selectStringList(Uri uri, String column) { return selectStringList(uri, column, null); } public List<String> selectStringList(Uri uri, String column, String orderBy) { Cursor cursor = null; try { cursor = select(uri, new String[] { column }, orderBy, false); List<String> list = new ArrayList<String>(cursor.getCount()); for(int i=0; i < cursor.getCount(); i++) { cursor.moveToNext(); list.add(cursor.getString(0)); } return list; } finally { Closeables.closeSilently(cursor); } } public int update(Uri uri, ContentValues values) { ContentResolver resolver = Mechanoid.getContentResolver(); return resolver.update(uri, values, toString(), getArgsArray()); } public int update(Uri uri, ContentValues values, boolean notifyChange) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(notifyChange)).build(); ContentResolver resolver = Mechanoid.getContentResolver(); return resolver.update(uri, values, toString(), getArgsArray()); } public int delete(Uri uri) { ContentResolver resolver = Mechanoid.getContentResolver(); return resolver.delete(uri, toString(), getArgsArray()); } public int delete(Uri uri, boolean notifyChange) { uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, String.valueOf(notifyChange)).build(); ContentResolver resolver = Mechanoid.getContentResolver(); return resolver.delete(uri, toString(), getArgsArray()); } public int count(Uri uri) { ContentResolver resolver = Mechanoid.getContentResolver(); Cursor c = null; uri = uri.buildUpon().appendQueryParameter(MechanoidContentProvider.PARAM_NOTIFY, "false").build(); try { c = resolver.query(uri, new String[]{"count(*)"}, toString(), getArgsArray(), null); int count = 0; if (c.moveToFirst()) { count = c.getInt(0); } return count; } finally { Closeables.closeSilently(c); } } public boolean exists(Uri uri) { return count(uri) > 0; } private static Async mAsync; public static class Async extends AsyncQueryHandler { int mTokens = 0; protected synchronized int createToken() { mTokens++; return mTokens; } public Async(ContentResolver cr) { super(cr); } public AsyncQuery startQuery(AsyncQueryCallback callback, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) { AsyncQuery query = new AsyncQuery(this, createToken(), callback); WeakReference<AsyncQuery> queryRef = new WeakReference<AsyncQuery>(query); startQuery(query.token, queryRef, uri, projection, selection, selectionArgs, orderBy); return query; } @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { super.onQueryComplete(token, cookie, cursor); WeakReference<AsyncQuery> queryRef = (WeakReference<AsyncQuery>) cookie; AsyncQuery asyncQuery = queryRef.get(); if(asyncQuery != null) { asyncQuery.completeQuery(cursor); } } } public interface AsyncQueryCallback { void onQueryComplete(Cursor cursor); } public static synchronized void init(ContentResolver resolver) { if(mAsync == null) { mAsync = new Async(resolver); } } }