Sorted list of contacts having duplicates ,why? - android

I have sorted and listed my phone contacts in to an arraylist but ,i got many duplicates of same contact names in the list .How this happens? how to avoid this?
This is what i have tried,
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,
"(" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + ") ASC");
while (cursor.moveToNext()) {
try {
name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
phonenumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contact_names_list.add(name);
phone_num_list.add(phonenumber);
} catch (Exception e) {
e.printStackTrace();
}
can anyone help??

No one here seems to answer your question.
The reason you're seeing duplicate contacts is that you're querying for phones not contacts.
In Android there are 3 main tables:
Contacts table - has one item per contact
RawContacts table - has one item per-contact per-account (such as Google, Outlook, Whatsapp, etc.) - multiple RawContacts are linked to a single Contact
Data table - has one item per detail (name, email, phone, address, etc.) - each data item is linked to a single RawContact, and multiple Data rows are linked to each RawContact.
You're querying on CommonDataKinds.Phone.CONTENT_URI which is a part of the Data table, so if a contact has more then one phone, and/or it has the same phone from multiple sources (e.g. Google and Whatsapp) you'll get the same phone with the same CONTACT_ID more then once.
The solution would be, to use a HashMap (rather then a HashSet), where the key is CONTACT_ID, so you can display multiple phones per contact:
String[] projection = new String[] { CommonDataKinds.Phone.CONTACT_ID, CommonDataKinds.Phone.DISPLAY_NAME, CommonDataKinds.Phone.NUMBER };
Cursor cursor = getContentResolver().query(CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
HashMap<Long, Contact> contacts = new HashMap<>();
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String name = cursor.getString(1);
String phone = cursor.getString(2);
Contact c = contacts.get(id);
if (c == null) {
// newly found contact, add to Map
c = new Contact();
c.name = name;
contacts.put(id, c);
}
// add phone to contact class
c.phones.add(phone);
}
cursor.close();
// simple class to store multiple phones per contact
private class Contact {
public String name;
// use can use a HashSet here to avoid duplicate phones per contact
public List<String> phones = new ArrayList<>();
}
If you want to sort your HashMap by name:
List<Contact> values = new ArrayList<>(contacts.values());
Collections.sort(values, new Comparator<Contact> {
public int compare(Contact a, Contact b) {
return a.name.compareTo(b.name);
}
});
// iterate the sorted list, per contact:
for (Contact contact : values) {
Log.i(TAG, "contact " + contact.name + ": ");
// iterate the list of phones within each contact:
for (String phone : contact.phones) {
Log.i(TAG, "\t phone: " + phone);
}
}

You can try with HashSet.
public class HashSet extends AbstractSet implements Set,
Cloneable, Serializable
Duplicate values are not allowed.
Code Structure
HashSet<String> hashSET = new HashSet<String>();
hashSET.add("AA");
hashSET.add("BB");
hashSET.add("CC");
hashSET.add("AA"); // Adding duplicate elements
Then
Iterator<String> j = hashSET.iterator();
while (j.hasNext())
System.out.println(j.next()); // Will print "AA" once.
}
Now SORT your Hashset Values using TreeSet.
TreeSet implements the SortedSet interface so duplicate values are not
allowed.
TreeSet<String> _treeSET= new TreeSet<String>(hashSET);

May be in your contacts having multiple groups, and that group will be a WhatsApp,Google etc..Go to your contacts and search that contact having whatsApp account. will showing double entry with different Group
you should use or change your ContactsBean , in your Bean use HashSet
Note: HashSet can avoid duplicate entry more
HashSet contains unique elements only,it can avoid same key element form HashSet
Example bean
public class ContactBean {
private HashSet<String> number = new HashSet<String>();
public void setNumber(String number) {
if (number == null)
return;
this.number.add(number.trim());
}
public HashSet<String> getNumber() {
return this.number;
}
}
Simple Example
//Creating HashSet and adding elements
HashSet<String> hashSet=new HashSet<String>();
hashSet.add("Dhruv");
hashSet.add("Akash");
hashSet.add("Dhruv"); //Avoiding this entry
hashSet.add("Nirmal");
//Traversing elements
Iterator<String> itr = hashSet.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}

You can use HashSet for avoid duplication:-
HashSet<String> hset =
new HashSet<String>();
you can add like ArrayList in HashSet:-
hset.add(your_string);
OR
Convert your ArrayList to HashSet :-
Set<String> set = new HashSet<String>(your_arraylist_object);
HashSet avoid Duplicate Entry :)

I don't know why are you getting duplicate items from contacts, maybe phone contacts already have duplicate values.You can check that in Contacts app.
You should always use set data structure wherever you want to avoid duplicate items. You can find the better explanation and example here.

I think your duplication is because of Whatsapp contact interfering with contact . so you can use something like this
String lastPhoneName = "";
String lastPhoneNumber = "";
//IN YOUR CONTACT FETCHING WHILE LOOP , INSIDE TRY
String contactName = c.getString(c
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phNumber = c
.getString(c
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (!contactName.equalsIgnoreCase(lastPhoneName) && !phNumber.equalsIgnoreCase(lastPhoneNumber)) {
lastPhoneName = contactName;
lastPhoneNumber = phNumber;
ContactModel model = new ContactModel();
model.setContact_id(contactid);
model.setContact_name(contactName.replaceAll("\\s+", ""));
model.setContact_number(phNumber.replaceAll("\\D+", ""));
list_contact_model.add(model);
}
this will check that previous number is same as old one than skip it . I hope You get your answer

HashSet add items in key/value pair and also remove duplicate entry from item set.
List<String> phone_num_list= new ArrayList<>();
// add elements to phone_num_list, including duplicates
Set<String> hs = new HashSet<>();
hs.addAll(phone_num_list);
phone_num_list.clear();
phone_num_list.addAll(hs);
Happy coding!!

Related

Android - Remove Duplicate Contacts

I am trying to display contacts in Recycler View everything is working fine but contacts are getting displayed twice or thrice.
Here is the Code
RecyclerView recyclerView;
List<Contacts> contactsList;
ContactsAdapter adapter;
public void getContactList() {
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Contacts contacts = new Contacts(name, number);
if (contactsList.contains(contacts)){
cursor.moveToNext();
}
else {
contactsList.add(contacts);
}
adapter = new ContactsAdapter(contactsList, getApplicationContext());
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
So, how can I solve this problem?
Pass only phoneNumber as a key and phoneName as a value
You can use :
Map<String, String> map = new HashMap<>();
map.put("A", "1");
...
System.out.printf("Before: %s%n", map);
// Set in which we keep the existing values
Set<String> existing = new HashSet<>();
map = map.entrySet()
.stream()
.filter(entry -> existing.add(entry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
System.out.printf("After: %s%n", map);
for example :
Before: {A=1, B=2, C=2, D=3, E=3} After: {A=1, B=2, D=3}
You can change List with HashSet. HashSet prevents to have duplicate items. Your final code should be like that:
RecyclerView recyclerView;
HashSet<Contacts> contactsSet;
ContactsAdapter adapter;
public void getContactList() {
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ") ASC");
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Contacts contacts = new Contacts(name, number);
contactsSet.add(contacts);
}
List<Contacts> contactsList = new ArrayList<>();
adapter = new ContactsAdapter(contactsList, getApplicationContext());
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
I think there are a couple of questions you should think about. Is the query wrong or is the way I save contacts wrong?
In your app should contacts be allowed to be duplicated? If no you could add a unique constraint to your migration. See Sql Lite Unique Constraints
If your DB should be accepting duplicate values, then you should add a column that signifies the unique ones to that user? Ie userId column or a place the contact is associated with and then add that into your query as Selection and SelectionArgs.
Lastly if you are just looking for a quick fix... you should dive deeper into your filtering.
Your code contactsList.contains(contacts) will never be true because you are always creating the contact on the line above. Then you are checking if your list contains that newly created one. Otherwise you can use the above solution https://stackoverflow.com/a/64535787/5398130
You can override equals() method in class Contacts. This way your contactList.contains(Contact c) can evaluate to true if two contact objects are logically equals from your override method, and thus it will not add it to the list again.
#Override
public boolean equals(Object o){
if(!o.getClass().equals(Contacts.class)) return false;
Contact c2 = (Contact)o;
// if phonenumber is unique to differentiate contact
return c2.number.equals(number);
}
You can follow these steps on this page where i can provide the full solution here is the link

showing contacts by filtering account_type column from RawContacts

Im getting showing contacts on a RecyclerView and below code retrieve contacts
Uri Contact_URI=ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
return new CursorLoader(getActivity(),Contact_URI,null,null,null,Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
ContactsContract.Contacts.DISPLAY_NAME+ "ASC");
but some contacts are being shown multiple times so I decided to filter Contacts on account_type column. Below code filter based on account_type
if(list.getString(list.getColumnIndex("account_type")).equals("Local Phone Account") || list.getString(list.getColumnIndex("account_type")).equals("SIM Account") ) {
textView.setText(list.getString(list.getColumnIndex(Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
ContactsContract.Contacts.DISPLAY_NAME)));
number.setText(list.getString(list.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
number.setText(list.getString(list.getColumnIndex("account_type")));
}
Problem is values of account_type for sim and phone contacts varies from device to device. In Samsung GT-l9082 gives values for sim "Sim Account" and for Phone "Local Phone Account" but When I tested it on Galaxy J5 it shows different values against account_type for sim and phone contacts.I want to show only Sim and Phone Contacts
This is not the way to go.
CommonDataKinds.Phone.CONTENT_URI is a table of all the phones in the Contacts DB, not contacts.
So even if you filter to just one account, if a contact contains more then one phone, it'll appear twice in your list.
If you want to display only one row per contact, but you still need to display a phone in your main list, you can't use the CursorLoader paradigm (which actually sucks and I wouldn't use it anyway).
Instead run a simple query for all the items in the Phones.CONTENT_URI table, and create a HashMap from CONTACT_ID to a list of NUMBERs, and then display one row per item in the map, and you'll get access to that contact's list of phones as well for display.
Map<String, List<String>> contacts = new HashMap<String, List<String>>();
String[] projection = { Phone.CONTACT_ID, Phone.DISPLAY_NAME, Phone.NUMBER };
Cursor cur = cr.query(Phone.CONTENT_URI, projection, null, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1);
String data = cur.getString(2); // the actual info, e.g. +1-212-555-1234
Log.d(TAG, "got " + id + ", " + name + ", " + data);
// add info to existing list if this contact-id was already found, or create a new list in case it's new
String key = id + " - " + name;
List<String> infos;
if (contacts.containsKey(key)) {
infos = contacts.get(key);
} else {
infos = new ArrayList<String>();
contacts.put(key, infos);
}
infos.add(data);
}
// now you can iterate over the 'contacts' map to display all contacts

Query Database with an array list and get an array list of results back

I have setup an application which currently can lookup an input id with one on the database to then give a single result. E.g. user enters id = 1 , database contains a record with an id of 1 then returns the name or number etc...
Now I want to improve the system slightly by querying my database with an arraylist which contains a range of id's e.g. 3, 456, 731 etc... which I want my database to search for. I have also grouped multiple values to certain id's for example the database might search for an id of 3 it will then find 5 results I want it to return the telephone number of each one of those results into another arraylist which I can print to the logs.
I hope I have explained this enough, but please ask questions if you require more information.
The code below demonstrates the modified version of the query used to gain a single result, but I cannot see what I'm doing wrong to gain multiple results.
Activity....
// New array list which is going to be used to store values from the database
ArrayList<String> contactsList;
// This arrayList has been received from another activity and contains my id's
ArrayList<String> contacts = intent.getStringArrayListExtra("groupCode");
// The database which i'm using
ContactDBHandler contactDBHandler = new ContactDBHandler(getApplicationContext(), null, null, 1);
//getAllValues is used to pass my arraylist id's to the database.
contactsList = contactDBHandler.GetAllValues(contacts);
// Simple log statement to loop and display results
for (int i = 0; i < contactsList.size(); i++){
Log.i("Numbers", contactsList.get(i));
}
ContactDBHandler
Query
// I'm telling it to get the contact number from the contact_list
// when the groupcode matches the code recieved.
public ArrayList<String> GetAllValues(ArrayList groupCode)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = null;
String alarmName = "";
ArrayList<String> list = new ArrayList<>();
cursor = db.rawQuery("SELECT contact_number FROM contact_list WHERE grp_code=?", new String[]{groupCode+ ""});
if (cursor.moveToFirst())
{
do
{
list.add(cursor.getString(0));
}
while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed())
{
cursor.close();
}
return list;
}
Thanks
Can you see where I have gone wrong?
Try this:
cursor = db.rawQuery("SELECT contact_number FROM contact_list WHERE grp_code IN (" + TextUtils.join(",", Collections.nCopies(groupCode.size(), "?")) + ")", groupCode.toArray(new String[groupCode.size()]));
Your current code fails to pass the list in the sql-format: = does only support single values, for lists you have to use IN.
Your code would result in a query like this:
SELECT contact_number FROM contact_list WHERE grp_code=["some_id","other_id"]
But what you need (and my code produces) is:
SELECT contact_number FROM contact_list WHERE grp_code IN ('some_id','other_id')
References:
SQL query to find rows with at least one of the specified values
WHERE IN clause in Android sqlite?
IN clause and placeholders
https://developer.android.com/reference/android/text/TextUtils.html#join(java.lang.CharSequence,%20java.lang.Iterable)
You cannot pass an ArrayList to an SQLQuery. To check for multiple values in the same field you have to use the 'in' keyword.
Ex:
SELECT * FROM `table1` where column in ( 'element1', 'element2', 'element3')
In your case,
String str = "";
for(String s: groupCode){
str = str+","+"\'"+s+"\'";
}
//to remove the extra ' in the begining
str = str.substring(1);
return str;
cursor = db.rawQuery("SELECT contact_number FROM contact_list WHERE grp_code IN (?)", new String[]{str});

How to sort HashMap<String,ArrayList> in Android?

How to sort HashMap<String,ArrrayList<String>>?
I am fetching contact name from phone contacts like:
"String name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));"
And stored it into one HashMap<String,ArrayList<String>>. But all contacts I am getting in unsorted order.
May I know how to sort those contacts in alphabetical order so that I can display list view in sorted order?
You can retrieve your data from the database already sorted. Just use
String orderBy = ContactsContract.Contacts.DISPLAY_NAME + " DESC";
in your query. After that, usually when you need to use HashMap an have also sorted data, then you use HashMap + ArrayList. In HashMap you keep normal key,value pairs, in ArrayList you keep sorted values (in case if you have already sorted data, you add it to ArrayList while reading, you don't need to sort again).
If you need to sort ArrayList which is value in your HashMap, you can use:
Collections.sort(yourArrayList);
Use below class and pass List of data to the it. It will give you new sorted list.
public class CompareApp implements Comparator<AppDetails> {
private List<AppDetails> apps = new ArrayList<AppDetails>();
Context context;
public CompareApp(String str,List<AppDetails> apps, Context context) {
this.apps = apps;
this.context = context;
Collections.sort(this.apps, this);
}
#Override
public int compare(AppDetails lhs, AppDetails rhs) {
// TODO Auto-generated method stub
return lhs.label.toUpperCase().compareTo(rhs.label.toUpperCase());
}
}
I used following code to get Contacts in Ascending Order of String names as follows:
public void getAllContacts(ContentResolver cr) {
Cursor phones = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null,
null, Phone.DISPLAY_NAME + " ASC");
while (phones.moveToNext()) {
String name = phones
.getString(phones
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = phones
.getString(phones
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name1.add(name);
phno1.add(phoneNumber);
}
phones.close();
}
Code for reference
Hope this is what you need.

How do I delete contacts repeated in the listview?

Following the tutorial
get android contact phone number list
I pulled the phone numbers and names of contacts, how do I get a listview clean, no duplicate contacts and possibly sorted by name?
Try this:
Cursor phones = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
List<String> phoneNumbers = new ArrayList<String>();
while (phones.moveToNext()) {
String name=phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
System.out.println(".................."+phoneNumber);
if(!phoneNumbers.contains(phoneNumber)) {
phoneNumbers.add(phoneNumber);
}
}
Collections.sort(phoneNumbers);
In short: check if the phone number is not already in the list before adding it. It might be handy to remove any whitespace from each phone number before making the check so that you're sure no duplicates make it through.
Some more info about sorting: http://developer.android.com/reference/java/util/Collections.html#sort%28java.util.List%3CT%3E%29
Try this;
Use a Data Structure that doesn't allow duplicate e.g. HashMap
Use the phone number as the Key
e.g. Key = phoneNumber, Value = Name OR
Key = phoneNumber, Value = List of OtherContactDetails
Use Collections to sort
Pass the sorted Collections to Your Adapter

Categories

Resources