Using CursorLoader to get emails causes duplication of emails - android

I am trying to get email ids of uses contacts. For that I am using Cursor Loader. There is one problem I am getting duplicate email ids also. How to remove email duplicacy. Should I use raw query "SELECT DISTINCT" instead of using CursorLoader or there is some other solution?
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA};
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ;
//showing only visible contacts
String[] selectionArgs = null;
return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
}

I recently ran into this problem. It appears that the CursorLoader does not have an implementation of "DISTINCT". My workaround adds a few lines to the onLoadFinish method and extends the BaseAdapter to accept a List parameter:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String projection[] = {
CommonDataKinds.Phone._ID,
CommonDataKinds.Phone.DISPLAY_NAME,
};
String select = "((" + CommonDataKinds.Phone.DISPLAY_NAME + " NOTNULL) and " + CommonDataKinds.Phone.HAS_PHONE_NUMBER + " > 0)";
String sort = CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
CursorLoader loader = new CursorLoader(
mContext,
CommonDataKinds.Phone.CONTENT_URI,
projection,
select,
null,
sort
);
return loader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
List<String> displayNames = new ArrayList<String>();
cursor.moveToFirst();
while(!cursor.isAfterLast()){
String name = cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME));
if(!displayNames.contains(name))
displayNames.add(name);
cursor.moveToNext();
}
mAdapter.swapCursor(displayNames);
}
Here is my BaseAdapter class:
public class AdapterAddContacts extends BaseAdapter{
private List<String> mData = new ArrayList<String>();
private Context mContext;
public AdapterAddContacts(Context context,List<String> displayNames){
mData = displayNames;
mContext = context;
}
#Override
public int getCount() {
if(mData != null)
return mData.size();
else
return 0;
}
#Override
public Object getItem(int pos) {
return mData.get(pos);
}
#Override
public long getItemId(int id) {
return id;
}
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.entry_add_contacts,parent,false);
String data = mData.get(pos);
TextView textName = (TextView)view.findViewById(R.id.my_contacts_add_display_name);
textName.setText(data);
textName.setTag(data);
return view;
}
public void swapCursor(List<String> displayNames){
mData = displayNames;
this.notifyDataSetChanged();
}
You should be able to modify this specifically for your needs.

Inspired by #mars, I have a solution that does not need a modification of the adapter. The idea is to delete the duplicates of the cursor; as there is no way to do it, we create a new cursor whithout the duplicates.
All the code is in onLoadFinished:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
MatrixCursor newCursor = new MatrixCursor(PROJECTION); // Same projection used in loader
if (cursor.moveToFirst()) {
String lastName = "";
do {
if (cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).compareToIgnoreCase(lastName) != 0) {
newCursor.addRow(new Object[]{cursor.getString(0), cursor.getString(1), cursor.getString(2) ...}); // match the original cursor fields
lastName =cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
}
} while (cursor.moveToNext());
}
mContactsAdapter.swapCursor(newCursor);
}

I used a small hack in my project - an SQL injection, like that:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(
this,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] {
"DISTINCT "+ MediaStore.Images.Media.BUCKET_ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME},
null, null, null);
}
This code returns only bundle names and their IDs from Gallery.
So, I'd rewrite your code like that:
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = new String[] {
"DISTINCT " + ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Email.DATA};
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ;
//showing only visible contacts
String[] selectionArgs = null;
return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder);
}

You can put setDistinct in your content provider.
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
...
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setDistinct(true);

If you are worried about performance and don't want to play around with cursor again in onLoadFinished(), then there is a small hack
I combined following two solutions from SO.
select distinct value in android sqlite
CursorLoader with rawQuery
And here is my working solution:
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
String tableName;
/*
* Choose the table to query and a sort order based on the code returned
* for the incoming URI.
*/
switch (uriMatcher.match(uri)) {
case NOTIFICATION:
tableName = NOTIFICATIONS_TABLE_NAME;
break;
case NOTIFICATION_TIMESTAMP:
Cursor cursor = db.query(true, NOTIFICATIONS_TABLE_NAME, projection, selection, selectionArgs, TIMESTAMP, null, sortOrder, null);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
case DOWNLOAD:
tableName = DOWNLOADS_TABLE;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (selection != null) {
selection = selection + "=?";
}
Cursor cursor = db.query(tableName, projection, selection, selectionArgs, null, null, sortOrder);
// Tell the cursor what uri to watch, so it knows when its source data
// changes
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
If you see in this case Table name is same is first 2 cases but i created a dummy Uri to achieve this. May not be a very good approach but works perfectly.

I found a solution
Use DISTINCT keyword in selection Array.
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, "DISTINCT" + ContactsContract.CommonDataKinds.Email.DATA};

Related

SQLite selectionArgs not working

I am having some kind of weird problem with my content provider: selectionArgs refused to work for a particular column.
Insertion
void saveFile(File file, Cursor cursor, Context context) {
if (file.isDirectory()) {
throw new IllegalArgumentException(file.getName() + " must be a file");
}
values.put(IS_DIRECTORY, false);
// Some other values.put()
context.getContentResolver().insert(FolderContract.CONTENT_URI, values);
}
And
void saveFolder(File file, String filesNum, Context context, int directTracks) {
if (!file.isDirectory()) {
throw new IllegalArgumentException(file.getName() + " must be a directory");
}
ContentValues values = new ContentValues();
values.put(IS_DIRECTORY, true);
// Some other values.put()
context.getContentResolver().insert(FolderContract.CONTENT_URI, values);
}
My Query
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Timber.w("onCreateLoader: ");
String selection = String.format("%s = ? AND %s = ?", FolderContract.IS_DIRECTORY, FolderContract.PARENT_ID);
String[] selectionArgs = selectionArgs = new String[]{"0", args.getString(FOLDER_PATH)};
return new CursorLoader(getActivity(), FolderContract.CONTENT_URI, null, selection, selectionArgs, null);
}
I want the the query to return the rows with FolderContract.PARENT_ID column = args.getString(FOLDER_PATH) while the FolderContract.IS_DIRECTORY column will be false.
But the above query returns zero results however if I run it without the FolderContract.IS_DIRECTORY in the selection it returns all the matching
results.The weird thing is that if I loop through the cursor, some of the returned rows has its FolderContract.IS_DIRECTORY column as 0 while
others is 1.
ContentProvider
#Override
public Uri insert(#NonNull Uri uri, ContentValues values) {
final SQLiteDatabase database = dbHelper.getWritableDatabase();
long insertedRowId = database.insert(FolderContract.TABLE_NAME, null, values);
if (insertedRowId > -1) {
Uri insertedUri = ContentUris.withAppendedId(CONTENT_URI, insertedRowId);
getContext().getContentResolver().notifyChange(insertedUri, null);
return insertedUri;
}
database.close();
return uri;
}
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final SQLiteDatabase database = dbHelper.getReadableDatabase();
Cursor cursor = database.query(FolderContract.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
Please any idea what I am doing wrong?

SQLite retrieve foreign key values with content provider

I have a sqlite database with "label" and "idea" tables in my Android app. made a foreign key on idea table as idea_label with int values that connected to label_table on its _id.
I use Loader to load my Cursor on the mainActivity that loads my idea table from the provider. As obvious it load idea_label int (But what I seek is to load the value from label_table which sets in label_body).
My loader on mainActivity class
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String[] projection = {
DatabaseContract.IdeaEntry._ID,
DatabaseContract.IdeaEntry.COLUMN_IDEA_NAME,
DatabaseContract.IdeaEntry.COLUMN_IDEA_DESCRIPTION,
DatabaseContract.IdeaEntry.COLUMN_IDEA_DATE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_LABEL,
DatabaseContract.IdeaEntry.COLUMN_IDEA_ICON,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_ACTIVE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_FAVORITE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_DONE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_ARCHIVED,
DatabaseContract.IdeaEntry.COLUMN_IDEA_ORDER,
};
return new CursorLoader(this,
DatabaseContract.IdeaEntry.CONTENT_URI_IDEA,
projection,
null, // selection
null, // selectionArgs
DatabaseContract.IdeaEntry.COLUMN_IDEA_ORDER // order
);
}
That calls this section on my provider class
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
SQLiteDatabase database = mDatabaseHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
switch (match){
case IDEAS:
cursor = database.query(DatabaseContract.IdeaEntry.IDEA_TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
Is there a way? trigger another loader? or my implementation is wrong in this case? any way or help that direct me to the right direction, will be very appreciated.
With SQLiteQueryBuilder you can join tables in setTables method and then just specify label_body column of label table in projection.
String[] projection = {
DatabaseContract.IdeaEntry._ID,
....
DatabaseContract.LabelEntry.COLUMN_LABEL_BODY,
};
...
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables("IDEA JOIN LABEL ON IDEA.IDEA_LABEL = LABEL._ID");
builder.query(database, projection, selection, selectionArgs, null, null, sortOrder);
Thanks to #gar_r to help and guide me , I did like this with DatabaseContract constants:
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String[] projection = {
DatabaseContract.IdeaEntry.IDEA_TABLE_NAME + "." + DatabaseContract.IdeaEntry._ID,
DatabaseContract.IdeaEntry.COLUMN_IDEA_NAME,
DatabaseContract.IdeaEntry.COLUMN_IDEA_DESCRIPTION,
DatabaseContract.IdeaEntry.COLUMN_IDEA_DATE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_LABEL,
DatabaseContract.IdeaEntry.COLUMN_IDEA_ICON,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_ACTIVE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_FAVORITE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_DONE,
DatabaseContract.IdeaEntry.COLUMN_IDEA_IS_ARCHIVED,
DatabaseContract.IdeaEntry.COLUMN_IDEA_ORDER,
DatabaseContract.LabelEntry.COLUMN_LABEL_BODY,
};
return new CursorLoader(this,
DatabaseContract.IdeaEntry.CONTENT_URI_IDEA,
projection,
null, // selection
null, // selectionArgs
DatabaseContract.IdeaEntry.COLUMN_IDEA_ORDER // order
);
}
That calls this section on my provider class
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
SQLiteDatabase database = mDatabaseHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
switch (match){
case IDEAS:
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(DatabaseContract.IdeaEntry.IDEA_TABLE_NAME + " JOIN " + DatabaseContract.LabelEntry.LABEL_TABLE_NAME
+ " ON " + DatabaseContract.IdeaEntry.COLUMN_IDEA_LABEL + " = " + DatabaseContract.LabelEntry.LABEL_TABLE_NAME + "." + DatabaseContract.LabelEntry._ID);
cursor = builder.query(database, projection, selection, selectionArgs, null, null, sortOrder);
break;

Why sqlite distinct doesn't work

I am using Loader to get data from db and I have some trouble whit distinct, in simple words - distinct doesn't work. here is my code:
private String[] CONTACTS_COLUMNS ={
"DISTINCT " + ContactsEntry.CONTACT_ID + " AS _id",
ContactsEntry.CONTACT_FROM,
ContactsEntry.CONTACT_NAME,
CardsPhonesEntry.CONTACT_PHONE,
CardsPhonesEntry.CARD_NUMBER
};
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(),
ContactsEntry.CONTENT_URI,
CONTACTS_COLUMNS,
mSelection, null, null);
}
here is some code from my contentProvider:
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor retCursor;
int match = sUriMatcher.match(uri);
switch (match){
case PEOPLE:
retCursor = getAllContacts(projection, selection, selectionArgs, sortOrder);
break;
default:
throw new UnsupportedOperationException("Unknown uri in query(): " + uri);
}
return retCursor;
}
private Cursor getAllContacts(String[] projection, String selection, String[] selectionArgs,
String sortOrder){
return sPeopleWithCardsAndPhones.query(
mDbHelper.getReadableDatabase(),
projection,
selection,
selectionArgs,
null, null,
sortOrder
);
}
static {
sPeopleWithCardsAndPhones = new SQLiteQueryBuilder();
sPeopleWithCardsAndPhones.setTables(
ContactsEntry.TABLE_NAME + " LEFT JOIN " + CardsPhonesEntry.TABLE_NAME + " ON " +
ContactsEntry.TABLE_NAME + "." + ContactsEntry.CONTACT_ID + " = " +
CardsPhonesEntry.TABLE_NAME + "." + CardsPhonesEntry.OWNER_ID
);
}
what i am doing wrong and why distinct doesn't want to work?
EDIT:
For example: I have 1 contact in first table, and in the second table there are 2 cards linked to this contact by contact_id;
I need to show list with only 1 item (first contact) without depending how much cards it has. and now when init loader - it shows me this contact twice, but not once.

How do implicit joined columns work with Android contacts data?

I'm querying the ContactsContract.Data table to find phone records.
I get an error when I create a new CursorLoader:
java.lang.IllegalArgumentException: Invalid column deleted
My code:
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
...
String[] projection = {
Phone.DELETED,
Phone.LOOKUP_KEY,
Phone.NUMBER,
Phone.TYPE,
Phone.LABEL,
Data.MIMETYPE,
Data.DISPLAY_NAME_PRIMARY
};
// "mimetype = ? AND deleted = ?"
String selection = Data.MIMETYPE + " = ? AND " Phone.DELETED + " = ?";
String[] args = {Phone.CONTENT_ITEM_TYPE, "0"};
return new CursorLoader(
this,
Data.CONTENT_URI,
projection,
selection,
args,
null);
Any idea why the Phone.DELETED column isn't included in the cursor? The documentation does say -
Some columns from the associated raw contact are also available
through an implicit join.
Looks like you've found a feature that has been documented in many places, but hadn't been implemented yet. I opened a bug for tracking this issue - lets see what AOSP guys have to say on the subject (bug report).
Meanwhile, you can use the following workaround:
Uri uri = ContactsContract.RawContactsEntity.CONTENT_URI;
String[] projection = {
Phone._ID,
Phone.DELETED,
//Phone.LOOKUP_KEY,
Phone.NUMBER,
Phone.TYPE,
Phone.LABEL,
Data.MIMETYPE,
Data.DISPLAY_NAME_PRIMARY
};
String selection = Data.MIMETYPE + " = ? AND " + Data.DELETED + " = ?";
String[] args = {
Phone.CONTENT_ITEM_TYPE, "0"
};
return new CursorLoader(
this,
uri,
projection,
selection,
args,
null);
Changes:
Use RawContactsEntity's URI
LOOKUP_KEY is not accessible via above URI - you'll have to execute additional query if you absolutely need this column
_ID column will be required if you are going to use the resulting Cursor in CursorAdapter.
Edit: following #MichaelAlanHuff's request I'm posting the parts of code which this answer is based upon
From com.android.providers.contacts.ContactsProvider2#queryLocal() (source code of ContactsProvider2):
protected Cursor queryLocal(final Uri uri, final String[] projection, String selection,
String[] selectionArgs, String sortOrder, final long directoryId,
final CancellationSignal cancellationSignal) {
final SQLiteDatabase db = mDbHelper.get().getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String groupBy = null;
String having = null;
String limit = getLimit(uri);
boolean snippetDeferred = false;
// The expression used in bundleLetterCountExtras() to get count.
String addressBookIndexerCountExpression = null;
final int match = sUriMatcher.match(uri);
switch (match) {
...
case DATA:
case PROFILE_DATA:
{
final String usageType = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL);
setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt);
if (uri.getBooleanQueryParameter(Data.VISIBLE_CONTACTS_ONLY, false)) {
qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY);
}
break;
}
...
}
qb.setStrict(true);
// Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders.
String localizedSortOrder = getLocalizedSortOrder(sortOrder);
Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy,
having, limit, cancellationSignal);
if (readBooleanQueryParameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) {
bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection,
selectionArgs, sortOrder, addressBookIndexerCountExpression,
cancellationSignal);
}
if (snippetDeferred) {
cursor = addDeferredSnippetingExtra(cursor);
}
return cursor;
}
As you can see, there are two additional methods where SQLiteQueryBuilder used to build the query could be changed: setTablesAndProjectionMapForData() and additional query() method.
Source of com.android.providers.contacts.ContactsProvider2#setTablesAndProjectionMapForData():
private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri,
String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) {
StringBuilder sb = new StringBuilder();
sb.append(Views.DATA);
sb.append(" data");
appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID);
appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID);
appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID);
appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID);
appendDataUsageStatJoin(
sb, usageType == null ? USAGE_TYPE_ALL : usageType, DataColumns.CONCRETE_ID);
qb.setTables(sb.toString());
boolean useDistinct = distinct || !ContactsDatabaseHelper.isInProjection(
projection, DISTINCT_DATA_PROHIBITING_COLUMNS);
qb.setDistinct(useDistinct);
final ProjectionMap projectionMap;
if (addSipLookupColumns) {
projectionMap =
useDistinct ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap;
} else {
projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap;
}
qb.setProjectionMap(projectionMap);
appendAccountIdFromParameter(qb, uri);
}
Here you see the construction of table argument of the final query using StringBuilder which is being passed to several append*() methods. I'm not going to post their source code, but they really join the tables that appear in methods' names. If rawContacts table would be joined in, I'd expect to see a call to something like appendRawContactJoin() here...
For completeness: the other query() method that I mentioned does not modify table argument:
private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection,
String selection, String[] selectionArgs, String sortOrder, String groupBy,
String having, String limit, CancellationSignal cancellationSignal) {
if (projection != null && projection.length == 1
&& BaseColumns._COUNT.equals(projection[0])) {
qb.setProjectionMap(sCountProjectionMap);
}
final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having,
sortOrder, limit, cancellationSignal);
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
}
return c;
}
The inspection of the above chain of methods led me to the conclusion that there is an officially documented feature which is not implemented.

Getting a CastClassException error in the onListItemClick method of a ListFragment

I have a class that extends ListFragment and I'm using the Support Library. In the class's onListItemClick method, I have this line:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
String item = (String) ((Fragment) getListAdapter().getItem(position)).getString(1);
DetailFrag frag = (DetailFrag) getFragmentManager().findFragmentById(R.id.frag_stitchdetail);
if (frag != null && frag.isInLayout()) {
frag.setText(item);
}
}
where position is a parameter that is passed to the onListItemClick method. Its value at runtime is 0 for the example app I'm building, as there's only one item in the list. This line throws the following error:
java.lang.CastClassException: android.content.ContentResolver$CursorWrapperInner cannot be cast to java.lang.String
I've Googled this and searched on stackoverflow, but I don't see anything that will give me a hint as to why I can't cast this to a String. Anyone know? Thanks!
My ContentProvider code for the query:
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(STITCHTABLE_BASEPATH);
int uriType = sURIMatcher.match(uri);
switch (uriType)
{
case STITCHES_ID:
queryBuilder.appendWhere(SQLData.KEY_ROWID + "=" + uri.getLastPathSegment());
break;
case STITCHES:
//no filter
break;
default:
throw new IllegalArgumentException("Unknown URI");
}
Cursor cursor = queryBuilder.query(mDB.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
The code in my ListFragment for the CursorLoader:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Intent myData = getActivity().getIntent();
Bundle info = myData.getExtras();
String[] dataColumns = { "stitchname" };
int[] viewIDs = { R.id.stitchlist1 };
mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.stitchlist, null, dataColumns, viewIDs, 0);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0, info, (LoaderCallbacks<Cursor>) this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String selection = "stitchlevel=?";
String[] selectionArgs = new String[] {args.getString("Level")};
return (Loader<Cursor>) new CursorLoader(getActivity(), STITCHES_URI,
PROJECTION, selection, selectionArgs, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mAdapter.swapCursor((android.database.Cursor) cursor);
}

Categories

Resources