So my app is attempting to integrate a sync adapter to the android native contact manager. This is all running smoothly except once a contact is synced, I am unable to update it. Details on this particular problem can be found here: Android: Content resolver query returning 0 rows when it ought not to But I can sum it up simply by just saying my content resolver query is returning 0 values because I am querying the wrong URI, I believe.
When I write the raw contact id to the phone, I do it with the following code:
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());
}
This constructor method is called when you are adding a new contact to the sync list:
private static void addContact(Context context, String account, Contact contact, BatchOperationForSync batchOperation) {
ContactSyncOperations contactOp = ContactSyncOperations.createNewContact(context, contact.getUsername(), account, batchOperation);//constructor called # this line
contactOp.addName(contact.getFirstName(), contact.getLastName());
contactOp.addEmail(contact.getEmail());
contactOp.addPhone(contact.getPhoneNumber(), Phone.TYPE_MOBILE);
contactOp.addPhone(contact.getHomePhone(), Phone.TYPE_HOME);
contactOp.addPhone(contact.getWorkPhone(), Phone.TYPE_WORK);
contactOp.addProfileAction(contact.getUsername());
Log.e("Adding contact", "Via sync");
}
As you can see in the constructor, I am calling a method called newInsertCpo, which can be viewed here:
private void addInsertOp() {
if(!mIsNewContact) {
mValues.put(Phone.RAW_CONTACT_ID, mRawContactId);
}
mBuilder = newInsertCpo(addCallerIsSyncAdapterParameter(Data.CONTENT_URI), mYield);
mBuilder.withValues(mValues);
if(mIsNewContact) {
mBuilder.withValueBackReference(Data.RAW_CONTACT_ID, mBackReference);
}
mYield = false;
mBatchOperation.add(mBuilder.build());
}
Now that you can see the code, let me explain the problem. When I am creating and writing the raw contact id, I am doing so to RawContacts.CONTENT_URI:
mBuilder = newInsertCpo(RawContacts.CONTENT_URI, true).withValues(mValues);
However, when I query the uri, I am querying as so:
Cursor cursor = resolver.query(Data.CONTENT_URI, DataQuery.PROJECTION, DataQuery.SELECTION, new String[] {String.valueOf(rawContactId)}, null);
From Data.CONTENT_URI. I believe this is where my problem is occurring. I used the sample code from Android's official dev site and tailored it for my own uses, but this part is pretty true to the example. Yet it is not working. My lead is what I said, I'm writing to one Uri and querying another. But I've attempted to change the query to RawContacts.CONTENT_URI (which caused an exception), and also changing the Uri I write to Data.CONTENT_URI, which also causes an exception. What's even more confusing is that I get raw contact ids from Data.CONTENT_URI when I do my lookupRawContactId 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;
}
So yea, if I get a cursor returning rawcontactId, why would I get 0 values querying that same Uri with that same rawcontactId i was returned? It doesn't make any sense! Does anyone have any insight? Thanks all.
The lookupRawContactId method should look like this instead:
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;
}
Notice the query is searching RawContacts.CONTENT_URI instead.
Related
I'm developing a SMS application and come to the following issue. Currently I can read SMS conversation by using provider Telephony.Sms.Conversations by using CursorLoader. From cursor returned by this CursorLoader, I can display conversations's address which are phone numbers.
My question is how to retrieve SMS conversation contact name efficiently to display along with SMS conversation, not the phone number. Is there anyway to load list of contacts from list of phone numbers returned by the CursorLoader before?. Of course I've tried to load one by one contact name by using phone number but that terribly reduce the application performance.
Thank you in advance.
I've been searching for a solution myself and eventually came out with a good compromise in my opinion.
As soon as my query is finished, I store in an HashMap<String, String> contact_map my values as
int SENDER_ADDRESS = cursor.getColumnIndex(Telephony.TextBasedSmsColumns.ADDRESS);
while (cursor.moveToNext()) {
contact_map.put(
cursor.getString(SENDER_ADDRESS),
getContactName(getApplicationContext(), cursor.getString(SENDER_ADDRESS))
);
}
Method getContactName:
public static String getContactName(Context context, String phoneNumber) {
ContentResolver cr = context.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor cursor = cr.query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (cursor == null) {
return null;
}
String contactName = null;
if(cursor.moveToFirst()) {
contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
}
if(cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (contactName != null) {
return contactName;
} else {
return phoneNumber;
}
}
EDIT:
I then get the contact name with
String name = contact_map.get(cursor.getString(SENDER_ADDRESS));
Hope it helps!
In my android app when an incoming call i want to show my custom ui and i am able to do this.
No i want to check incoming number is from contacts or not.
Below is my code for doing so but it returns null for an incoming number which is stored in my contacts list.
public String findNameByNumber(String num){
Uri uri = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(num));
String name = null;
Cursor cursor = mContext.getContentResolver().query(uri,
new String[] { Phones.DISPLAY_NAME }, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
name = cursor.getString(cursor.getColumnIndex(Phones.DISPLAY_NAME));
cursor.close();
callyName.setText(name+" Calling..");
}
return name;
}
and i have incoming call from a number say+917878787878 but in my contacts this contact is stored as name XYZ with number 78 78 787878,which is formated because between number there are space.and also try by excluding +91 but still it returns null.
So how can i find number which is stored in any format.Which may be stored with country code or not.
Thanks In advance.
Try this code instead (using PhoneLookup.CONTENT_FILTER_URI instead of Phones):
String res = null;
try {
ContentResolver resolver = ctx.getContentResolver();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME}, null, null, null);
if (c != null) { // cursor not null means number is found contactsTable
if (c.moveToFirst()) { // so now find the contact Name
res = c.getString(c.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME));
}
c.close();
}
} catch (Exception ex) {
/* Ignore */
}
return res;
As docs says ContactsContract.PhoneLookup: A table that represents the result of looking up a phone number, for example for caller ID. To perform a lookup you must append the number you want to find to CONTENT_FILTER_URI. This query is highly optimized.
My application uses the Contacts ContentProvider to store some of its data. When I load a contact into memory, I want to save its ID (so that I know how to save changes later), and the ID of all data fields it is using (so they can be directly updated). Here is some of my code:
Uri entityUri = Uri.withAppendedPath(
ContentUris.withAppendedId(RawContacts.CONTENT_URI, id),
Entity.CONTENT_DIRECTORY);
Cursor resultData = context.getContentResolver().query(
entityUri,
new String[]{RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1},
null, null, null);
resultData.moveToFirst();
this.id = id;
while (resultData.isAfterLast() == false) {
this.source_id = resultData.getInt(0);
if (!resultData.isNull(1)) {
if (resultData.getString(2).equals(Fields.DISPLAY_NAME)) {
this.display_name = resultData.getString(3);
this.display_name_id = resultData.getInt(1);
}
}
resultData.moveToNext();
}
resultData.close();
return this;
That queries the ContentProvider and gets the DISPLAY_NAME field from the data. The ID of the data record is stored in the display_name_id variable. It comes out as 4612 when I run it on my device.
I tried saving it, but it does not update as expected. In order to debug, I added a query that tries to find the correct data table record.
Cursor c = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
new String[] {ContactsContract.Data._ID, CommonDataKinds.StructuredName.DISPLAY_NAME},
ContactsContract.Data._ID + "=?",
new String[] {String.valueOf(this.display_name_id)}, null);
However, this cursor comes back as having a length of 0. How can this be? Why is the ID incorrect?
I tried locally and it works for me, here's my code slightly adapted from yours:
public void testContacts(final #Nonnull Context context, final int rawContactId, final #Nonnull String expectedDisplayName) {
Uri entityUri = Uri.withAppendedPath(
ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, rawContactId),
ContactsContract.RawContacts.Entity.CONTENT_DIRECTORY);
Cursor resultData = context.getContentResolver().query(
entityUri,
new String[]{
ContactsContract.RawContacts.SOURCE_ID,
ContactsContract.RawContacts.Entity.DATA_ID,
ContactsContract.RawContacts.Entity.MIMETYPE,
ContactsContract.RawContacts.Entity.DATA1
},
null, null, null);
int displayNameId = -1;
try {
final int columnIndexDataId = resultData.getColumnIndex(ContactsContract.RawContacts.Entity.DATA_ID);
final int columnIndexMimetype = resultData.getColumnIndex(ContactsContract.RawContacts.Entity.MIMETYPE);
final int columnIndexData = resultData.getColumnIndex(ContactsContract.RawContacts.Entity.DATA1);
while (resultData.moveToNext()) {
if (!resultData.isNull(columnIndexDataId)) {
if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(resultData.getString(columnIndexMimetype)) &&
expectedDisplayName.equals(resultData.getString(columnIndexData))) {
displayNameId = resultData.getInt(1);
break;
}
}
}
} finally {
resultData.close();
}
String reLookedUpDisplayName = null;
if (displayNameId != -1) {
Cursor reLookupCursor = context.getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
new String[] {
ContactsContract.Data._ID,
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
},
ContactsContract.Data._ID + "=?",
new String[] {String.valueOf(displayNameId)},
null);
try {
final int columnIndexId = reLookupCursor.getColumnIndex(ContactsContract.Data._ID);
final int columnIndexDisplayName = reLookupCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
while (reLookupCursor.moveToNext()) {
reLookedUpDisplayName = reLookupCursor.getString(columnIndexDisplayName);
}
} finally {
reLookupCursor.close();
}
}
Toast.makeText(
context,
reLookedUpDisplayName != null ? "Found re-looked up name: " + reLookedUpDisplayName : "Didn't find name re-looking it up",
Toast.LENGTH_LONG)
.show();
}
There's no big difference from your code, so compare or try to replace bits of it to see where you have a problem. Make sure you use a fresh Cursor for each query, and close it correctly afterwards (in a finally clause).
Another thing, make sure that if (resultData.getString(2).equals(Fields.DISPLAY_NAME)) is really what you're wanting to do (it compares the entry mime type with Fields.DISPLAY_NAME), but since you're saying you get the data ID correctly this shouldn't be the problem.
I am trying to create an app which simply offers an edittext and imagebutton. If the butten gets clicked, the idea is that an album is added to the Playlist, named in the edittext box. Albums should be selected randomly. Goes without saying that the album tracks should be in the correct order.
I can add more functionality later eg. save, overwrite, delete etc.
I have the interface but am struggling with the code. I sort of get the concept of ContentProviders.
so the code needs to:
access the Playlists, which I believe is achieved by using MediaStore.Audio.Playlists
access the Albums, which I believe is achieved by using MediaStore.Audio.Albums
add to the Playlist
I have the following code (most bits obtained from this site. Thanks btw) to access the Playlist but it crashes with a Null Exception error.
public void checkforplaylists()
{
//Get a cursor over all playlists.
final ContentResolver resolver= MediaProvider.mContentResolver;
final Uri uri=MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= resolver.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
return;
}
Anyone who can help?
I think you mean NullPointerException, which means one of your assignments is null and then you try to access a member of the object you intended it to be. Most likely it is resolver, but to be sure you need the line number reported and/or to step through that with a debugger.
This works. When using the ContentResolver, the Context (this) is required.
public void checkforplaylists(Context context)
{
ContentResolver cr = context.getContentResolver();
final Uri uri=MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= cr.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
Log.e(TAG,"Found playlists.");
return;
}
use this code, will work
public boolean addPlaylist(String pname) {
Uri playlists = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
Cursor c = resolver.query(playlists, new String[] { "*" }, null, null,
null);
long playlistId = 0;
c.moveToFirst();
do {
String plname = c.getString(c
.getColumnIndex(MediaStore.Audio.Playlists.NAME));
if (plname.equalsIgnoreCase(pname)) {
playlistId = c.getLong(c
.getColumnIndex(MediaStore.Audio.Playlists._ID));
break;
}
} while (c.moveToNext());
c.close();
if (playlistId != 0) {
Uri deleteUri = ContentUris.withAppendedId(playlists, playlistId);
Log.d(TAG, "REMOVING Existing Playlist: " + playlistId);
// delete the playlist
resolver.delete(deleteUri, null, null);
}
Log.d(TAG, "CREATING PLAYLIST: " + pname);
ContentValues v1 = new ContentValues();
v1.put(MediaStore.Audio.Playlists.NAME, pname);
v1.put(MediaStore.Audio.Playlists.DATE_MODIFIED,
System.currentTimeMillis());
Uri newpl = resolver.insert(playlists, v1);
Log.d(TAG, "Added PlayLIst: " + newpl);
flag=true;
return flag;
}
I'm working on an extend SMS application. And now i can read all the SMS message from the mmssms.db. In the SMS database table, the field 'person' indicate the '_id' in contact table. When 'person' is >= 1, that means the message is sent from people in the contact list. So i can 'managedQuery' from contact table. But the question is, in my mobile phone, the test program sometimes can NOT query the contact infomation even 'person' >= 1. So can somebody show me some correct ways to query contact infomation by 'person' filed in SMS table ?
Here is some sample code which can make my question more clear:
class ContactItem {
public String mName;
}
ContactItem getContact(Activity activity, final SMSItem sms) {
if(sms.mPerson == 0) return null;
Cursor cur = activity.managedQuery(ContactsContract.Contacts.CONTENT_URI,
new String[] {PhoneLookup.DISPLAY_NAME},
" _id=?",
new String[] {String.valueOf(sms.mPerson)}, null);
if(cur != null &&
cur.moveToFirst()) {
int idx = cur.getColumnIndex(PhoneLookup.DISPLAY_NAME);
ContactItem item = new ContactItem();
item.mName = cur.getString(idx);
return item;
}
return null;
}
Ok, as there is no one help me out, i tried to read some open source project code, and i got answer now. As i supposed at the very beginning, the best way to query the contact information from a SMS message, is to query by the NUMBER(also know as ADDRESS):
ContactItem getContactByAddr(Context context, final SMSItem sms) {
Uri personUri = Uri.withAppendedPath(
ContactsContract.PhoneLookup.CONTENT_FILTER_URI, sms.mAddress);
Cursor cur = context.getContentResolver().query(personUri,
new String[] { PhoneLookup.DISPLAY_NAME },
null, null, null );
if( cur.moveToFirst() ) {
int nameIdx = cur.getColumnIndex(PhoneLookup.DISPLAY_NAME);
ContactItem item = new ContactItem();
item.mName = cur.getString(nameIdx);
cur.close();
return item;
}
return null;
}