I'm using the below code:
int indexContactId = people.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
/*......*/
int id = Integer.parseInt(people.getString(indexContactId));
Cursor name_cursor = ctx.getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, ContactsContract.Data.CONTACT_ID + " = " + id, null, null);
String first_name = "";
String last_name = "";
while (name_cursor.moveToNext()) {
if (name_cursor.getString(name_cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)) != null) {
first_name = name_cursor.getString(name_cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
last_name = name_cursor.getString(name_cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
}
}
name_cursor.close();
to get the first and last name of the contact (separately).
Alongside this, I'm also retrieving the DISPLAY_NAME
int indexName = people.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
The problem is that for some of the contacts, I get values like:
DISPLAY_NAME: Franck MILEN
FAMILY_NAME: 0
GIVEN_NAME: Skype video
call
What's going on with the Skype call thing ? And how can I retrieve the proper MILEN name ? Or at least know to ignore these, as Franck MILEN is also returned with proper names.
EDIT
What I need is a better way to get the first_name, last_name, and phone_number of a user (first and last name should be separate) and not get or ignore the Skype video call things :)
Seems I need to answer my own question. Here's the code I'm using, and it seems to work, it doesn't return the Skype contacts anymore:
int id = Integer.parseInt(people.getString(indexContactId));
String[] projectionNames = new String[]{ContactsContract.Data.DATA2, ContactsContract.Data.DATA3};
String where = ContactsContract.Data.CONTACT_ID + " = " + id
+ " AND "
+ ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'";
Uri uriNames = ContactsContract.Data.CONTENT_URI;
Cursor name_cursor = ctx.getContentResolver().query(uriNames, projectionNames, where, null, null);
String first_name = "";
String last_name = "";
while (name_cursor.moveToNext()) {
first_name = name_cursor.getString(name_cursor.getColumnIndex(ContactsContract.Data.DATA2));
last_name = name_cursor.getString(name_cursor.getColumnIndex(ContactsContract.Data.DATA3));
}
name_cursor.close();
name_cursor = null;
Related
I am using this code to get phone contacts. I am getting multiple names based on how many mobile/work numbers I have entered . I basically want only one contact id with last edited/added mobile number .Is there a way I can edit the following query to achieve that result ? I really appreciate any help.
Cursor cur = managedQuery(ContactsContract.Data.CONTENT_URI, null, ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'", null,
ContactsContract.Data.DISPLAY_NAME + " ASC");
int number = mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int name = mCursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
while (mCursor.moveToNext()) {
String phName = mCursor.getString(name);
String phNumber = mCursor.getString(number);
String thumbnailUri = mCursor.getString(mCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI));
}
try using the modified date
Cursor cur = managedQuery(ContactsContract.Data.CONTENT_URI, null, ContactsContract.Contacts.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'", null, ContactsContract.Data.CONTACT_LAST_UPDATED_TIMESTAMP + " DESC");
int _id = mCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
int number = mCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int name = mCursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME);
while (mCursor.moveToNext()) {
int id = mCursor.getString(_id);
String phName = mCursor.getString(name);
String phNumber = mCursor.getString(number);
String thumbnailUri = mCursor.getString(mCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI));
}
check you queue. If your id are same for similar name contacts then group them. Let me know after trying this
I'm trying to learn how to use database in Android and so far I have learned how to fetch all rows of data in the table, but I'm not sure how I'm going to use a SQL query to search for a value that is equal to the string value I pass to the method below?
I wonder if someone could add some simple code how to query the database with SQL in my code? Preciate the help! Thanks!
Something like this: String query = "Select * from DB_TABLE where TABLE_IMAGE_PATH = filePath";
// Read from database
public String readContacts(String filePath){
String[] columns = new String[]{TABLE_ID, TABLE_IMAGE_PATH, TABLE_CONTACT_NAME, TABLE_CONTACT_NUMBER, TABLE_CONTACT_ADDRESS};
Cursor c = db.query(DB_TABLE, columns, null, null, null, null, null);
int id = c.getColumnIndex(TABLE_ID);
int imagePath = c.getColumnIndex(TABLE_IMAGE_PATH);
int name = c.getColumnIndex(TABLE_CONTACT_NAME);
int number = c.getColumnIndex(TABLE_CONTACT_NUMBER);
int address = c.getColumnIndex(TABLE_CONTACT_ADDRESS);
String result = "";
for(c.moveToFirst(); !c.isAfterLast(); c.moveToNext()){
result = result + c.getString(id) + " " + c.getString(imagePath) + " " + c.getString(name) + " " + c.getString(number) + " " + c.getString(address);
}
return result;
}
You can search Query
its for exact string
String query = "Select * from DB_TABLE where TABLE_IMAGE_PATH like '"+filePath+"';
if value contains your string
String query = "Select * from DB_TABLE where TABLE_IMAGE_PATH like '%"+filePath+"%';
if value needs from last
String query = "Select * from DB_TABLE where TABLE_IMAGE_PATH like '%"+filePath+"';
if value needs from first
String query = "Select * from DB_TABLE where TABLE_IMAGE_PATH like '"+filePath+"%';
put these
SQLiteDatabase dbR = this.getReadableDatabase();
Cursor cur = dbR.rawQuery("query", null);
return cur
using this cursor you can get value.
you can do that easily by modifying your query code to:
String[] columns = new String[]{TABLE_ID, TABLE_IMAGE_PATH, TABLE_CONTACT_NAME, TABLE_CONTACT_NUMBER, TABLE_CONTACT_ADDRESS};
Cursor cursor = db.query(ANSWERS_TABLE,
columns,
columns[1] + " = ?", new String[]{filePath}, null, null, null);
Take a look here : SQLiteDataBase Query
I'm building an app that needs to pick a contact from Android contacts. I do it and I use the following code in my app for retrieve name and family name form chosen contact (in onActivityResult).
Cursor namesc = getContentResolver().query( ContactsContract.Data.CONTENT_URI, null, ContactsContract.Data.CONTACT_ID + " = " + id,null, null);
if (namesc.moveToFirst()) {
String name = namesc.getString(namesc.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String family = namesc.getString(namesc
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
String middle = namesc.getString(namesc
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
It works well in HTC phones and some other phones but in many of SE Xperia models and some Samsung devices it can't give me name and family and returns null. Can anybody help me?
Here is the code I use:
String structuredNameWhere = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] structuredNameWhereParams = new String[]{id, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE};
cursor = resolver.query(ContactsContract.Data.CONTENT_URI, null, structuredNameWhere, structuredNameWhereParams, null);
if (cursor.moveToFirst())
{
String StructuredNameId = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName._ID));
String prefix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PREFIX));
String givenName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String middleName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
String familyName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
String suffix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.SUFFIX));
}
The only difference I really see is I added mime-type to the where parameters and I am retrieving data slightly differently.
Android Contacts has just been REBORN! Using the Contacts, Reborn API, the answer to the original question,
I use the following code in my app for retrieve name and family name form chosen contact... it can't give me name and family and returns null. Can anybody help me?
is the following...
In Java,
public void printContactFullName(Context context, Long contactId) {
Contacts contactsApi = ContactsFactory.create(context);
Contact contact = contactsApi
.query()
.include(Fields.Name.getAll())
.where(WhereKt.equalTo(Fields.Contact.Id, contactId))
.find()
.get(0);
// Assuming this contact only have 1 RawContact...
Name name = ContactDataKt.nameList(contact).get(0);
Log.d(
"Contact",
"Prefix: " + name.getPrefix() +
", Given name: " + name.getGivenName() +
", Middle name: " + name.getMiddleName() +
", Family name: " + name.getFamilyName() +
", Suffix: " + name.getSuffix()
);
}
In Kotlin,
fun printContactFullName(context: Context, contactId: Long) {
Contacts(context)
.query()
.include(Fields.Name.all)
.where(Fields.Contact.Id equalTo contactId)
.find()
.first().let { contact ->
// Assuming this contact only have 1 RawContact...
val name = contact.names().first()
Log.d(
"Contact",
"""
Prefix: ${name.prefix}
Given name: ${name.givenName}
Middle name: ${name.middleName}
Family name: ${name.familyName}
Suffix: ${name.suffix}
""".trimIndent()
)
}
}
Do keep in mind, however, that not all Contacts have to have proper names. Users can create contacts without a name. This is the behavior in the native Contacts app.
You can do a lot more than this using the Contacts, Reborn API. You can even build your own full-fledged Contacts application that can stand toe-to-toe with the one that comes with almost every copy of Android.
/* Return all contacts and show each contact data in android monitor console as debug info. */ private List<ContactDTO> getAllContacts() {
List<ContactDTO> ret = new ArrayList<ContactDTO>();
// Get all raw contacts id list.
List<Integer> rawContactsIdList = getRawContactsIdList();
int contactListSize = rawContactsIdList.size();
ContentResolver contentResolver = getContentResolver();
// Loop in the raw contacts list.
for(int i=0;i<contactListSize;i++)
{
// Get the raw contact id.
Integer rawContactId = rawContactsIdList.get(i);
Log.d(TAG_ANDROID_CONTACTS, "raw contact id : " + rawContactId.intValue());
// Data content uri (access data table. )
Uri dataContentUri = ContactsContract.Data.CONTENT_URI;
// Build query columns name array.
List<String> queryColumnList = new ArrayList<String>();
// ContactsContract.Data.CONTACT_ID = "contact_id";
queryColumnList.add(ContactsContract.Data.CONTACT_ID);
// ContactsContract.Data.MIMETYPE = "mimetype";
queryColumnList.add(ContactsContract.Data.MIMETYPE);
queryColumnList.add(ContactsContract.Data.DATA1);
queryColumnList.add(ContactsContract.Data.DATA2);
queryColumnList.add(ContactsContract.Data.DATA3);
queryColumnList.add(ContactsContract.Data.DATA4);
queryColumnList.add(ContactsContract.Data.DATA5);
queryColumnList.add(ContactsContract.Data.DATA6);
queryColumnList.add(ContactsContract.Data.DATA7);
queryColumnList.add(ContactsContract.Data.DATA8);
queryColumnList.add(ContactsContract.Data.DATA9);
queryColumnList.add(ContactsContract.Data.DATA10);
queryColumnList.add(ContactsContract.Data.DATA11);
queryColumnList.add(ContactsContract.Data.DATA12);
queryColumnList.add(ContactsContract.Data.DATA13);
queryColumnList.add(ContactsContract.Data.DATA14);
queryColumnList.add(ContactsContract.Data.DATA15);
// Translate column name list to array.
String queryColumnArr[] = queryColumnList.toArray(new String[queryColumnList.size()]);
// Build query condition string. Query rows by contact id.
StringBuffer whereClauseBuf = new StringBuffer();
whereClauseBuf.append(ContactsContract.Data.RAW_CONTACT_ID);
whereClauseBuf.append("=");
whereClauseBuf.append(rawContactId);
// Query data table and return related contact data.
Cursor cursor = contentResolver.query(dataContentUri, queryColumnArr, whereClauseBuf.toString(), null, null);
/* If this cursor return database table row data.
If do not check cursor.getCount() then it will throw error
android.database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0.
*/
if(cursor!=null && cursor.getCount() > 0)
{
StringBuffer lineBuf = new StringBuffer();
cursor.moveToFirst();
lineBuf.append("Raw Contact Id : ");
lineBuf.append(rawContactId);
long contactId = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID));
lineBuf.append(" , Contact Id : ");
lineBuf.append(contactId);
do{
// First get mimetype column value.
String mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE));
lineBuf.append(" \r\n , MimeType : ");
lineBuf.append(mimeType);
List<String> dataValueList = getColumnValueByMimetype(cursor, mimeType);
int dataValueListSize = dataValueList.size();
for(int j=0;j < dataValueListSize;j++)
{
String dataValue = dataValueList.get(j);
lineBuf.append(" , ");
lineBuf.append(dataValue);
}
}while(cursor.moveToNext());
Log.d(TAG_ANDROID_CONTACTS, lineBuf.toString());
}
Log.d(TAG_ANDROID_CONTACTS, "=========================================================================");
}
return ret; }
I want to fetch All Contacts atleast with one phone Number, also I want all Phone Numbers and All emails for every Contact.
Current code :
// To get All Contacts having atleast one phone number.
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " > ?";
String[] selectionArgs = new String[] {"0"};
Cursor cu = applicationContext.getContentResolver().query(uri,
null, selection, selectionArgs, null);
// For getting All Phone Numbers and Emails further queries :
while(cu.moveToNext()){
String id = cu.getString(cu.getColumnIndex(ContactsContract.Contacts._ID));
// To get Phone Numbers of Contact
Cursor pCur = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
new String[]{id}, null);
// To get Email ids of Contact
Cursor emailCur = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
new String[]{id}, null);
// Iterate through these cursors to get Phone numbers and Emails
}
If there are more than 1000 contacts in my Device, it is taking too much time. How can I get All Data in single query, rather than doing two additional queries for each contact?
Or is there any other way to optimize?
Thank you in Advance.
ICS: When you query from Data.CONTENT_URI you have all the rows from the associated Contact already joined - i.e. this would work:
ContentResolver resolver = getContentResolver();
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
Data.HAS_PHONE_NUMBER + "!=0 AND (" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?)",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID);
while (c.moveToNext()) {
long id = c.getLong(c.getColumnIndex(Data.CONTACT_ID));
String name = c.getString(c.getColumnIndex(Data.DISPLAY_NAME));
String data1 = c.getString(c.getColumnIndex(Data.DATA1));
System.out.println(id + ", name=" + name + ", data1=" + data1);
}
If you are targeting 2.3 you need to account for the fact that HAS_PHONE_NUMBER is not available through the joins used when querying Data.
Fun.
This could, for instance, be solved either by skipping your requirement that the contact must have a phone number and instead settle for "any contact with at least a phone number or an e-mail address":
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID);
If that is not an option you can always go for a horribly hacky sub-select:
Cursor c = resolver.query(
Data.CONTENT_URI,
null,
"(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?) AND " +
Data.CONTACT_ID + " IN (SELECT " + Contacts._ID + " FROM contacts WHERE " + Contacts.HAS_PHONE_NUMBER + "!=0)",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE}, Data.CONTACT_ID);
or solve it by using two Cursors:
Cursor contacts = resolver.query(Contacts.CONTENT_URI,
null, Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID + " ASC");
Cursor data = resolver.query(Data.CONTENT_URI, null,
Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=?",
new String[]{Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID + " ASC");
int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
boolean hasData = data.moveToNext();
while (contacts.moveToNext()) {
long id = contacts.getLong(idIndex);
System.out.println("Contact(" + id + "): " + contacts.getString(nameIndex));
if (hasData) {
long cid = data.getLong(cidIndex);
while (cid <= id && hasData) {
if (cid == id) {
System.out.println("\t(" + cid + "/" + id + ").data1:" +
data.getString(data1Index));
}
hasData = data.moveToNext();
if (hasData) {
cid = data.getLong(cidIndex);
}
}
}
}
I went through the exact same problem. Since then I build my own solution which is inspired from this post yet a bit different. Now I'd like to share it as my first StackOverFlow answer :-)
Its quite similar to the double cursor approach suggested by Jens. The idea is to
1- fetch relevant contact from the Contacts table
2- fetch relevant Contacts information (mail, phone, ...)
3- combine these results
The "relevant" is up to you of course but the important point is the performance !
Besides, I'm sure other solutions using well suited SQL query might as well do the job but here I only want to use the Android ContentProvider
Here is the code :
Some constants
public static String CONTACT_ID_URI = ContactsContract.Contacts._ID;
public static String DATA_CONTACT_ID_URI = ContactsContract.Data.CONTACT_ID;
public static String MIMETYPE_URI = ContactsContract.Data.MIMETYPE;
public static String EMAIL_URI = ContactsContract.CommonDataKinds.Email.DATA;
public static String PHONE_URI = ContactsContract.CommonDataKinds.Phone.DATA;
public static String NAME_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME;
public static String PICTURE_URI = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ? ContactsContract.Contacts.PHOTO_THUMBNAIL_URI : ContactsContract.Contacts.PHOTO_ID;
public static String MAIL_TYPE = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
public static String PHONE_TYPE = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
1 Contact
Here I require that the Contacts must have DISPLAY_NAME free of "#" and that their informations match a given string (these requirement can of course be modified). The result of the following method is the first cursor :
public Cursor getContactCursor(String stringQuery, String sortOrder) {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactCursor search has started...");
Long t0 = System.currentTimeMillis();
Uri CONTENT_URI;
if (stringQuery == null)
CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
else
CONTENT_URI = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(stringQuery));
String[] PROJECTION = new String[]{
CONTACT_ID_URI,
NAME_URI,
PICTURE_URI
};
String SELECTION = NAME_URI + " NOT LIKE ?";
String[] SELECTION_ARGS = new String[]{"%" + "#" + "%"};
Cursor cursor = sContext.getContentResolver().query(CONTENT_URI, PROJECTION, SELECTION, SELECTION_ARGS, sortOrder);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
This query is quite performant as you'll see !
2 Contact Details
Now let's fetch Contact informations. At this point, I dont make any link between the already fetched Contact and the retrieved information : I just fetch all informations form the Data table... Yet, to avoid useless info I still require DISPLAY_NAMES free of "#" and since I'm interested in email and phone I require that the data MIMETYPE to be either MAIL_TYPE or PHONE_TYPE (see Constants). Here is the code :
public Cursor getContactDetailsCursor() {
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
Logger.e(TAG, "ContactDetailsCursor search has started...");
Long t0 = System.currentTimeMillis();
String[] PROJECTION = new String[]{
DATA_CONTACT_ID_URI,
MIMETYPE_URI,
EMAIL_URI,
PHONE_URI
};
String SELECTION = ContactManager.NAME_URI + " NOT LIKE ?" + " AND " + "(" + MIMETYPE_URI + "=? " + " OR " + MIMETYPE_URI + "=? " + ")";
String[] SELECTION_ARGS = new String[]{"%" + "#" + "%", ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
Cursor cursor = sContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
SELECTION_ARGS,
null);
Long t1 = System.currentTimeMillis();
Logger.e(TAG, "ContactDetailsCursor finished in " + (t1 - t0) / 1000 + " secs");
Logger.e(TAG, "ContactDetailsCursor found " + cursor.getCount() + " contacts");
Logger.i(TAG, "+++++++++++++++++++++++++++++++++++++++++++++++++++");
return cursor;
}
Once again you will see that this query is quite fast...
3 Combining
Now let's combine both Contact and their respective informations. The idea is to use HashMap(Key, String) where Key is the Contact id and String is whatever you like (name, email, ...).
First, I run through the Contact cursor (which is alphabetically ordered) and store names and picture uri in two different HashMap. Note also that I store all Contact id's in a List in the very same order that Contacts appear in the cursor. Lets call this list contactListId
I do the same for the Contact informations (mail and email). But now I take care of the correlation between the two cursor : if the CONTACT_ID of an email or phone does not appear in contactListId it is put aside. I check also if the email has already been encountered. Notice that this further selection can introduce asymmetries between the Name/Picture content and the Email/Phone HashMap content but don't worry.
Eventually, I run over the contactListId list and build a list of Contact object taking care of the fact that : a contact must have information (keySet condition) and that the contact must have at least a mail or an email (the case where mail == null && phone == null may appear if the contact is a Skype contact for instance).
And here is the code :
public List<Contact> getDetailedContactList(String queryString) {
/**
* First we fetch the contacts name and picture uri in alphabetical order for
* display purpose and store these data in HashMap.
*/
Cursor contactCursor = getContactCursor(queryString, NAME_URI);
List<Integer> contactIds = new ArrayList<>();
if (contactCursor.moveToFirst()) {
do {
contactIds.add(contactCursor.getInt(contactCursor.getColumnIndex(CONTACT_ID_URI)));
} while (contactCursor.moveToNext());
}
HashMap<Integer, String> nameMap = new HashMap<>();
HashMap<Integer, String> pictureMap = new HashMap<>();
int idIdx = contactCursor.getColumnIndex(CONTACT_ID_URI);
int nameIdx = contactCursor.getColumnIndex(NAME_URI);
int pictureIdx = contactCursor.getColumnIndex(PICTURE_URI);
if (contactCursor.moveToFirst()) {
do {
nameMap.put(contactCursor.getInt(idIdx), contactCursor.getString(nameIdx));
pictureMap.put(contactCursor.getInt(idIdx), contactCursor.getString(pictureIdx));
} while (contactCursor.moveToNext());
}
/**
* Then we get the remaining contact information. Here email and phone
*/
Cursor detailsCursor = getContactDetailsCursor();
HashMap<Integer, String> emailMap = new HashMap<>();
HashMap<Integer, String> phoneMap = new HashMap<>();
idIdx = detailsCursor.getColumnIndex(DATA_CONTACT_ID_URI);
int mimeIdx = detailsCursor.getColumnIndex(MIMETYPE_URI);
int mailIdx = detailsCursor.getColumnIndex(EMAIL_URI);
int phoneIdx = detailsCursor.getColumnIndex(PHONE_URI);
String mailString;
String phoneString;
if (detailsCursor.moveToFirst()) {
do {
/**
* We forget all details which are not correlated with the contact list
*/
if (!contactIds.contains(detailsCursor.getInt(idIdx))) {
continue;
}
if(detailsCursor.getString(mimeIdx).equals(MAIL_TYPE)){
mailString = detailsCursor.getString(mailIdx);
/**
* We remove all double contact having the same email address
*/
if(!emailMap.containsValue(mailString.toLowerCase()))
emailMap.put(detailsCursor.getInt(idIdx), mailString.toLowerCase());
} else {
phoneString = detailsCursor.getString(phoneIdx);
phoneMap.put(detailsCursor.getInt(idIdx), phoneString);
}
} while (detailsCursor.moveToNext());
}
contactCursor.close();
detailsCursor.close();
/**
* Finally the contact list is build up
*/
List<Contact> contacts = new ArrayList<>();
Set<Integer> detailsKeySet = emailMap.keySet();
for (Integer key : contactIds) {
if(!detailsKeySet.contains(key) || (emailMap.get(key) == null && phoneMap.get(key) == null))
continue;
contacts.add(new Contact(String.valueOf(key), pictureMap.get(key), nameMap.get(key), emailMap.get(key), phoneMap.get(key)));
}
return contacts;
}
The Contact object definition is up to you.
Hope this will help and thanks for the previous post.
Correction/Improvement
I forgot to check the phone key set : it should rather looks like
!mailKeySet.contains(key)
replaced by
(!mailKeySet.contains(key) && !phoneKeySet.contains(key))
with the phone keySet
Set<Integer> phoneKeySet = phoneMap.keySet();
I why not add an empty contact cursor check like :
if(contactCursor.getCount() == 0){
contactCursor.close();
return new ArrayList<>();
}
right after the getContactCursor call
I have this cuestion for you...
We are trying to pass an argument to a query in a content resolver. We want to get all rows if the argument is NULL, or get the specific value indicated at the argument as contrary.
The method we use declares:
static String allArtists = "";
////
Some code
...
Vector<String> discs = new Vector<String>();
discs = getDiscs(allArtists);
////
public Vector<String> getDiscs(String artistName){
So, the query we made is as follows:
Uri discsUri = MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
ContentResolver contRes = getContentResolver();
String[] queryColumns = { MediaStore.Audio.Albums._ID,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Albums.ARTIST };
String selectionQuery = "? = ?";
String [] selectionArgs = { MediaStore.Audio.Albums.ARTIST, artistName };
String sortOrderClause = MediaStore.Audio.Albums.ALBUM + " ASC";
Cursor queryRes = contRes.query(discsUri,
queryColumns,
selectionQuery,
selectionArgs,
sortOrderClause
);
In order to make this method reusable, we pass artistName as argument, and because we want all rows to be selected, we pass the constant allArtists.
The point is that this query returns no rows, because the constant allArtists = "" doesnt act as the null value as we expected.
The question finally is: How must I declare the constant in a way that the query takes it as a NULL value instead of ""?.
Thanks a lot.
Turn empty strings into null
String selectionQuery = "? = nullif(?,'''')";
We resolved our problem making the selection this way:
String[] queryColumns = { MediaStore.Audio.Albums._ID,
MediaStore.Audio.Albums.ALBUM,
MediaStore.Audio.Albums.ARTIST };
String selectionQuery = "( nullif('" + artistName + "','') ISNULL )" +
" OR (" + MediaStore.Audio.Albums.ARTIST + " == '" + artistName + "' )";
String sortOrderClause = MediaStore.Audio.Albums.ALBUM + " ASC";
Cursor queryRes = contRes.query(discsUri,
queryColumns,
selectionQuery,
null,
sortOrderClause);
Hope this helps anyone else.
Greetings!