Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loading multiple contacts with Xamarin.Contacts.AddressBook

I want to load several contacts via Xamarin.Contacts.AddressBook, at the moment I have something like:

var loookupIDs = /* load 10 saved contact IDs */
var addressBook = new AddressBook(context) { PreferContactAggregation = true };

foreach(var id in loookupIDs)
{
    var contact = addressBook.Load(id);
    names.Add(contact.DisplayName);
}

However, this is really slow (tested on Android device) - even just loading 10 contacts. Is there a way to batch up the loading so it's faster? Or is the only option to use platform specific APIs instead of the Xamarin wrapper.

like image 664
Wilka Avatar asked Feb 12 '23 09:02

Wilka


1 Answers

Yes, Xamarin.Mobile is kind of slow. It combines all possible contacts (phones, mails, etc) and all possible fields, which is not recommended by Android reference manual.

I recommend you to use native way to query your contacts with Cursor and filter it for your needs. Sadly, Xamarin dev mixed up all constants, so it is not trivial task.

Here is complete example

    public class PhoneContactInfo
    {
        public string PhoneContactID { get; set; }
        public string ContactName { get; set; }
        public string ContactNumber { get; set; }
    }

    public IEnumerable<PhoneContactInfo> GetAllPhoneContacts(IEnumerable<int> filterIds = null)
    {
        Log.Debug("GetAllPhoneContacts", "Getting all Contacts");
        var arrContacts = new System.Collections.Generic.List<PhoneContactInfo>();
        PhoneContactInfo phoneContactInfo = null;
        var uri = ContactsContract.CommonDataKinds.Phone.ContentUri;

        string[] projection = { ContactsContract.Contacts.InterfaceConsts.Id, 
                                ContactsContract.Contacts.InterfaceConsts.DisplayName,
                                ContactsContract.CommonDataKinds.Phone.Number
                              };

        //String[] strings = filterIds.Select(k => Convert.ToString(k)).ToArray();
        //string whereClause = ContactsContract.Contacts.InterfaceConsts.Id + " = ? ";

        var cursor = MainActivity.ContextHolder.ContentResolver.Query(uri, projection,
                                null,
                                null,
                                null);

        cursor.MoveToFirst();

        while (cursor.IsAfterLast == false)
        {
            int phoneContactID = cursor.GetInt(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.Id));

            if (filterIds.Contains(phoneContactID))                
            {
                String contactNumber = cursor.GetString(cursor.GetColumnIndex(ContactsContract.CommonDataKinds.Phone.Number));
                String contactName = cursor.GetString(cursor.GetColumnIndex(ContactsContract.Contacts.InterfaceConsts.DisplayName));

                phoneContactInfo = new PhoneContactInfo()
                {
                    PhoneContactID = Convert.ToString(phoneContactID),
                    ContactName = contactName,
                    ContactNumber = contactNumber
                };

                arrContacts.Add(phoneContactInfo);
            }
            cursor.MoveToNext();
        }
        cursor.Close();
        cursor = null;
        Log.Debug("GetAllPhoneContacts", "Got all Contacts");
        return arrContacts;
    }

If you wish to add some fancy async

public Task<IEnumerable<PhoneContactInfo>> GetAllPhoneContactsAsync(IEnumerable<int> filterIds)
{
    return Task.FromResult(GetAllPhoneContacts(filterIds));
}

Also take a look at commented whereClause. You possibly can construct 'SQL like' where clause to make this query even more faster. Just build a string with several '=' and 'or'

P.S. I didn't measure performance differences, if anyone has decent statistics i will be grateful

like image 145
xakpc Avatar answered Feb 15 '23 11:02

xakpc