Here is the code I am using to write a contact PHOTO. It works. If I am quick enough after writing it to the address book and go back and read the contact, I get my picture back.
But, after 10 seconds or so, in the debugger you can see an alarm fire and a thread startup and after that goes away if I go read the same contact back, the PHOTO was scaled down from roughly 400x400 down to 96x96.
It looks like there is a trigger firing on the backend to scale the PHOTO after it is written. Does anyone know a way to get around this or control it? Or is there a better way of writing a PHOTO that will not cause this scaling trigger?
I am doing this in Android 2.1 on Droid.
In trying to determine if I was writing the PHOTO correctly, I saw that there was a
ContactsContract.ContactsColumns interface with PHOTO_URI and PHOTO_THUMB_URI members, but I cannot find any way to get at it as the interface is protected and I cannot find any of the joins returning them. Does anyone how to use them?
Uri uri = ContactsContract.RawContacts.CONTENT_URI;
String[] projection = new String[] { BaseColumns._ID };
String selection = ContactsContract.RawContacts.CONTACT_ID + "=?";
String[] arguments = new String[] { "" + lContactID };
Cursor cursor = resolver.query(uri, projection, selection, arguments, null);
if (cursor != null && cursor.getCount() > 0)
{
while (cursor.moveToNext())
{
long rawContactId = cursor.getLong(0);
int row = -1;
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = new String[] { ContactsContract.Data._ID };
String selection = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";
String[] params = new String[] { "" + rawContactId };
Cursor cursor = resolver.query(uri, projection, selection, params, null);
if (cursor != null && cursor.getCount() > 0)
{
if (cursor.moveToFirst())
{
row = cursor.getInt(0);
}
cursor.close();
}
ContentValues values = new ContentValues();
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
values.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, pic);
values.put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
if (row >= 0)
{
resolver.update(ContactsContract.Data.CONTENT_URI, values, ContactsContract.Data._ID + " = " + photoRow, null);
}
else
{
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
}
}
cursor.close();
cursor = null;
}
It turns out that it is the gmail.com sync adapter that is scaling the photo.
if I turn off
Settings->Accounts&Syncs->gmail.com->“Sync Contacts” the problem goes away.
Why does the sync adapter need to do this on a PHOTO it did not put there?
If that were not enough, it is not even very consistent with how it does it:
If the gmail.com contact does not previously have a picture on the server, when I write the PHOTO to the address book it is not scaled. The picture shows up on the server.
However, if the gmail.com contact previously had a picture on the server, when I write the contact to the address book the picture I just wrote is scaled to 96x96. Once again the picture shows up on the server.
So, either the sync adapter has a bug with processing the original picture, or has a bug with the update of a picture, because it does not always scale the photo. Either way, it should not be scaling the picture :(
Related
I have facing very critical issue. I developing call log related apps but when i getting call log from content provider i am not able to find CallLog.Calls.CACHED_PHOTO_URI. It's always returning empty. I am using VIVO 1917( Funtouch OS_10)
public static void loadData(Context context) {
mainList = new ArrayList<>();
String projection[] = {"_id", CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.DURATION, CallLog.Calls.CACHED_PHOTO_URI, CallLog.Calls.TYPE, CallLog.Calls.CACHED_NAME};
ContentResolver contentResolver = context.getApplicationContext().getContentResolver();
Cursor cursor = contentResolver.query(CallLog.Calls.CONTENT_URI, projection, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
if (cursor == null) {
Log.d("CALLLOG", "cursor is null");
return;
}
if (cursor.getCount() == 0) {
Log.d("CALLLOG", "cursor size is 0");
return;
}
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
CallLogInfo callLogInfo = new CallLogInfo();
callLogInfo.setName(cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME)));
callLogInfo.setNumber(cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER)));
callLogInfo.setCallType(cursor.getString(cursor.getColumnIndex(CallLog.Calls.TYPE)));
callLogInfo.setDate(cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE)));
callLogInfo.setDuration(cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DURATION)));
String ss = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_PHOTO_URI));
Log.e("TAG", " recent calls " + ss);
mainList.add(callLogInfo);
cursor.moveToNext();
}
cursor.close();
}
Documentation from Android:
CACHED_PHOTO_URI Added in API level 23
The cached photo URI of the picture associated with the phone number,
if it exists.
This value is typically filled in by the dialer app for the caching
purpose, so it's not guaranteed to be present, and may not be current
if the contact information associated with this number has changed.
This means depending on the Dialer App which differs from manufacturers this value may not be present.
You might have a better chance by looking up the number/name in the contacts yourself and get an image this way.
I am trying to figure out how to get the photo for a merged contact, to display in a QuickContactBadge. I've been searching and googling, and all the things I can find online say this is not possible if the contact's default image comes from a Facebook sync. However all the examples I find also reference Froyo or Gingerbread.
Is there still no way to do this in the ICS/JB age?
This answer seemed the most promising, but the comments seem to say it is hit or miss.
None of the things I've found online have worked for me.
Here is the code I currently have:
public static Uri getContactPhotoUri(long ContactId) {
Uri person = ContentUris.withAppendedId(Contacts.CONTENT_URI, ContactId);
Uri photo = Uri.withAppendedPath(person, Contacts.Photo.CONTENT_DIRECTORY);
Cursor cur = App.ContentResolver().query(
Data.CONTENT_URI,
new String[] { Data._ID },
ContactsContract.Data.CONTACT_ID
+ "="
+ ContactId
+ " AND "
+ Data.MIMETYPE
+ "='"
+ Photo.CONTENT_ITEM_TYPE
+ "'", null, Data.IS_PRIMARY + " DESC");
Uri rv = null;
rv = (cur == null || !cur.moveToFirst())? null: photo;
if (cur != null) cur.close();
return rv;
}
It shows the image properly for contacts where the image comes from the google contact.
The image does not show properly for contacts where the primary image comes from Facebook.
Is there REALLY, still, no reliable way to get the default image for a contact regardless of where the image comes from?
EDIT (01/18/2013): I've also tried querying the PHOTO_URI and PHOTO_THUMBNAIL_URI as follows, with the same results.
public static String[] GroupMembersProjection = new String[] {
Contacts._ID,
Contacts.LOOKUP_KEY,
Contacts.DISPLAY_NAME_PRIMARY,
Contacts.PHOTO_THUMBNAIL_URI
};
public static Cursor getGroupMembers(int groupid, String sort) {
String ord;
if (sort.equals("A")) { ord = Contacts.DISPLAY_NAME_PRIMARY; }
else { ord = Contacts.TIMES_CONTACTED + " DESC"; /* SORT = "U"; DEFAULT */ }
ContentResolver cr = App.ContentResolver();
Cursor contacts = cr.query(Data.CONTENT_URI,
GroupMembersProjection,
GroupMembership.GROUP_ROW_ID + "=" + groupid, null, ord);
return contacts;
}
Additionally, I tried querying PHOTO_ID instead of the PHOTO_URI fields, and then using the following code to get the URI manually and use that for the image, but this also yields the same results, showing google images, but not Facebook ones.
Uri puri = ContentUris.withAppendedId(ContactsContract.Data.CONTENT_URI, photoid);
I am trying to get the contact's phone number after I have retrieved their ID number from the built-in activity. However, whenever I query the database using the cursor in my code below -- I get zero rows returned even though there is a mobile number for the contact I have selected.
Can anyone point me in a better direction or show an example of how to get the contact's phone number AFTER getting their userID?
My code:
private Runnable getSMSRunnable() {
return new Runnable() {
public void run() {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(i, CONTACTS_REQUEST_CODE);
}
};
}
Returns the Log output
content://com.android.contacts/data/6802
From which i pass the ID (6802) into a method which is designed to return the phone number from the ID with the given type (in this case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
public static String getContactPhoneNumberByPhoneType(Context context, long contactId, int type) {
String phoneNumber = null;
String[] whereArgs = new String[] { String.valueOf(contactId), String.valueOf(type) };
Log.d(TAG, String.valueOf(contactId));
Cursor cursor = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ? and "
+ ContactsContract.CommonDataKinds.Phone.TYPE + " = ?", whereArgs, null);
int phoneNumberIndex = cursor
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER);
Log.d(TAG, String.valueOf(cursor.getCount()));
if (cursor != null) {
Log.v(TAG, "Cursor Not null");
try {
if (cursor.moveToNext()) {
Log.v(TAG, "Moved to first");
Log.v(TAG, "Cursor Moved to first and checking");
phoneNumber = cursor.getString(phoneNumberIndex);
}
} finally {
Log.v(TAG, "In finally");
cursor.close();
}
}
Log.v(TAG, "Returning phone number");
return phoneNumber;
}
Which returns null for a phone number -- which means it cannot find the row that I am trying to access -- which means that something is wrong with my query -- however if I check a contact that has a mobile phone number -- how could I get a 0 row query?
Any help would be greatly appreciated. Thank you so much!
I found the answer.
The reason I was not getting any rows from the cursor was because I was using the line
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
"The id of the row in the Contacts table that this data belongs to."
Since I was getting the URI from contacts table anyways -- this was not needed and the following should have been substituted. The ID was the one corresponding to the contact in the phone table not the raw contact.
ContactsContract.CommonDataKinds.Phone._ID
Exchanging the lines returned the correct results in the query. Everything seems to be working well at the moment.
This should work, (maybe try losing the type)
Phone numbers are stored in their own table and need to be queried separately. To query the phone number table use the URI stored in the SDK variable ContactsContract.CommonDataKinds.Phone.CONTENT_URI. Use a WHERE conditional to get the phone numbers for the specified contact.
if (Integer.parseInt(cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
// Do something with phones
}
pCur.close();
}
Perform a second query against the Android contacts SQLite database. The phone numbers are queried against the URI stored in ContactsContract.CommonDataKinds.Phone.CONTENT_URI. The contact ID is stored in the phone table as ContactsContract.CommonDataKinds.Phone.CONTACT_ID and the WHERE clause is used to limit the data returned.
in my app i am listing contacts in a listview. no of contacts is 1000+. i get the contacts
by using ContentResolver query that is cr.query(...),store the values in an arraylist
and after that load the array list in setListAdapter(...). to display the all contacts my
apps takes nearly 1 minute so that i use Async task but there is no big differences by using the async task.
i need to display all contacts within 2 to 4 seconds. i check in the default contacts
application on android simulator which is load within in 2 to 4 seconds. i have spend
long time in google. but i could not get any helpful solution. please help me how to fast the loading contacts on listview. please help me.
my coding sample:
private ArrayList<ContactListEntry> loadContactListInternal(String searchString) {
ArrayList<ContactListEntry> contactList = new ArrayList<ContactListEntry>();
ContentResolver cr = getContentResolver();
Cursor cur = null;
String[] projection = new String[] {BaseColumns._ID,ContactsContract.Contacts.DISPLAY_NAME,ContactsContract.Contacts.PHOTO_ID};
....
cur=cr.query(ContactsContract.Contacts.CONTENT_URI, projection, selection, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
while (cur.moveToNext()) {
int id = Integer.parseInt(cur.getString(0));
....
if (input !=null)
photo = BitmapFactory.decodeStream(input);
....
ArrayList<ContactListEntry.PhoneEntry> phoneEntries = new ArrayList<ContactListEntry.PhoneEntry>();
String[] projection1 = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER,ContactsContract.CommonDataKinds.Phone.TYPE};
Cursor pcur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,projection1, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { String.valueOf(id) }, null);
while (pcur.moveToNext()) {
...
}
pcur.close();
ContactListEntry entry = new ContactListEntry(id, name, photo, phoneEntries);
contactList.add(entry);
}
cur.close();
return contactList;
}
.....
in another class
private void selectionUpdated() {
....
setListAdapter(new SelectedArrayAdapter(this, app.selectedContacts));
...
}
Use the Concept of projections and selection arguments to retrive the contacts in my case for 500 contacts intially it was taking 12 sec.
Now it is taking 350ms(lessthan second)
void getAllContacts() {
long startnow;
long endnow;
startnow = android.os.SystemClock.uptimeMillis();
ArrayList arrContacts = new ArrayList();
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
Cursor cursor = ctx.getContentResolver().query(uri, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Contacts._ID}, selection, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
cursor.moveToFirst();
while (cursor.isAfterLast() == false) {
String contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
int phoneContactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
int contactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Log.d("con ", "name " + contactName + " " + " PhoeContactID " + phoneContactID + " ContactID " + contactID)
cursor.moveToNext();
}
cursor.close();
cursor = null;
endnow = android.os.SystemClock.uptimeMillis();
Log.d("END", "TimeForContacts " + (endnow - startnow) + " ms");
}
More information on this link http://www.blazin.in/2016/02/loading-contacts-fast-from-android.html ....
So your problem is that you do a lot of subqueries for each contact. I has the same issue once upon a time. My case was that I showed many contacts and allowed the user to click on any of them. After that I started processing the contact in another activity.
Back then I finally decided that I should display only the name and lazily fetch all the other data later on, just before I launch the next activity. This was amazing: decreased the speed of my program almost by a factor of 200, and the operations became completely invisible to the user.
The default android list does the same if I am not wrong - it displays only the name, and later on loads all the other contact-related data.
i use cursor adapter and i cut the databse,arrayadapter and optimize the code.
Consider having just one query and getting rid of the sub query idea (as already suggested). You can achieve speed by just querying using the Content Uri:
"ContactsContract.CommonDataKinds.Phone.CONTENT_URI"
This URI also has the "ContactsContract.Contacts.DISPLAY_NAME" field.
You might also want to consider doing this query and working with your adapter in a seperate thread to make it completely transparent.
This worked for me.
OPTIMIZED SOLUTION HERE.....
private static final String[] PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
};
.
.
.
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
if (cursor != null) {
try {
final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String name, number;
while (cursor.moveToNext()) {
name = cursor.getString(nameIndex);
number = cursor.getString(numberIndex);
}
} finally {
cursor.close();
}
}
CHEERS...:)
I am trying to get the contact's phone number after I have retrieved their ID number from the built-in activity. However, whenever I query the database using the cursor in my code below -- I get zero rows returned even though there is a mobile number for the contact I have selected.
Can anyone point me in a better direction or show an example of how to get the contact's phone number AFTER getting their userID?
My code:
private Runnable getSMSRunnable() {
return new Runnable() {
public void run() {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(i, CONTACTS_REQUEST_CODE);
}
};
}
Returns the Log output
content://com.android.contacts/data/6802
From which i pass the ID (6802) into a method which is designed to return the phone number from the ID with the given type (in this case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
public static String getContactPhoneNumberByPhoneType(Context context, long contactId, int type) {
String phoneNumber = null;
String[] whereArgs = new String[] { String.valueOf(contactId), String.valueOf(type) };
Log.d(TAG, String.valueOf(contactId));
Cursor cursor = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ? and "
+ ContactsContract.CommonDataKinds.Phone.TYPE + " = ?", whereArgs, null);
int phoneNumberIndex = cursor
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER);
Log.d(TAG, String.valueOf(cursor.getCount()));
if (cursor != null) {
Log.v(TAG, "Cursor Not null");
try {
if (cursor.moveToNext()) {
Log.v(TAG, "Moved to first");
Log.v(TAG, "Cursor Moved to first and checking");
phoneNumber = cursor.getString(phoneNumberIndex);
}
} finally {
Log.v(TAG, "In finally");
cursor.close();
}
}
Log.v(TAG, "Returning phone number");
return phoneNumber;
}
Which returns null for a phone number -- which means it cannot find the row that I am trying to access -- which means that something is wrong with my query -- however if I check a contact that has a mobile phone number -- how could I get a 0 row query?
Any help would be greatly appreciated. Thank you so much!
I found the answer.
The reason I was not getting any rows from the cursor was because I was using the line
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
"The id of the row in the Contacts table that this data belongs to."
Since I was getting the URI from contacts table anyways -- this was not needed and the following should have been substituted. The ID was the one corresponding to the contact in the phone table not the raw contact.
ContactsContract.CommonDataKinds.Phone._ID
Exchanging the lines returned the correct results in the query. Everything seems to be working well at the moment.
This should work, (maybe try losing the type)
Phone numbers are stored in their own table and need to be queried separately. To query the phone number table use the URI stored in the SDK variable ContactsContract.CommonDataKinds.Phone.CONTENT_URI. Use a WHERE conditional to get the phone numbers for the specified contact.
if (Integer.parseInt(cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
// Do something with phones
}
pCur.close();
}
Perform a second query against the Android contacts SQLite database. The phone numbers are queried against the URI stored in ContactsContract.CommonDataKinds.Phone.CONTENT_URI. The contact ID is stored in the phone table as ContactsContract.CommonDataKinds.Phone.CONTACT_ID and the WHERE clause is used to limit the data returned.