Is there a unique identifier for Android phone contacts? - android

I have an app that reads the contact details of the phone. This code returns 744 as the id of a particular contact's row when accessed through Email.ContentUri.
var uriEmail = ContactsContract.CommonDataKinds.Email.ContentUri;
string[] projectionEmail = { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName, ContactsContract.Contacts.InterfaceConsts.PhotoUri, ContactsContract.CommonDataKinds.Email.Address };
var cursorEmail = this.Activity.ContentResolver.Query(uriEmail, projectionEmail, null, null, null);
// var contactList = new List<string>();
contacts = new ObservableCollection<Contact>();
if (cursorEmail.MoveToFirst())
{
do
{
//contactList.Add(cursor.GetString(cursor.GetColumnIndex(projection[2])));
contacts.Add(new Contact()
{
Id = cursorEmail.GetInt(cursorEmail.GetColumnIndex(projectionEmail[0])),
Name = cursorEmail.GetString(cursorEmail.GetColumnIndex(projectionEmail[1])),
Photo = cursorEmail.GetString(cursorEmail.GetColumnIndex(projectionEmail[2])),
Email = cursorEmail.GetString(cursorEmail.GetColumnIndex(projectionEmail[3])),
});
}
while (cursorEmail.MoveToNext());
}
ListView listEmail = v.FindViewById<ListView>(Resource.Id.listViewSelect);
listEmail.Adapter = new ContactAdapter(v.Context, contacts);
listEmail.ItemClick += OnClientListClick;
This code returns 752 as the id of the same contact when accessed through StructuredPostal.ContentUri.
var uriAddress = ContactsContract.CommonDataKinds.StructuredPostal.ContentUri;
string[] projectionAddress = { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.Contacts.InterfaceConsts.DisplayName, ContactsContract.Contacts.InterfaceConsts.PhotoUri, ContactsContract.CommonDataKinds.StructuredPostal.Street, ContactsContract.CommonDataKinds.StructuredPostal.Postcode };
var cursorAddress = this.Activity.ContentResolver.Query(uriAddress, projectionAddress, null, null, null);
// var contactList = new List<string>();
properties = new ObservableCollection<Property>();
if (cursorAddress.MoveToFirst())
{
do
{
int n = cursorAddress.GetInt(cursorAddress.GetColumnIndex(projectionAddress[0]));
string str = cursorAddress.GetString(cursorAddress.GetColumnIndex(projectionAddress[1]));
if (n == nId)
{
//contactList.Add(cursor.GetString(cursor.GetColumnIndex(projection[2])));
properties.Add(new Property()
{
Id = cursorAddress.GetInt(cursorAddress.GetColumnIndex(projectionAddress[0])),
Name = cursorAddress.GetString(cursorAddress.GetColumnIndex(projectionAddress[1])),
Photo = cursorAddress.GetString(cursorAddress.GetColumnIndex(projectionAddress[2])),
Street = cursorAddress.GetString(cursorAddress.GetColumnIndex(projectionAddress[3])),
Postcode = cursorAddress.GetString(cursorAddress.GetColumnIndex(projectionAddress[4])),
});
}
}
while (cursorAddress.MoveToNext());
}
ListView listAddress = v.FindViewById<ListView>(Resource.Id.listViewSelect);
listAddress.Adapter = new PropertyAdapter(v.Context, properties);
listAddress.ItemClick += OnPropertyListClick;
Is there a unique identifier that's allocated to the contact in the Android phone?

If you wanna a unique id, you could use CONTACT_ID which is a reference to _ID of each contact.
CONTACT_ID:
https://developer.android.com/reference/android/provider/ContactsContract.RawContactsColumns.html#CONTACT_ID
_ID:https://developer.android.com/reference/android/provider/BaseColumns#_ID
If you want to use the unique id cross device, you could try to use the LOOKUP_KEY.
LOOKUP_KEY:
https://developer.android.google.cn/reference/android/provider/ContactsContract.ContactsColumns.html#LOOKUP_KEY
For more code details, you could check the link below. Get cross-device unique ID for Android phone contacts

Related

can't retrieve user profile

I want to retrieve a user's profile and it's image, but this is not working. I always get an empty cursor (cursor.getCount() == 0). Can someone help?
I have a profile with an image and a phone number on my phone but I can't read it. Permissions (read and write contacts permissions) are granted and I can retrieve all my phone contacts, but not the own profile.
Any ideas?
Code
void loadUser() {
Uri dataUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
String[] selection = new String[]
{
ContactsContract.Data.RAW_CONTACT_ID,
ContactsContract.Data._ID,
ContactsContract.Profile.DISPLAY_NAME,
ContactsContract.Profile.PHOTO_URI,
ContactsContract.Profile.LOOKUP_KEY,
ContactsContract.Data.DATA_VERSION
};
Cursor cursor = MainApp.get().getContentResolver().query(
dataUri,
selection,
null,
null,
null);
if (cursor != null) {
L.d("MY PROFILE - cursor size: %d", cursor.getCount());
int rawId = cursor.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
int id = cursor.getColumnIndex(ContactsContract.Data._ID);
int name = cursor.getColumnIndex(ContactsContract.Profile.DISPLAY_NAME);
int photoUri = cursor.getColumnIndex(ContactsContract.Profile.PHOTO_URI);
int lookupKey = cursor.getColumnIndex(ContactsContract.Profile.LOOKUP_KEY);
int version = cursor.getColumnIndex(ContactsContract.Data.DATA_VERSION);
try {
if (cursor.moveToFirst()) {
long phRawId = cursor.getLong(rawId);
int phId = cursor.getInt(id);
String phName = cursor.getString(name);
String phImageUri = cursor.getString(photoUri);
String phLookupKey = cursor.getString(lookupKey);
int phVersion = cursor.getInt(version);
boolean phExists = true;
L.d("MY PROFILE - RawID: %d, ID: %d", phRawId, phId);
// ... profile successfully retrieved
} else {
L.d("MY PROFILE - cursor is EMPTY");
}
} finally {
cursor.close();
}
} else {
L.d("MY PROFILE - cursor = NULL");
}
}
Additional info
I think this code worked on my S6 with android 7 but it's not working on my new S9 with android 8 on it (can't test it on my old phone anymore as it's not working anymore). So this may be an android version specific problem...
This appears to be bad implementation of Samsung's Contacts app, I've opened a bug report on their developer's forum here: https://developer.samsung.com/forum/thread/contacts-app-profile-is-not-accessible-via-contactscontractprofile-api/201/354874

ORMLite get single database column

I'm trying to use this SQL command with Ormlite:
select address from receive
With this code:
List<Receivers> receiver_address = receiverDao.queryBuilder().selectColumns("address").query();
But the object returned is:
1 = {Receivers#830028192208}
address = {String#830028192264} "my new address"
city = null
email = null
telephone = null
mobile = null
name_family = null
national_code = null
postal_code = null
receiver_name = null
id = 2
I need only address field in this query without iterator such as:
List<String> list = new ArrayList<String>();
for( Receivers lst:receiver_address)
list.add(lst.getAddress());
How to do this action?
You can use RawRowMapper here:
List<String> addresses = receiverDao.queryRaw("select address from receive", new RawRowMapper<String>() {
#Override
public String mapRow(String[] columnNames, String[] resultColumns) throws SQLException {
return resultColumns[0];
}
}).getResults();

Difference between LG default calendar and Google calendars

On my LG-G3 there is a default calendar named "Phone". It's not Google's.
I build an application which syncs events with the user's Google Calendars, but when I select all the calendars with a query - I get the "Phone" calendar too. Since it's not a Google calendar, I can't use it with the Google Calendar functions (insert, delete, etc.).
I can't see any different between "Phone" calendar and Google canledars except of its name. Is there any way to know if a calendar is Google's or not?
This is my query:
String[] l_projection = new String[] { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_ACCESS_LEVEL, Calendars.ALLOWED_REMINDERS, Calendars.SYNC_EVENTS };
Uri l_calendars;
if (Build.VERSION.SDK_INT >= 8) {
l_calendars = Uri.parse("content://com.android.calendar/calendars");
} else {
l_calendars = Uri.parse("content://calendar/calendars");
}
try {
Cursor l_managedCursor = activity.getContentResolver().query(l_calendars, l_projection, null, null, null);
if (l_managedCursor.moveToFirst()) {
String l_methodAllow;
String l_accessPermission;
String l_calName;
String l_calId;
String l_syncEvents;
int l_cnt = 0;
int l_syncEventsCol = l_managedCursor.getColumnIndex(l_projection[4]);
int l_methodAllowCol = l_managedCursor.getColumnIndex(l_projection[3]);
int l_accessPermissionCol = l_managedCursor.getColumnIndex(l_projection[2]);
int l_nameCol = l_managedCursor.getColumnIndex(l_projection[1]);
int l_idCol = l_managedCursor.getColumnIndex(l_projection[0]);
do {
String access = l_managedCursor.getString(l_accessPermissionCol);
if (access.equals("500") || access.equals("600") || access.equals("700") || access.equals("800")) {
l_syncEvents = l_managedCursor.getString(l_syncEventsCol);
l_methodAllow = l_managedCursor.getString(l_methodAllowCol);
l_accessPermission = l_managedCursor.getString(l_accessPermissionCol);
l_calName = l_managedCursor.getString(l_nameCol);
l_calId = l_managedCursor.getString(l_idCol);
calNames.add(l_calName);
// ....
++l_cnt;
}
} while (l_managedCursor.moveToNext());
}
} catch (Exception e) {
// ...
}
Google calendar can be identified by looking at the domain name of the Calendar ID. For primary calendar, calendar ID domain name is #gmail.com. If its secondary calendar, calendar ID domain name is group.calendar.google.com

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.
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
It looks like you access AdressBook for each loookupID, this might cause your speed issue.
Try:
1) Fetch all contacts, or only those you might be interested in. (Use Linq)
2) Do further work with found contacts
Example from Xamarin docs:
http://blog.xamarin.com/introducing-xamarin-contacts/
var book = new AddressBook (this) {
PreferContactAggregation = true
};
foreach (Contact c in book.Where (c => c.LastName == "Smith")) {
print (c.DisplayName);
foreach (Phone p in c.Phones)
print ("Phone: " + p.Number);
foreach (Email e in c.Emails)
print ("Email: " + e.Address);
}

Update phone contact (Android) via Phonegap

Using phonegap, I can get/filter a single contact from contact list. But how to update (add/remove) phone number field. Please help. Thanks alot.
Lets say 1 got a contact name John Smith with 2 phone number [('Home', '1111'), ('Work', '2222')].
When I try to remove the 'Work' number, just keep the 'Home' one. First get the contact, try to remove all number, then add the 'Home' number but I always get both 3 numbers [('Home', '1111'), ('Work', '2222'), ('Home', '1111')]
Weir that if I try to remove all number, then add nothing, it really remove all the number from contact ?
Here is my code
var phoneNumbers = [];
for (...){
phoneNum = {
type: ...,
value: ...,
pref: false
};
phoneNumbers.push(phoneNum);
}
contact = contacts_list[index]; //get the contact need to edit
//try to remove all current phone number
if (contact.phoneNumbers){
for (var i = 0; i < contact.phoneNumbers.length; i++){
delete contact.phoneNumbers[i];
//contact.phoneNumbers[i] = null; //i try this too
//contact.phoneNumbers[i] = []; //i try this too
}
}
//set new phone number
contact.phoneNumbers = phoneNumbers;
contact.save(...)
I also try create a new contact with only 1 number [('Home', '1111')], set id and rawId as same as i contact object I need to update, then save(). But i still get the same result [('Home', '1111'), ('Work', '2222'), ('Home', '1111')]
var contact = navigator.contacts.create();
var phoneNumbers = [];
phoneNumbers[0] = new ContactField('Home', '1111', false);
contact.phoneNumbers = phoneNumbers;
contact.id = ...
contact.rawId = ...
contact.save(...);
this also
contact = contacts_list[index]; //get the contact need to edit
//try to remove all current phone number
if (contact.phoneNumbers){
for (var i = 0; i < contact.phoneNumbers.length; i++){
delete contact.phoneNumbers[i];
//contact.phoneNumbers[i] = null; //i try this too
//contact.phoneNumbers[i] = []; //i try this too
}
}
var phoneNumbers = [];
phoneNumbers[0] = new ContactField('Home', '1111', false);
contact.phoneNumbers = phoneNumbers;
contact.save(...)
In the contact plugin of cordova, you can save the contact passing the original contact id, it will update the contact details in the database.
Here is an example:
//Set the options for finding conact
var options = new ContactFindOptions();
options.filter = 'Bob'; //name that you want to search
options.multiple = false;
var fields = ["id","displayName", "phoneNumbers"];
navigator.contacts.find(fields, sucessUpdate, onError, options);
function sucessUpdate(contacts) {
var contact = contacts[0]; //found contact array must be one as we disabled multiple false
// Change the contact details
contact.phoneNumbers[0].value = "999999999";
contact.name = 'Bob';
contact.displayName = 'Mr. Bob';
contact.nickname = 'Boby'; // specify both to support all devices
// Call the "save" function on the object
contact.save(function(saveSuccess) {
alert("Contact successful update");
}, function(saveError){
alert("Error when updating");
});
}
function onError(contactError)
{
alert("Error = " + contactError.code);
}

Categories

Resources