I have gone through a lot of posts but didn't find any answer that answers the question efficiently or even correctly. The closest I came was this How to avoid duplicate contact name (data ) while loading contact info to listview? but this has too much overhead. Is there any simpler or more efficient way to solve this?
I had the same problem you had: I was getting duplicate phone numbers. I solved this problem by obtaining the normalized number for each cursor entry and using a HashSet to keep track of which numbers I'd already found. Try this:
private void doSomethingForEachUniquePhoneNumber(Context context) {
String[] projection = new String[] {
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,
//plus any other properties you wish to query
};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
} catch (SecurityException e) {
//SecurityException can be thrown if we don't have the right permissions
}
if (cursor != null) {
try {
HashSet<String> normalizedNumbersAlreadyFound = new HashSet<>();
int indexOfNormalizedNumber = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
int indexOfDisplayName = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int indexOfDisplayNumber = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
while (cursor.moveToNext()) {
String normalizedNumber = cursor.getString(indexOfNormalizedNumber);
if (normalizedNumbersAlreadyFound.add(normalizedNumber)) {
String displayName = cursor.getString(indexOfDisplayName);
String displayNumber = cursor.getString(indexOfDisplayNumber);
//haven't seen this number yet: do something with this contact!
} else {
//don't do anything with this contact because we've already found this number
}
}
} finally {
cursor.close();
}
}
}
After API 21 We Write this Query for remove contact duplicacy.
String select = ContactsContract.Data.HAS_PHONE_NUMBER + " != 0 AND " +
ContactsContract.Data.MIMETYPE
+ " = " + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "
AND "+ ContactsContract.Data.RAW_CONTACT_ID + " = " +
ContactsContract.Data.NAME_RAW_CONTACT_ID;
Cursor cursor = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, select,
null, null);
ContentResolver cr = this.getContentResolver();
String[] FieldList = {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER,ContactsContract.CommonDataKinds.Phone.CONTACT_ID};
Cursor c = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,FieldList,
null,null,ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
String name,phone,ContactID;
HashSet<String> normalizedNumbers = new HashSet<>();
if(c!=null)
{
while(c.moveToNext()!=false)
{
phone = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER));
if(normalizedNumbers.add(phone)==true)
{
name = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
ContactID = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
MyContacts m = new MyContacts(name,phone,ContactID);
ContactList.add(m);
}
}
c.close();
Related
I tried other solutions on Stack Overflow, but they are not working with my problem. The below code is working perfectly before Android Q versions, but did not work with Android Q.
Here are the functions which are used to fetch albums and images.
First for fetching AlbumImages:
public static ArrayList<ImageFolder> getPicturePaths(Context context){
ArrayList<ImageFolder> picFolders = new ArrayList<>();
ArrayList<String> picPaths = new ArrayList<>();
Uri allImagesuri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media.BUCKET_ID};
Cursor cursor = context.getContentResolver().query(allImagesuri, projection, null, null, null);
try {
if (cursor != null) {
cursor.moveToFirst();
}
do {
ImageFolder folds = new ImageFolder();
String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));
String folder = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
String datapath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
//String folderpaths = datapath.replace(name,"");
String folderpaths = datapath.substring(0, datapath.lastIndexOf(folder + "/"));
folderpaths = folderpaths + folder + "/";
if (!picPaths.contains(folderpaths)) {
picPaths.add(folderpaths);
folds.setPath(folderpaths);
folds.setFolderName(folder);
folds.setFirstPic(datapath);//if the folder has only one picture this line helps to set it as first so as to avoid blank image in itemview
folds.addpics();
picFolders.add(folds);
} else {
for (int i = 0; i < picFolders.size(); i++) {
if (picFolders.get(i).getPath().equals(folderpaths)) {
picFolders.get(i).setFirstPic(datapath);
picFolders.get(i).addpics();
}
}
}
} while (cursor.moveToNext());
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
return picFolders;
}
And here is the method to fetch images from albums:
public static ArrayList<PhotoItemModel> getAllImagesByFolder(Context context, String path){
ArrayList<PhotoItemModel> images = new ArrayList<>();
Uri allImagesuri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Images.ImageColumns.DATA ,MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.SIZE};
Cursor cursor = context.getContentResolver().query( allImagesuri, projection, MediaStore.Images.Media.DATA + " like ? ", new String[] {"%"+path+"%"}, null);
try {
cursor.moveToFirst();
do{
PhotoItemModel pic = new PhotoItemModel();
pic.setPicturName(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)));
pic.setPicturePath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)));
pic.setPictureSize(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE)));
images.add(pic);
}while(cursor.moveToNext());
cursor.close();
ArrayList<PhotoItemModel> reSelection = new ArrayList<>();
for(int i = images.size()-1;i > -1;i--){
reSelection.add(images.get(i));
}
images = reSelection;
} catch (Exception e) {
e.printStackTrace();
}
return images;
}
How can I fix this?
First check whether the Android Q is getting some problem (permission related) to reach Folder path
String folderpaths = datapath.substring(0, datapath.lastIndexOf(folder + "/"));
folderpaths = folderpaths + folder + "/";
Under Android Q you cannot use the .DATA column.
Instead use .RELATIVE_PATH.
Read the threads tagged mediastore.
I am using Contact picker library for selecting multiple contacts but if a contact doesn't contain any number and if it is selected then it is showing some null pointer exception in the edit text field. How to remove that message and also how to remove trailing comma. Below is my Code.
try {
int pos = 0;
for (Contact contact : contacts) {
String displayName = contact.getDisplayName();
result.append(displayName + ",");
result.setSpan(new BulletSpan(15), pos, pos + displayName.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//pos += displayName.length() + 1;
}
}
catch (Exception e) {
result.append(e.getMessage());
}
contactsView.setText(result);
please try to check this code
void getAllContacts() {
ArrayList<String> nameList = new ArrayList<>();
ArrayList<String> numberList = new ArrayList<>();
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
String[] list = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Contacts._ID};
Cursor cursor = getContentResolver().query(uri, list, selection, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
cursor.moveToFirst();
if (cursor.moveToFirst()) {
do {
String contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
contactNuber.add(contactNumber);
contactsName.add(contactName);
nameList.add(contactName);
numberList.add(contactNumber);
} while (cursor.moveToNext());
cursor.close();
myContacts.put("name", nameList);
myContacts.put("number", numberList);
}
}
I search about read song in android and I see a code from you:
private static ArrayList<SongModel> LoadSongsFromCard() {
ArrayList<SongModel> songs = new ArrayList<SongModel>();
// Filter only mp3s, only those marked by the MediaStore to be music and longer than 1 minute
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0"
+ " AND " + MediaStore.Audio.Media.MIME_TYPE + "= 'audio/mpeg'"
+ " AND " + MediaStore.Audio.Media.DURATION + " > 60000";
final String[] projection = new String[] {
MediaStore.Audio.Media._ID, //0
MediaStore.Audio.Media.TITLE, //1
MediaStore.Audio.Media.ARTIST, //2
MediaStore.Audio.Media.DATA, //3
MediaStore.Audio.Media.DISPLAY_NAME
};
final String sortOrder = MediaStore.Audio.AudioColumns.TITLE
+ " COLLATE LOCALIZED ASC";
Cursor cursor = null;
try {
// the uri of the table that we want to query
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; //getContentUriForPath("");
// query the db
cursor = _context.getContentResolver().query(uri,
projection, selection, null, sortOrder);
if (cursor != null) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
//if (cursor.getString(3).contains("AwesomePlaylists")) {
SongModel GSC = new SongModel();
GSC.ID = cursor.getLong(0);
GSC.songTitle = cursor.getString(1);
GSC.songArtist = cursor.getString(2);
GSC.path = cursor.getString(3);
// This code assumes genre is stored in the first letter of the song file name
String genreCodeString = cursor.getString(4).substring(0, 1);
if (!genreCodeString.isEmpty()) {
try {
GSC.genre = Short.parseShort(genreCodeString);
} catch (NumberFormatException ex) {
Random r = new Random();
GSC.genre = (short) r.nextInt(4);
} finally {
songs.add(GSC);
}
}
//}
cursor.moveToNext();
}
}
} catch (Exception ex) {
} finally {
if (cursor != null) {
cursor.close();
}
}
return songs;
}
I'm really learning to program for Android, and modify your code for what I needed , but I have trouble understanding when you assign cursor
cursor = _context.getContentResolver () query ( uri , projection , selection , null, sortOrder ) . ;
Part of _context not understand you kindly explain it please.
If your class extends activity you can use this for _context. If not, you need to pass the context from your activity. The first case mentioned would look like this:
// query the db
cursor = this.getContentResolver().query(uri, projection, selection, null, sortOrder);
__
What is Context in Android?
Here is the log of my map entries. That have two same key exists. How this is possible?
Map<String, Objects> map = new HashMap<String, Objects>();
addContact("+917111111111");
addContact("+919222222222");
addContact("+919222222222");
private void addContact(String number){
if(TextUtils.isEmpty(number))return;
number = number.trim();
number = number.replaceAll("-", "");
number = number.replaceAll(" ", "");
if(!map.containsKey(number)) {
map.put(number, null);
}
}
/* While debugging in android studio. I have found the map have below entry.
0 = {HashMap$HashMapEntry#3798} "+919222222222" -> "null"
1 = {HashMap$HashMapEntry#3832} "+919222222222" -> "null"
2 = {HashMap$HashMapEntry#3694} "+917111111111" -> "null"
*/
map.containsKey("+919222222222");// ==> return false
Why this is happen ?
Actual task:
private void getContacts(){
try {
Cursor cursor = null;
StringBuffer sb = new StringBuffer();
Map<String, Object> map = new HashMap<String, Object>();
try {
String strOrder = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, strOrder);
int contactIdIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);
int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int phoneNumberIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int photoIdIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_ID);
cursor.moveToFirst();
do {
String idContact = cursor.getString(contactIdIdx);
String name = cursor.getString(nameIdx);
String phoneNumber = cursor.getString(phoneNumberIdx);
//...
phoneNumber = getFormatedNumber(phoneNumber);
//as map key same phone number can not be two times
if(!map.containsKey(phoneNumber)) {
map.put(phoneNumber, null);
sb.append("\nPhone Number:--- " + phoneNumber + "\nUser Name:--- "
+ name);
sb.append("\n----------------------------------");
}
} while (cursor.moveToNext());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
textView.setText(sb); //As in output it shows one number showing two times
} catch (Exception e) {
e.printStackTrace();
}
}
private String getFormatedNumber(String number){
if(TextUtils.isEmpty(number))return null;
number = number.trim();
number = number.replaceAll("-", "");
number = number.replaceAll(" ", "");
return number;
}
After all discussion, I found the issue like the problem occur due to unicode character append in my string that is invisible while debugging but if we copied into notepad then it clearly visible. as like :
'\u202A\u202A+91922222222\u202A\u202C'
I'm looking to export the contacts that are stored in the native database.
I'm having trouble retrieving contacts from native database.
Here is the query I would like to use :
Get all the contacts that have at least a phone number or an email.
Here is the query I am using :
String dataWhere = ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?";
String[] dataWhereValues = new String[]{ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
String[] dataProjection = new String[]{ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY, ContactsContract.Data.DISPLAY_NAME_PRIMARY, ContactsContract.Data.STARRED, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA_VERSION};
Cursor data = getContentResolver().query(ContactsContract.Data.CONTENT_URI, dataProjection, dataWhere, dataWhereValues, ContactsContract.Data.CONTACT_ID);
But this query gives me lots of weird contacts, and some of my contacts are also missing...
Can anyone help me please ?
this may help you for getting phone numbers and display names...
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
// importing phone contacts
while (phones.moveToNext())
{
listMobileNo.add(phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
listName.add(phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
}
phones.close();
Here is the query I ended up with
String where = ContactsContract.Data.MIMETYPE + "=? OR " + ContactsContract.Data.MIMETYPE + "=?";
String[] whereValues = new String[]{ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE};
String[] dataProjection = new String[]{ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY, ContactsContract.Data.DISPLAY_NAME_PRIMARY, ContactsContract.Data.STARRED, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA_VERSION};
Cursor cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, dataProjection, dataWhere, dataWhereValues, ContactsContract.Data.CONTACT_ID + " ASC");
int cidIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.CONTACT_ID);
int nameIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.DISPLAY_NAME_PRIMARY);
int starredIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.STARRED);
int typeIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE);
int data1Index = cursor.getColumnIndexOrThrow(ContactsContract.Data.DATA1);
int data2Index = cursor.getColumnIndexOrThrow(ContactsContract.Data.DATA2);
int lookupKeyIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.LOOKUP_KEY);
int versionIndex = cursor.getColumnIndexOrThrow(ContactsContract.Data.DATA_VERSION);
boolean hasData = cursor.moveToNext();
while (hasData){
contactId = cursor.getLong(cidIndex);
nativeLookupKey = cursor.getString(lookupKeyIndex);
fullName = cursor.getString(nameIndex);
starred = cursor.getInt(starredIndex);
version = cursor.getLong(versionIndex);
while (currentContactId <= contactId && hasData) {
if (currentContactId == contactId) {
String type = cursor.getString(typeIndex);
if (type.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
String email = cursor.getString(data1Index);
} else if (type.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
String number = cursor.getString(data1Index);
}
}
hasData = cursor.moveToNext();
if (hasData) {
currentContactId = cursor.getLong(cidIndex);
}
}
}