Sorry if this might be a duplicate question, I've spent the evening trying to wrap my head around this, and I can't seem to find other posts that might cast some light on this as well, so I am hoping that a few more pair of eyes might spot something.
I am having this impression from the API docs for ContactsContract.Data that when you specify certain fields, the library does some magic and performs an implicit join for you in the background.
Doesn't seem to be working for me.
import android.provider.ContactsContract.CommonDataKinds.Phone;
private Cursor getContacts()
{
// Run query
Uri uri = Phone.CONTENT_URI;
String[] projection = new String[] {
Phone.DISPLAY_NAME,
Phone.NUMBER,
Phone.CONTENT_ITEM_TYPE,
Phone.HAS_PHONE_NUMBER,
Phone.IN_VISIBLE_GROUP
};
String selection = Phone.HAS_PHONE_NUMBER + " = '1' AND " + Phone.IN_VISIBLE_GROUP + " = '1'";
String[] selectionArgs = null;
String sortOrder = Phone.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
return getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
}
When this is run, it dies with a:
java.lang.IllegalArgumentException: Invalid column vnd.android.cursor.item/phone_v2
From the docs for ContactsContract.CommonDataKinds.Phone it clearly states that:
You can use all columns defined for ContactsContract.Data as well as the following aliases.
What am I missing?
Phone.CONTENT_ITEM_TYPE is your problem. That's not a column name, that's a constant that Data.MIME_TYPE is set to. Remove it from your projection and it should be fine.
Related
I'm making an Android App with a sqlite db, which has a query method like this:
String[] projection = {COLUMN_NAME_ID};
String selection = COLUMN_NAME_TEAM + " = ?";
String[] selectionArgs = {teamId.toString()};
String sortOrder = COLUMN_NAME_NAME + " ASC";
Cursor c = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
if (c.moveToFirst()){
//...
}
as suggested here. The point is to implement this query:
SELECT PLAYER_ID FROM PLAYERS WHERE TEAM_ID = ? ORDER BY NAME ASC;
According to the SQLiteDatabase.query documentation, this is a valid call to query(), and so far I've been doing it successfully with all my other queries in this App.
For some reason, in this query (and only in this) the App freezes in c.moveToFirst().
Some answers to similar questions suggest that it might be a performance issue. I don't think this is the case, as my table has only 6 rows and the query is quite simple.
Any ideas?
Thanks
I have created a getContentResolver().query using the follwing
uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String projection[] = { android.provider.MediaStore.Audio.Media.DATA,
android.provider.MediaStore.Audio.Media.TITLE,
android.provider.MediaStore.Audio.Media.ARTIST,
android.provider.MediaStore.Audio.Media.ALBUM,
android.provider.MediaStore.Audio.Media.COMPOSER,
android.provider.MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media._ID,
android.provider.MediaStore.Audio.Media.ALBUM_ID };
String selection1 = MediaStore.Audio.Media._ID + "=?" ;
String[] selectionArgs = new String[] {"" + 19};
cursor = this.getContentResolver().query(uri, projection,
selection1, selectionArgs, null);
songs = new ArrayList<String>();
while (cursor.moveToNext()) {
songs.add(cursor.getString(1));
This works perfectly fine for selecting single song , however when I try to select multiple songs using
String selection1 = MediaStore.Audio.Media._ID + "IN(?,?)";
String[] selectionArgs = new String[] {"" + 12,"" + 19};
This query fails .
Is there a proper way to select multiple songs , what is wrong with the above query.
please explain the changes needed to be made.
I don't see anything wrong with your query using the IN operator. I suspect the query with IN has quirks and limitations. I did not use it however. Try just using the OR operator instead.
Example:
String selection1 = MediaStore.Audio.Media._ID + "=12" + " OR " + MediaStore.Audio.Media._ID + "=19";
And of course not use selectionArgs as the parameter.
Another good try is using raw query instead, documentation # SQL rawQuery. With a raw query, you can use the IN operator, and I think it's a good idea for simplifying queries. I should use it next time.
Thanks for this question!
I'm trying to filer out city names and return matching cities based on the selection parameter being passed.
However the query call still returns all the rows. Even tried putting in a fake city name that doesn't already exist in the database and it still spits out the same original rows.
public void addLocation(String cityName){
Cursor cursor = mContext.getResolver().query(
WeatherContract.LocationEntry.CONTENT_URI,
null,
WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING +
" = " + cityName,
null,
null);
I've read the Android docs over and over, ContentResolver Query, and passing the selection parameter in doesn't seem to be doing anything.
public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
Assuming the provider you're using is androidx.core.content.FileProvider (according to what you've declared in your manifest), the selection, selectionArgs and sortOrder parameters of FileProvider.query are... ignored in AndroidX's implementation !
See the v1.2.0 source here (line 409)
The only solution I found is to filter and sort the results post-query, in your own code.
And yeah, that's kinda sad...
Instead of passing the value in the selection parameter, put it in selectionArgs.
public void addLocation(String cityName){
String[] args = { cityName };
Cursor cursor = mContext.getResolver().query(
WeatherContract.LocationEntry.CONTENT_URI,
null,
WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING +
" = ?",
args,
null);
i have constructed a basic content provider that stores SMS messages for learning purposes, so far i can read(without selection args), insert, update and delete.
However i have been stumped trying to figure out how to format the selection args for the WHERE clause in my provider:
Basicly my application needs to search for a specific timestamp (in long format) and return its _id
say your database has an entry like this that your trying to access:
2|1|1410293471300|test type 1||testing|0
and the entire database looks like this:
_id|CLIENTTRXID|CREATED_AT|TYPE|MESSAGEPRIO|MESSAGE|ACCEPTED
1|1|1410293471000|test type 1||testing|0
2|1|1410293471300|test type 1||testing|0
3|1|1410293471600|test type 1||testing|0
in sql the query would be
"select _id from alerts where CREATED_AT=1410293471300;"
the code i was hoping would do the equivalent:
//normally i would get the string dynamically but to make it equal to the sql
String date = "1410293471300";
String[] selectionArgs = new String[]{ date };
Cursor cursor = getContext().getContentResolver().query(AlertContract.CONTENT_URI, null, AlertContract.Column.CREATED_AT, selectionArgs, AlertContract.DEFAULT_SORT);
seems to always produce the following error no matter what i try as selectionArgs
Exception caught﹕ Cannot bind argument at index 1 because the index is out of range. The statement has 0 parameters.
here is the query method of my contentprovider:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables( AlertContract.TABLE);
switch (sURIMatcher.match(uri)) {
case AlertContract.STATUS_DIR:
break;
case AlertContract.STATUS_ITEM:
qb.appendWhere(AlertContract.Column.ID + "=" + uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException( "illegal uri: " + uri);
}
String orderBy = (TextUtils.isEmpty(sortOrder)) ? AlertContract.DEFAULT_SORT : sortOrder;
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
//register for uri changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
Log.d(TAG, "queried records: "+cursor.getCount());
return cursor;
}
Presumably im missing something extremely obvious, and will feel quite silly for having posted this question.
But for the moment i would very much appreciate any help, as i am quite stumped.
It looks like your issue is with your selection, rather than with your selectionArgs per se. The selection should be the whole query after the "where". Here your selection is "CREATED_AT". You need two more items to get it to work:
an =, since you want equality (you can also do other operators, of course)
a ?. This is where your selectionArgument will be inserted (each argument needs a ? in the selection, so there should be the same number of ?s in the selection as selectionArguments.
The end result should be more like "CREATED_AT = ?"
Check out the documentation and this tutorial for more info on how to correctly construct a ContentProvider query.
When you query the content provider, try the following. The selection should be AlertContract.Column.CREATED_AT + "=?"
Cursor cursor = getContext().getContentResolver().query(AlertContract.CONTENT_URI, null, AlertContract.Column.CREATED_AT + "=?", selectionArgs, AlertContract.DEFAULT_SORT);
I am making a query on the Android Contacts ContentProvider. I need a Group By clause. In Gingerbread and Honeycomb, I do something like this to search phone numbers and emails at the same time:
(The actual WHERE clause is much more complicated as it includes types checks. This is a simplification, but it yields the same result)
String request = Phone.NUMBER + " LIKE ? OR " + Email.DATA + " LIKE ?";
String[] params = new String["%test%", "%test%"];
Cursor cursor = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data._ID, Data.RAW_CONTACT_ID },
request + ") GROUP BY (" + Data.RAW_CONTACT_ID,
params, "lower(" + Data.DISPLAY_NAME + ") ASC");
The injection of the ')' finishes the WHERE clause and allow the insertion of a GROUP BY clause.
However, in Ice Cream Sandwich, it appears that the ContentProvider detects this and adds the correct number of parenthesis to prevent my injection. Any other way of doing this in a single cursor query?
Edit
Currently, I have removed the GROUP BY, and added a MatrixCursor to limit the impact, but I'd rather have a real cursor:
MatrixCursor result = new MatrixCursor(new String[] { Data._ID, Data.RAW_CONTACT_ID });
Set<Long> seen = new HashSet<Long>();
while (cursor.moveToNext()) {
long raw = cursor.getLong(1);
if (!seen.contains(raw)) {
seen.add(raw);
result.addRow(new Object[] {cursor.getLong(0), raw});
}
}
I recently battled this issue querying the CallLog.Calls DB (where we were not able to modify the ContentProvider). What we ended up going with was building a query that looked like this:
SELECT _id, date, duration, type, normalized_number FROM calls WHERE _id IN (
SELECT _id FROM calls WHERE date < ? GROUP BY normalized_number ORDER BY date DESC LIMIT ?
);
The idea here is that we place any valid sqlite in our subquery, return a list of ids and then query again for all calls with those ids.
The final code looked something like this:
String whereClause = "_id IN (SELECT _id FROM calls WHERE data < ? GROUP BY normalized_number ORDER BY date DESC LIMIT ?)";
Cursor cursor = context.getContentResolver().query(
CallLog.Calls.CONTENT_URI,
new String[] { "_id", "date", "duration", "normalized_number" },
whereClause,
new String[]{ String.valueOf(amount), String.valueOf(dateFrom) },
null
);
...
In the case that you're querying for contacts, it would look something like this:
String whereClause = "_id IN (SELECT _id FROM contacts WHERE " + Phone.NUMBER + " LIKE ? OR " + Email.DATA + " LIKE ? GROUP BY " + Data.RAW_CONTACT_ID + " ORDER BY lower(" + Data.DISPLAY_NAME + ") ASC)";
String[] params = new String["%test%", "%test%"];
Cursor cursor = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data._ID, Data.RAW_CONTACT_ID },
whereClause,
params,
null
);
There will be some decrease in performance (since we're essentially querying twice for the same results), but it will surely be a lot faster than querying for all calls and doing the GROUP BY work in java world and also allows you to build up the query with additional clauses.
Hope this helps. We used this on Oreo and it fulfilled our needs.
You could create a custom Uri such that when your UriMatcher in your ContentProvider gets it, you can insert your group by clause and then execute the raw sql directly on the database.
first off all excuse my POOR English!
I'm new to Java/Android, started with 4.2.1 and fight with that too almost 2 days, then i start reading some more details about SQLiteQueryBuilder the query part is pretty much that what u are looking for ;)
it have:
public Cursor query (SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder)
the query "function" of the Content Provider only gives you:
query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
here u can trick around, i will post you my code snip:
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
/* a String is a Object, so it can be null!*/
String groupBy = null;
String having = null;
switch (sUriMatcher.match(uri)) {
...
...
...
case EPISODES_NEXT:
groupBy = "ShowID";
queryBuilder.setTables(EpisodenTable.TableName);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Cursor c = queryBuilder.query(db, projection, selection, selectionArgs,
groupBy, having, sortOrder);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
thats its!
here the code i use to execute:
Cursor showsc = getContext().getContentResolver().query(
WhatsOnTVProvider.CONTENT_EPISODES_NEXT_URI,
EpisodenTable.allColums_inclCount,
String.valueOf(Calendar.getInstance().getTimeInMillis() / 1000)
+ " < date", null, null);