Im trying to make some changes to an app that i've downloaded, the app uses ContactContracts to show the list of contacts stored in the phone,
what im trying to do is using a content provider to show a list of contacts that i've stored in my database
the original app uses this method to load contacts from the phone and store the in "contact" object:
public void loadContactsData(){
if(SmsSchedulerApplication.contactsList.size()==0){
System.currentTimeMillis();
String[] projection = new String[] {Groups._ID};
Uri groupsUri = ContactsContract.Groups.CONTENT_URI;
groupCursor = managedQuery(groupsUri, projection, null, null, null);
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if(cursor.moveToFirst()){
do{
if(!(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)).equals("0"))){
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Cursor phones = cr.query(Phone.CONTENT_URI, null, Phone.CONTACT_ID + " = " + id, null, null);
if(phones.moveToFirst()){
Contact contact = new Contact();
contact.content_uri_id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
contact.name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
contact.number = phones.getString(phones.getColumnIndex(Phone.NUMBER));
Cursor cur = cr.query(ContactsContract.Data.CONTENT_URI, new String[]{ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID}, ContactsContract.CommonDataKinds.GroupMembership.CONTACT_ID + "=" + contact.content_uri_id, null, null);
if(cur.moveToFirst()){
do{
if(!String.valueOf(cur.getLong(cur.getColumnIndex(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID))).equals(contact.number) && cur.getLong(cur.getColumnIndex(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID))!=0){
boolean isValid = false;
if(groupCursor.moveToFirst()){
do{
if(!cur.isClosed() && !groupCursor.isClosed() && cur.getLong(cur.getColumnIndex(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID)) == groupCursor.getLong(groupCursor.getColumnIndex(Groups._ID))){
isValid = true;
break;
}
}while(groupCursor.moveToNext());
}
if(isValid){
contact.groupRowId.add(cur.getLong(cur.getColumnIndex(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID)));
}
}
}while(cur.moveToNext());
}
cur.close();
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(contact.content_uri_id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
try{
contact.image = BitmapFactory.decodeStream(input);
contact.image.getHeight();
} catch (NullPointerException e){
contact.image = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.drawable.no_image_thumbnail);
}
SmsSchedulerApplication.contactsList.add(contact);
}
}
}while(cursor.moveToNext());
}
}
}
im not familiar with using content provider so im not sure what should i define in my content provider class
You don't have to use a content provider if you don't want to. Also using a contacts provider example to realize an own user database management is the wrong approach here. The provider was evolved multiple times to support all kind of use cases. I mean, just look at the documentation of it. All this tables and and micromanagement. I doubt that you need all of this.
I suggest you do the following. First create you database scheme. Find out what tables do you need and what columns are required for the application to work. Then put it into an sqlite database and define the queries to store and retrieve the contents. This should be all necessary to make the app work.
Then if everything works and you still have the motivation or requirements, create a content provider by using the existing database as the data store withing the provider and declaring URI's to access and modify the content. Of course you then have to rewrite some portions of the existing code. But if you were using the query, update, delete and insert methods of the database, it should be no problem.
Or you do it the hard way and insert your contacts in the contacts provider by inserting raw contacts. But it you don't have to share your contacts with the system (to crate custom actions on existing contacts for example) or other apps, then just don't do it.
Related
So I've written a query to extract the WhatsApp contacts of a phone. My initial query goes like this:
Cursor c = con.getContentResolver().query(
ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts.CONTACT_ID, ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY},
ContactsContract.RawContacts.ACCOUNT_TYPE + "= ?",
new String[]{"com.whatsapp"},
null
);
ArrayList<String> myWhatsappContacts = new ArrayList<String>();
int contactNameColumn = c.getColumnIndex(ContactsContract.RawContacts.DISPLAY_NAME_PRIMARY);
while (c.moveToNext()) {
// You can also read RawContacts.CONTACT_ID to read the
// ContactsContract.Contacts table or any of the other related ones.
myWhatsappContacts.add(c.getString(contactNameColumn));
}
The purpose of this is to find out how many WhatsApp contacts the phone has at any one time. When I do:
Log.i("WhatsApp contacts found:", Integer.toString(myWhatsappContacts.size());
It should print out how many WhatsApp contacts there were into LogCat. And this works - up to a point.
Let's say for example that the number of WhatsApp contacts I have now is 101. The next phase of this little project is to delete away ALL contacts if there are more than 100 of them. In which case, we go:
if (myWhatsappContacts.size() > 100) {
//Delete all contacts code here
}
I've tested the delete contacts code, it works. I check the contacts directory of the phone via the contacts app, and it says 0. But now when I do the query again (refer to code above), it still shows 101! What's going on?
If it helps, my DeleteContacts method is as follows:
private void deleteContact(Context ctx, String phone, String name) {
Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phone));
Cursor cur = ctx.getContentResolver().query(contactUri, null, null, null, null);
try {
if (cur.moveToFirst()) {
do {
if (cur.getString(cur.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)).equalsIgnoreCase(name)) {
String lookupKey = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey);
ctx.getContentResolver().delete(uri, null, null);
return;
}
} while (cur.moveToNext());
}
} catch (Exception e) {
System.out.println(e.getStackTrace());
} finally {
cur.close();
}
return;
}
What am I doing wrong? Is my DeleteContacts code faulty? Or is the query itself faulty?
I am very new to app development. I am trying to read contact info without having to request permission to contacts (so I am using intents).
I get a URI with the following code in my main activity:
Intent selectContactIntent = new Intent(Intent.ACTION_PICK);
selectContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
if (selectContactIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(selectContactIntent, REQUEST_CODE_SELECT_CONTACT);
}
else {
showContactRequiredMessage(view);
}
In another (sub)activity, I do the following:
contactUri = intent.getParcelableExtra(MainActivity.CONTACT_URI);
String[] projection = new String[] {
ContactsContract.Contacts.Data._ID,
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.Contacts.Data.DATA1,
ContactsContract.Contacts.Data.DATA2,
ContactsContract.Contacts.Data.DATA3,
ContactsContract.Contacts.Data.DATA4,
ContactsContract.Contacts.Data.DATA5,
ContactsContract.Contacts.Data.DATA6,
ContactsContract.Contacts.Data.DATA7,
ContactsContract.Contacts.Data.DATA8,
ContactsContract.Contacts.Data.DATA9,
ContactsContract.Contacts.Data.DATA10,
ContactsContract.Contacts.Data.DATA11,
ContactsContract.Contacts.Data.DATA12,
ContactsContract.Contacts.Data.DATA13,
ContactsContract.Contacts.Data.DATA14,
ContactsContract.Contacts.Data.DATA15
};
Cursor contactResults = getContentResolver().query(contactUri, projection, null, null, null);
The last line throws the exception java.lang.IllegalArgumentException: Invalid column <any column after _ID>
My app doesn't require all of the data in reality I just want to see what is available, I will most likely need first name, last name, phone, and email.
My issue is the MIME type that I set on the intent when I request the contact info. The documentation states ContactsContract.Contacts.CONTENT_TYPE should be used. However, if I use, something like ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE, I can get display name and phone number. I am not sure if this means I will need to make multiple queries to get everything (the information shown in the contact picker changes when changing the type requested).
TL;DR: Used the "wrong" content type when creating the intent to select a contact.
As mentioned in my comment to your answer, you should be able to get the information expected without using a specific CONTENT_TYPE like CommonDataKinds.Phone.CONTENT_TYPE.
The problem I see in your code is that you're trying to access Data table info from a Contacts table uri.
The ContactsContract api stored info on 3 main tables: Contacts, RawContacts and Data.
You were given a contactUri which points to an entry in the Contacts table, use the following code to read Data entries related to that contact:
long contactId = ContentUris.parseId(contactUri);
String projection = String[] { Data.MIMETYPE, Data.DISPLAY_NAME, Data.DATA1 };
String selection = Data.CONTACT_ID + " = " + contactId;
Cursor cursor = getContentResolver().query(Data.CONTENT_URI, projection, selection, null, null);
while (cursor != null && cursor.moveToNext()) {
String mime = cursor.getString(0);
String name = cursor.getString(1);
String info = cursor.getString(2);
if (mime.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
Log.d(TAG, name + ": email = " + info;
}
if (mime.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
Log.d(TAG, name + ": phone = " + info;
}
// Add more mimetypes here if needed...
}
if (cursor != null) {
cursor.close();
}
I can see in LogCat that ActivityManager is reporting something about a particular content provider. I use the following code to try to access that content provider in order to learn more about it:
Cursor cursor = context.getContentResolver().query(uriDSDS, null, null, null, null);
String[] columnNames = cursor.getColumnNames();
Log.d(TAG, "columnNames=" + columnNames.toString());
Unfortunately, after trying to get the cursor, I see the following error in LogCat:
Failed to find provider for __
What's the problem? Why can I not access this provider? Could it be that access is restricted to certain apps? Is there a way to go into ADB or something else to see all of the available content providers on the device?
make sure that you add correct Uri
for example
Uri uriDSDS = Uri.parse(
"content://aaa.aaa/values");
As in the working example below, do check if your uriDSDSis correct.
Cursor people = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, null, null, null,
null);
// get contact id
while (people.moveToNext()) {
if (people != null) {
int numberFieldColumnIndex = people
.getColumnIndex(PhoneLookup._ID);
String number = people.getString(numberFieldColumnIndex);
contactId.add(number);
}
}
people.close();
I created an app. It is working on htc desire hd, but when i tested it on ZTE Blade, a strange problem appeared. In my app when user selects a contact name from a spinner a menu appears. In the menu the user can send sms to the user, call him/her or just look at his/her contact info. On HTC Desire HD, everything is working fine. On ZTE there seems to be an exasperating problem with the contact info button: In certain cases when user selects a contact and wants to see his contact info, some other contact's info is shown. So I select Pete from my spinner but I get Dave's contact info. In other cases I select Tom from the spinner and I get Tom's contact info. The problem is not existing on my HTC. I couldn't figure out what causes the problem. By the way, my contact list on HTC is populated also from gmail and facebook and the app still working fine, while the contact list of the ZTE has never seen any gmail or facebook accounts (i am not entirely sure about this).
This is the code i am using to get to the contact info:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[]{id_contact}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
I have similar problems in my application. The app is a widget that give possibility to use favorites contacts in fast way. User creates new widget and assigns contact to it. When he clicks on widget he is able to make common contact actions - call, sms, edit, etc.
Some users send report like this one:
"I select about 4 contacts and the app shows 4 but only one of them right 3 others is same contact which I didnt even picked".
"Why does the wrong contact show when picked? On my droid incredible"
I can't reproduce the problem on my side. I have tried several different devices, tried to make import/export contacts - no results.
Anyway, i have some ideas about source of the problem.
My application uses lookup keys to store contacts, according to sdk. So, when user picks up a contact, application stores contact lookup key. Then it uses this lookup key to get Contact ID for this contact. After that, it uses ONLY received Contact ID in all other functions. So does your function - it uses only Contact ID in sub-query.
The question is: is it possible that different contacts (with different lookup keys) have same Contact ID? It looks like it's possible on some devices in rare cases... If so, we need to use Contact ID + lookup key always together to identify contact. (update: the suggestion was incorrect. Lookup key can be changed after contact info modification, you won't find contact).
As I understand, you are able to reproduce the problem on your side. I would suggest to change your code in such way:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String lookup_key = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ? AND " + ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + " = ?", new String[]{id_contact, lookup_key}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
and try to reproduce the problem again.
You can see this link, It's very good: http://www.vtutorials.info/2014/08/working-with-android-contacts-how-to.html
private Map<Long, ArrayList<LocalContactInfo>> queryAllPhones(ContentResolver contentresolver)
{
HashMap<Long, ArrayList<LocalContactInfo>> hashmap = new HashMap<Long, ArrayList<LocalContactInfo>>();
Uri PhoneCONTENT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String Phone_CONTACT_ID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
String NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
Cursor cursor = contentresolver.query(PhoneCONTENT_URI, null, null, null, null);
do
{
if (!cursor.moveToNext())
{
cursor.close();
return hashmap;
}
long l = cursor.getLong(cursor.getColumnIndex(Phone_CONTACT_ID));
String s = cursor.getString(cursor.getColumnIndex(NUMBER));
LocalContactInfo temp = new LocalContactInfo(String.valueOf(l), "", s, "", 1);
if (hashmap.containsKey(Long.valueOf(l))){
((List<LocalContactInfo>)hashmap.get(Long.valueOf(l))).add(temp);
} else{
ArrayList<LocalContactInfo> arraylist = new ArrayList<LocalContactInfo>();
arraylist.add(temp);
hashmap.put(Long.valueOf(l), arraylist);
}
} while (true);
}
And:
public List<LocalContactInfo> doloadContacts() {
List<LocalContactInfo> _return = new ArrayList<LocalContactInfo>();
Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
String _ID = ContactsContract.Contacts._ID;
String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;
String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;
ContentResolver contentResolver = mContext.getContentResolver();
Map<Long, ArrayList<LocalContactInfo>> map = queryAllPhones(contentResolver);
Cursor cursor = contentResolver.query(CONTENT_URI, null,null, null, null);
if(cursor == null || cursor.getCount()==0){
return _return;
}
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
Long contact_id = cursor.getLong(cursor.getColumnIndex( _ID ));
String fullname = cursor.getString(cursor.getColumnIndex( DISPLAY_NAME )).trim();
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex( HAS_PHONE_NUMBER )));
if (hasPhoneNumber > 0 && map.get(contact_id) != null){
Object obj = ((List<LocalContactInfo>)map.get(contact_id)).get(0);
if(obj instanceof LocalContactInfo){
LocalContactInfo _temp = (LocalContactInfo)obj;
_return.add(new LocalContactInfo(String.valueOf(contact_id), fullname, _temp.getNumberPhone(), "",hasPhoneNumber));
}
}
}
}
return _return;
}
instead of using "contacts/people/xxx", try using contactsContract.Contacts.CONTENT_URI.
basically, you want to replace.
Uri contactUri = Uri.parse("content://contacts/people/" + id_contact2));
with
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
id_contact2);
Another thing to note is that "id_contact2" may be changed if a sync occurs before you use it (like when google sync merges a contact). To prevent this, it might be better to use a LookupKey instead.
ContactsContract.Contacts.LOOKUP_KEY
and retrieve using
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI,
yourContactLookupKey);
When picking out contact details, is there a built in domain class they can be mapped to? Or, do you have to create your own?
For example I do the following :
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
String s = null;
if (cursor.getCount() > 0)
{
while (cursor.moveToNext())
{
s = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
}
}
With s, can I put that into a "contact" object/domain class, perhaps something like :
Contact myContact = new Contact();
myContact.setName(s);
AFAIK There is no built in Contacts class, besides with extensible Contacts model in android it will soon become a Bean around CommonDataKinds plus Map for everything else, besides I believe it's also true for the most of data provided by Android SDK
They give you access to Data how you handle it is yours choice