I have an SQL query from string and trying to access ContentProvider. The sql query looks like:
String query = "SELECT * FROM application_settings WHERE _id = ?";
I have to access content provider by gettting ContentResolver like:
context.getContentResolver().query()
but query method accepts:
Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
Is there a way I can split the string query into projection, selection, selectionArgs and sortOrder?
I do not wish to execute raw queries so I would prefer to have a solution for this function with bind values.
I have just written a library which provides what you need. You only need to copy and paste it into the project and if you would like to add, expand and customize it depending on your requirements.
SqliteHandler.java
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
class SqliteHandler {
// VERY IMPORTANT MAKE SURE IT'S CORRECT AND REGISTERED IN THE MANIFEST
private String PROVIDER_NAME = "com.example.android.mySqlite";
private String CONTENT_URL = "content://" + PROVIDER_NAME + "/";
private Context context;
SqliteHandler(Context context, String PROVIDER_NAME) {
this.context = context;
this.PROVIDER_NAME = PROVIDER_NAME;
}
Cursor exeQuery(String query) {
try {
queryObject obj = convertQueryStringToQueryObject(query);
return context.getContentResolver().query(obj.uri, obj.projection, obj.selection, obj.selectionArgs, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cursor exeQuery(String query, String[] selectionArgs) {
try {
queryObject obj = convertQueryStringToQueryObject(query);
return context.getContentResolver().query(obj.uri, obj.projection, obj.selection, selectionArgs, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cursor exeQuery(String query, String selection, String[] selectionArgs) {
try {
queryObject obj = convertQueryStringToQueryObject(query);
return context.getContentResolver().query(obj.uri, obj.projection, selection, selectionArgs, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cursor exeQuery(String query, String[] projection, String[] selectionArgs) {
try {
queryObject obj = convertQueryStringToQueryObject(query);
return context.getContentResolver().query(obj.uri, projection, obj.selection, selectionArgs, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Cursor exeQuery(queryObject obj) {
try {
return context.getContentResolver().query(obj.uri, obj.projection, obj.selection, obj.selectionArgs, null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
class queryObject {
Uri uri;
String[] projection;
String selection;
String[] selectionArgs;
String sortOrder;
queryObject(String table_name, String[] projection, String selection, String[]
selectionArgs) {
this.uri = Uri.parse(CONTENT_URL + table_name);
this.projection = projection;
this.selection = selection;
this.selectionArgs = selectionArgs;
}
}
queryObject convertQueryStringToQueryObject(String query) {
try {
String selection = null;
String[] selectionArgs = null;
query = query.toLowerCase();
String[] s = query.split("select")[1].split("from");
String[] projection = s[0].split(",");
String[] s2 = s[1].split("where");
String table_name = s2[0];
String logText = "";
if (s2.length > 1) {
selection = s2[1];
String[] args = s2[1].split("=");
selectionArgs = new String[args.length - 1];// half of the args are values others are keys
int count = 0;
for (int i = 1; i < args.length; i++) {
selectionArgs[count] = args[i]
.split("and")[0]
.split("or")[0]
.replace(" ", "")
.replace("and", "")
.replace("or", "");
count++;
}
for (int i = 0; i < selectionArgs.length; i++) {
logText += selectionArgs[i];
if (i < selectionArgs.length - 1) logText += ",";
selection = selection.replace(selectionArgs[i], "?");
}
}
Log.i("table_name", table_name);
Log.i("selection: ", selection == null ? "null" : selection);
Log.i("selectionArgs", logText.equals("") ? "null" : logText);
logText = "";
for (int i = 0; i < projection.length; i++) {
logText += projection[i];
if (i < projection.length - 1) logText += ",";
}
Log.i("projection", logText);
return new queryObject(table_name, projection, selection, selectionArgs);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}}
How To Use
instantiate SqliteHandler, it's very important to pass valid PROVIDER_NAME and also make sure that your CONTENT_PROVIDER was registered in the AndroidManiFest.xml. For an illustration of how does it work, we pass three different queries and get return values which are objects of type queryObject
SqliteHandler sh = new SqliteHandler(this,"PROVIDER_NAME");
SqliteHandler.queryObject obj1 = sh.convertQueryStringToQueryObject("SELECT * FROM table_name");
SqliteHandler.queryObject obj2 = sh.convertQueryStringToQueryObject("SELECT * FROM table_name WHERE _id = ?");
SqliteHandler.queryObject obj3 = sh.convertQueryStringToQueryObject("SELECT param1,param2,param3 FROM table_name WHERE param1 =\"a\" and param2=\"b\" or param3=\"c\"");
The method convertQueryStringToQueryObject converts query string into query class then we can use this class for getContentResolver().query().
Important Note: because getContentResolver().query() needs Uri. Therefore, we need to create a Uri from the table_name. As a result, we need to pass valid PROVIDER_NAME to the instance of SqliteHandler.
Output Log
As you can see the three different queries broke apart into parameters which we can use in the getContentResolver().query()
// 1th query
I/table_name: table_name
I/selection:: null
I/selectionArgs: null
I/projection: *
// 2th query
I/table_name: table_name
I/selection:: _id = ?
I/selectionArgs: ?
I/projection: *
// 3th query
I/table_name: table_name
I/selection:: param1 =? and param2=? or param3=?
I/selectionArgs: "a","b","c"
I/projection: param1,param2,param3
Complete Example
in The SqliteHandler.java there is the exeQuery method which has several overloads. Moreover, You can have a Cursor at the Content Provider depending on different input parameters.
SqliteHandler sh = new SqliteHandler(this,"PROVIDER_NAME");
SqliteHandler.queryObject obj1 = sh.convertQueryStringToQueryObject("SELECT * FROM table_name");
SqliteHandler.queryObject obj2 = sh.convertQueryStringToQueryObject("SELECT * FROM table_name WHERE _id = ?");
SqliteHandler.queryObject obj3 = sh.convertQueryStringToQueryObject("SELECT param1,param2,param3 FROM table_name WHERE param1 =\"a\" and param2=\"b\" or param3=\"c\"");
Cursor c = sh.exeQuery(obj1);
Cursor c = sh.exeQuery(obj2);
Cursor c = sh.exeQuery(obj3);
Cursor c = sh.exeQuery("SELECT param1,param2,param3 FROM table_name WHERE param1 =\"a\" and param2=\"b\" or param3=\"c\"");
Cursor c = sh.exeQuery("SELECT * FROM table_name WHERE _id = ?",new String[]{"whereArg"});
Cursor c = sh.exeQuery("SELECT * FROM table_name"," _id = ? ",new String[]{"whereArg"});
Cursor c = sh.exeQuery("SELECT ? FROM table_name WHERE _id = ?",new String[]{"Field"},new String[]{"whereArg"});
However, if you don't want to use exeQuery try below walking through:
queryObject obj = convertQueryStringToQueryObject(query);
Cursor c = this.getContentResolver().query(obj.uri, obj.projection, obj.selection, obj.selectionArgs, null);
from the android document https://developer.android.com/guide/topics/providers/content-provider-basics#ClientProvider
cursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
projection, // The columns to return for each row
selectionClause, // Selection criteria
selectionArgs, // Selection criteria
sortOrder); // The sort order for the returned rows
you can do this
String[] projection = {"*"};
String[] selectionArgs = {"1", "2"}; //your ids here
Cursor cursor = getContentResolver().query(Uri.parse("content://your_provider/your_table"), projection, "_id", selectionArgs, null);
cursor.close();
to create provider see this https://developer.android.com/guide/topics/providers/content-provider-creating#top_of_page
see this answer also https://stackoverflow.com/a/1031101/10989990
Related
I'm trying to import contacts into my app, but struggling with getting the company name. Here's my code:
public List<ContactItem> getContactList(){
ArrayList<ContactItem> contactList = new ArrayList<ContactItem>();
Uri contactUri = ContactsContract.Contacts.CONTENT_URI;
String[] PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
};
String SELECTION = ContactsContract.Contacts.HAS_PHONE_NUMBER + "='1'";
Cursor contacts = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, PROJECTION, SELECTION, null, null);
if (contacts.getCount() > 0)
{
while(contacts.moveToNext()) {
ContactItem aContact = new ContactItem();
int idFieldColumnIndex = 0;
int nameFieldColumnIndex = 0;
int numberFieldColumnIndex = 0;
int companyFieldColumnIndex = 0;
String contactId = contacts.getString(contacts.getColumnIndex(ContactsContract.Contacts._ID));
nameFieldColumnIndex = contacts.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
if (nameFieldColumnIndex > -1)
{
aContact.setName(contacts.getString(nameFieldColumnIndex));
}
// Tried to get a company, but
// this always returns -1
companyFieldColumnIndex = contacts.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY);
if (companyFieldColumnIndex > -1)
{
Log.d(TAG, "getContactList: starts");
aContact.setCompany(contacts.getString(companyFieldColumnIndex));
}
PROJECTION = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER};
final Cursor phone = managedQuery(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, ContactsContract.Data.CONTACT_ID + "=?", new String[]{String.valueOf(contactId)}, null);
if(phone.moveToFirst()) {
while(!phone.isAfterLast())
{
numberFieldColumnIndex = phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
if (numberFieldColumnIndex > -1)
{
aContact.setPhoneNum(phone.getString(numberFieldColumnIndex));
phone.moveToNext();
TelephonyManager mTelephonyMgr;
mTelephonyMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (!mTelephonyMgr.getLine1Number().contains(aContact.getPhoneNum()))
{
contactList.add(aContact);
}
}
}
}
phone.close();
}
contacts.close();
}
return contactList;
}
I add a comment in my code, where i'm trying to get the contact's company name, but i always get -1. Some contacts have the company name, so something wrong in this part of code. How to get the company properly?
First thing, you are passing Projection which doesn't contains anything related to Company name so of course you won't get it.
Other thing which I am not sure is, you need to pass contact's RawId instead of ID to fetch the Company name. Here's something how you should do it,
String contactId = contacts.getString(contacts.getColumnIndex(ContactsContract.Contacts._ID));
String rawContactId = getRawContactId(contactId);
String companyName = getCompanyName(rawContactId);
& here are the functions you'll need:
private String getRawContactId(String contactId) {
String[] projection = new String[]{ContactsContract.RawContacts._ID};
String selection = ContactsContract.RawContacts.CONTACT_ID + "=?";
String[] selectionArgs = new String[]{contactId};
Cursor c = mContentResolver.query(ContactsContract.RawContacts.CONTENT_URI, projection, selection, selectionArgs, null);
if (c == null) return null;
int rawContactId = -1;
if (c.moveToFirst()) {
rawContactId = c.getInt(c.getColumnIndex(ContactsContract.RawContacts._ID));
}
c.close();
return String.valueOf(rawContactId);
}
and:
private String getCompanyName(String rawContactId) {
try {
String orgWhere = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] orgWhereParams = new String[]{rawContactId,
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE};
Cursor cursor = mContentResolver.query(ContactsContract.Data.CONTENT_URI,
null, orgWhere, orgWhereParams, null);
if (cursor == null) return null;
String name = null;
if (cursor.moveToFirst()) {
name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY));
}
cursor.close();
return name;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Using the code below i can check if a row contains a single string ( String a) but how would i check if a row equals either string (String a or b)?
public Cursor fetchMyList() {
String[] columns = { KEY_ROWID, KEY_CATEGORY, KEY_SUMMARY,
KEY_DESCRIPTION, KEY_EMAIL };
String selection = "description=?";
String a = "blue";
String b = "red";
String[] selectionArgs = { a };
// String[] selectionArgs = { a , b}; ///tried this dont work!!
Cursor cursor = null;
try {
cursor = database.query(DATABASE_TABLE, columns, selection,
selectionArgs, null, null, null);
} catch (Exception e) {
e.printStackTrace();
}
int numberOfRows = cursor.getCount();
if (numberOfRows <= 0) {
return cursor;
}
return cursor;
}
You can only pass 2 arguments if you declare 2 arguments. This is what you want:
String selection = "description=? OR description=?"; // Select rows with either description
String[] selectionArgs = {a , b};
I strongly suggest you check SQL language.
PS: do not catch Exception. You'll regret it later. Catch specifc Exception children; in your case you want to catch SQLException.
PS2: use Log instead of printStackTrace().
I want to use select query for retrieving data from table. I have found, rawQuery(query, selectionArgs) method of SQLiteDatabase class to retrieve data. But I don't know how the query and selectionArgs should be passed to rawQuery method?
rawQuery("SELECT id, name FROM people WHERE name = ? AND id = ?", new String[] {"David", "2"});
You pass a string array with an equal number of elements as you have "?"
Maybe this can help you
Cursor c = db.rawQuery("query",null);
int id[] = new int[c.getCount()];
int i = 0;
if (c.getCount() > 0)
{
c.moveToFirst();
do {
id[i] = c.getInt(c.getColumnIndex("field_name"));
i++;
} while (c.moveToNext());
c.close();
}
One example of rawQuery - db.rawQuery("select * from table where column = ?",new String[]{"data"});
if your SQL query is this
SELECT id,name,roll FROM student WHERE name='Amit' AND roll='7'
then rawQuery will be
String query="SELECT id, name, roll FROM student WHERE name = ? AND roll = ?";
String[] selectionArgs = {"Amit","7"}
db.rawQuery(query, selectionArgs);
see below code it may help you.
String q = "SELECT * FROM customer";
Cursor mCursor = mDb.rawQuery(q, null);
or
String q = "SELECT * FROM customer WHERE _id = " + customerDbId ;
Cursor mCursor = mDb.rawQuery(q, null);
For completeness and correct resource management:
ICursor cursor = null;
try
{
cursor = db.RawQuery("SELECT * FROM " + RECORDS_TABLE + " WHERE " + RECORD_ID + "=?", new String[] { id + "" });
if (cursor.Count > 0)
{
cursor.MoveToFirst();
}
return GetRecordFromCursor(cursor); // Copy cursor props to custom obj
}
finally // IMPORTANT !!! Ensure cursor is not left hanging around ...
{
if(cursor != null)
cursor.Close();
}
String mQuery = "SELECT Name,Family From tblName";
Cursor mCur = db.rawQuery(mQuery, new String[]{});
mCur.moveToFirst();
while ( !mCur.isAfterLast()) {
String name= mCur.getString(mCur.getColumnIndex("Name"));
String family= mCur.getString(mCur.getColumnIndex("Family"));
mCur.moveToNext();
}
Name and family are your result
I am trying to make a query to sqlite android to see for example how many users of a given username exist in a table.
This is my function. I must specify that "getContentResolver() != null" and so is variable name.
private int findSelectedUser(String name) {
int count = 0;
try {
String[] whereArgs = new String[] {name};
String[] PROJECTION = new String[] { MyProvider.SETTINGS_USERNAME };
Cursor c = getContentResolver().query(MyProvider.SETTINGS_URI,
PROJECTION, MyProvider.SETTINGS_USERNAME , whereArgs, null);
if (c != null) {
count = c.getCount();
c.close();
}
} catch (NullPointerException e) {
}
System.out.println("Found something? " + count);
return count;
}
And after running i receive the error from the subject...and don't get it. In my where clause i have one column, in my where arguments one value.
Please help me make some sence of this, Thank you.
I guess that works:
String[] whereArgs = new String[] {name};
String[] PROJECTION = new String[] { MyProvider.SETTINGS_USERNAME };
Cursor c = getContentResolver().query(MyProvider.SETTINGS_URI,
PROJECTION, MyProvider.SETTINGS_USERNAME + "=?" , whereArgs, null);
if (c != null) {
count = c.getCount();
c.close();
}
If you want to use whereArgs you have to have the same amount of ? in the where as you have items whereArgs
whereArgs will replace the ? in the final database query
String where = "name = ? OR name = ?";
String[] whereArgs = new String[] {
"Peter",
"Jim"
};
that results in name = 'Peter' OR name = 'Jim' for the query.
Btw: don't catch(NullPointerException e) - make your code safe so they can't happen
Cursor cursor = resolver.query(
Data.CONTENT_URI,
DataQuery.PROJECTION,
DataQuery.SELECTION,
new String[] {String.valueOf(rawContactId)},
null);
With PROJECTION being:
public static final String[] PROJECTION = new String[] {
Data._ID,
Data.MIMETYPE,
Data.DATA1,
Data.DATA2,
Data.DATA3};
and SELECTION being:
public static final String SELECTION = Data.RAW_CONTACT_ID + "=?";
The rawcontactId does return values, I've made logs to check. To give it some context I'm working with Account sync. The goal here is for it to find the data columns for existing contacts and writing over them with any new data. I'm working from the following sample code provided by android: http://developer.android.com/resources/samples/SampleSyncAdapter/src/com/example/android/samplesync/platform/ContactManager.html
To summarize my problem, I have two contacts via this synced account which are added without any problems, but are not being able to be updated. Anyone have experience with this? Thanks.
EDIT: Here is my rawContact returning method
private static long lookupRawContact(ContentResolver resolver, String username) {
Log.e("Looking up Raw Contact", username);
long authorId = 0;
Cursor cursor = resolver.query(
Data.CONTENT_URI,
UserIdQuery.PROJECTION,
UserIdQuery.SELECTION,
new String[] {username},
null);
try {
if(cursor != null && cursor.moveToFirst()) {
authorId = cursor.getLong(UserIdQuery.COLUMN_ID);
}
} finally {
if(cursor != null) {
cursor.close();
}
}
return authorId;
}
The numbers I get back are like 3061. Here is the UserIdQuery class:
final private static class UserIdQuery {
private UserIdQuery() {
}
public final static String[] PROJECTION = new String[] {RawContacts._ID};
public final static int COLUMN_ID = 0;
public static final String SELECTION = RawContacts.ACCOUNT_TYPE + "='" +
"com.tagapp.android" + "' AND " + RawContacts.SOURCE_ID + "=?";
}
And here is my constructor for a ContactSyncOperations class being used to add a new contact. The source id here is a username, the same as I call in my SELECTION argument.
public ContactSyncOperations(Context context, String username,
String accountName, BatchOperationForSync batchOperation) {
this(context, batchOperation);
mBackReference = mBatchOperation.size();
mIsNewContact = true;
mValues.put(RawContacts.SOURCE_ID, username);
mValues.put(RawContacts.ACCOUNT_TYPE, "com.tagapp.android");
mValues.put(RawContacts.ACCOUNT_NAME, accountName);
mBuilder = newInsertCpo(RawContacts.CONTENT_URI, true).withValues(mValues);
mBatchOperation.add(mBuilder.build());
}
Thanks!
There was an error in the lookupRawContactId method, the rawcontactId long I was getting wasn't the right one. It should have looked like this:
private static long lookupRawContact(ContentResolver resolver, String username) {
Log.e("Looking up Raw Contact", username);
long authorId = 0;
Cursor cursor = resolver.query(
RawContacts.CONTENT_URI,
UserIdQuery.PROJECTION,
UserIdQuery.SELECTION,
new String[] {username},
null);
try {
if(cursor != null && cursor.moveToFirst()) {
authorId = cursor.getLong(UserIdQuery.COLUMN_ID);
}
} finally {
if(cursor != null) {
cursor.close();
}
}
return authorId;
}
There are a few issues that i could locate with the following query:
Cursor cursor = resolver.query(Data.CONTENT_URI,
UserIdQuery.PROJECTION,
UserIdQuery.SELECTION,
new String[] {username}, null);
If all the columns are pointing out at RawContacts table then you should use RawContacts.CONTENT_URI instead of Data.CONTENT_URI.
Here the value of RawContacts.SOURCE_ID is compared with username
public static final String SELECTION = RawContacts.ACCOUNT_TYPE + "='" +
"com.tagapp.android" + "' AND " + RawContacts.SOURCE_ID + "=?";
new String[] {username}