I'm trying to show a list of contacts along with the photo associated to them, but I see most of the contacts without the photo although they do have a photo when I go to the phone's (SG2) default contact list. I figured that this is because some of those contacts are actually merged, and the picture comes from the (raw?) contact that is not displayed in the list, so the question is - how do I get all the contacts merged with a given contact? I tried to play with the SQLite, but I can't find any column that will uniquely identify merged contacts.
From code perspective, I started with this method:
protected Bitmap getImageByContactId(final Context appContext, final String strContactId)
{
Bitmap bmContactImage = null;
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, Long.parseLong(strContactId));
Uri photoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
InputStream image = null;
try {
image = appContext.getContentResolver().openInputStream(photoUri);
bmContactImage = BitmapFactory.decodeStream(image);
try {
image.close();
} catch (IOException e ) {
e.printStackTrace();
}
} catch (FileNotFoundException e1) {
// This can be ignored because the Uri is generic and photo may not really exist
}
return bmContactImage;
}
but this only returned photos associated to the "original" contact that matched the original search criteria (in my case - the contact id) - not if the picture was associated to a contact linked to the original contact.
I was hoping to make something like the following work, if I could only get the (raw?) id of the contacts linked/associated/merged with the original contact, but I can't seem to find a way to find those "other" contacts...
public static Bitmap loadContactPhoto(ContentResolver cr, long id) {
Uri uri = Uri.parse(String.format("content://com.android.contacts/contacts/%s", id));
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
if (input == null) {
return null;
}
return BitmapFactory.decodeStream(input);
}
Bottom line - what I'm trying to do is to show a photo of a contact, regardless whether it is from the contact matching the search criteria - or any of the contacts merged with that contacts - and any solution will be appreciated!
Thanks!
(I got here from our facebook page..)
here is how I get all contacts from my content resolver:
public void addContactsMultiple()
{
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cur.getCount() > 0) {
String id="";//Contact ID
String name="";//Contact Name
id = "";
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if(Integer.parseInt
(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
//Getting the Contact Image
if(cur.getString(cur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI)) != null)
{
imagePath = cur
.getString(cur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
}
else {imagePath="none";}//If there is no contact image
}
}
cur.close();
}
In my code you get the image URI and than put it in your Contact object, than you can use
ImageView image = (ImageView)findViewById(R.id.image)
image.setImageURI(Uri.parse(imagePath));
or you can use Picasso Image Loader:
Picasso
and than use this code:
Picasso.with(context)
.load(imagePath)
.skipMemoryCache()
.into(image );
As You can see, I don't use BitMaps or decodeStream.
You only need the uri of the photo (which directs your image to the Android gallery)
if you get several contacts from the same name you can use this code to marge them:
//(mContactList) is the ArrayList of contacts object I have created)
HashSet<Contact> hs = new HashSet<Contact>();
hs.addAll(mContactList);
mContactList.clear();
mContactList.addAll(hs);
this code will delete duplications
with a little adjustments you can get the image uri to the contact it belongs..
come back if you have more questions.
Related
I am trying to implement a feature like Instagram or WhatsApp, where the thumbnail of a single image that exists in a folder in android, is shown on top of a list item, more like a sample of what kinds of image are in the folder.
Help me to understand this feature.
How I implemented it. It might not be the best though, but it works.
I fetched the URIs of all the images using MediaStore, you can learn how to use it here.
The First step was done in a background thread to prevent it from blocking the UI thread.
I sorted out the images I got, grouping them in a List<Image>, which would represent a single directory.
I then added the List<Image> into a List<List<Image>>, which served as the overall images that were fetched and have their total size which I used later to track the number of images in the directory.
The code is below.
#Override
public void run() {
Uri storageUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
storageUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
storageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
// the queries to the MediaStore API (The image details or metadata I need
String[] projection = {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media.DISPLAY_NAME};
// now query the MediaStore API using ContentResolver
Cursor imgCursor = getApplicationContext().getContentResolver().query(storageUri, projection, null, null, null);
int bucketId = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
int imgSize = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.SIZE);
int name = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
int bucketName = imgCursor.getColumnIndexOrThrow(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
// directoryDictionary is a temporary list of directory names that was found, while querying the MediaStore API
List<String> directoryDictionary = new ArrayList<>();
// generalList is just a list that would represent a general image list, where all images can be found. Just like Whatsapp
List<Image> generalList = new ArrayList<>();
while (imgCursor.moveToNext()) {
long id = imgCursor.getLong(bucketId);
int size = imgCursor.getInt(imgSize);
String fileName = imgCursor.getString(name);
String folderName = imgCursor.getString(bucketName);
// As recommended by the Android developers doc
Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
// a single image
Image currentImage = new Image(contentUri, size, fileName, folderName);
// add all images to the general image list, but modifying the directory name
Image genImage = new Image(contentUri, size, fileName, "All Media");
generalList.add(genImage);
int directoryIndex = CollectionUtils.linearSearch(directoryDictionary, folderName);
// if search result (directoryIndex) passes this test, then it means that there is
// no such directory in list of directory names
if (directoryIndex < 0) {
imageDirectoryList.add(new ArrayList<>());
directoryDictionary.add(folderName);
directoryIndex = CollectionUtils.linearSearch(directoryDictionary, folderName);
if (directoryIndex >= 0)
imageDirectoryList.get(directoryIndex).add(currentImage);
} else {
imageDirectoryList.get(directoryIndex).add(currentImage);
}
}
//...then add it if the image list of folder is > 2
if (imageDirectoryList.size() > 2) imageDirectoryList.add(0, generalList);
imgCursor.close();
runOnUiThread(() -> {
// imageAdapter is the RecyclerView's list Adapter.
// notifyDataSetChanged() must be call to refresh list.
imageAdapter.notifyDataSetChanged();
// doViewUpdate was just used to turn on and off the visibility of some views
doViewUpdate();
});
}
I am trying to use DocumentsContract to traverse a directory recursively using the following method.
void traverseDirectoryEntries(Uri rootUri) {
ContentResolver contentResolver = activityMain.getContentResolver();
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
rootUri, DocumentsContract.getTreeDocumentId(rootUri));
List<Uri> dirNodes = new LinkedList<>();
dirNodes.add(childrenUri);
while (!dirNodes.isEmpty()) {
childrenUri = dirNodes.remove(0); // get the item from top
try (Cursor cursor = contentResolver.query(childrenUri, new String[]{
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_MIME_TYPE},
null, null, null)) {
if (cursor != null) {
while (cursor.moveToNext()) {
final String docId = cursor.getString(0);
final String name = cursor.getString(1);
final String mime = cursor.getString(2);
if (isDirectory(mime)) {
final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(
rootUri, docId);
traverseDirectoryEntries(newNode);
}
}
}
}
}
}
// Util method to check if the mime type is a directory
private static boolean isDirectory(String mimeType) {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
Unfortunately, this does not work. It goes through the same root directory over and over despite the directory passed in being a child of the root. The line final Uri newNode = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, docId); returns a Uri with the child directory attached at the end. That works fine. The problem is the follwing line: Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, DocumentsContract.getTreeDocumentId(rootUri)); where rootUri is the newNode. I get the same childrenUri; the one I got from rootUri. Is this a bug? Did I make a mistake? Is the docId wrong? I tried getDocumentId(rootUri), but that throws an error because the id it returns is not a tree. It is almost like getTreeDocumentId(rootUri) is returning what getRootId(rootUri) should be returning. Is there a way to fix this?
The rootUri was obtained via ACTION_OPEN_DOCUMENT_TREE. I am running this on Android Pie. I left some code out where I am building a tree structure from the data.
You are abusing this well known void traverseDirectoryEntries(Uri rootUri) function.
You think it is recursive but it is not.
Remove the recursive call and instead add 'newNode' to the list.
// traverseDirectoryEntries(newNode);
dirNodes.add(newNode);
Then you make use of the list.
Then original code is restored.
Who changed it without telling so?
Of course you can adapt this function to make it recursiv.
Nice exercise ;-).
So I am working on a project where I need to get the user's contact list (Specifically the Name, email address, and location of the contact details), put that into a list, and then use that list in an autocomplete view so they can start typing a name and it will filter it out.
My code works just fine, it all compiles and all runs without error. The problem is that it is VERY slow. For someone who has 10 contacts, this will likely not take long, but my phone holds 1700 contacts in it so this entire process takes upwards of 2 minutes to complete... which is horrendous!
Here is the current working code below (I cut out the code for the location adding as it was wordy):
public static List<MyObject> getContactList(){
List<MyObject> contactList = new ArrayList<>();
Cursor people = MyApplication.getAppContext().getContentResolver().
query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (people.moveToNext()) {
String email = "";
String location = "";
String phone = "";
String contactName = people.getString(people.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
String contactId = people.getString(people.getColumnIndex(
ContactsContract.Contacts._ID));
if(contactId != null){
Cursor contactsEmails = getSpecificEmailsCursor(contactId);
while (contactsEmails.moveToNext()){
//For now, just setting it to the last email
email = contactsEmails.getString(contactsEmails.getColumnIndex(
ContactsContract.CommonDataKinds.Email.DATA));
phone = contactsEmails.getString(contactsEmails.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
}
contactsEmails.close();
MyObject person = new MyObject();
try{
person.setEmail(email);
person.setName(contactName);
person.setPhone(phone);
} catch (NullPointerException e){
L.m("Null pointer hit on person");
}
if(contactName != null && email != null){
contactList.add(person);
}
}
}
people.close();
return contactList;
}
Does anyone have any recommendations on how to either speed this process up, or, a better way to go about what I am trying to accomplish? Thanks!
-Sil
i need to get all the MMS Data detalis like mms_image, address,date and type.
i am using following logic. In this i am using two cursors, one for images and other for remaining fields. but the size of two cursors are different. so, i am unable to match both image and other fields.
//for date,address,type
Cursor curPdu = getContentResolver ().query(Uri.parse("content://mms"), null, null, null, null);
while(curPdu.moveToNext())
{
String id = curPdu.getString (curPdu.getColumnIndex ("_id"));
String date = curPdu.getString (curPdu.getColumnIndex ("date"));
mms_add.add(getAddressNumber(Integer.parseInt(id)));
int type = Integer.parseInt(curPdu.getString(curPdu.getColumnIndex("m_type")));
mms_type.add((type==128)?"2":"1");
mms_date.add(getDate(Long.parseLong(date)));
}
//for image
Cursor curPart = getContentResolver (). query (Uri.parse ("content://mms/part"), null, null, null, null);
while(curPart.moveToNext())
{
coloumns = curPart.getColumnNames();
if(values == null)
values = new String[coloumns.length];
for(int i=0; i< curPart.getColumnCount(); i++)
{
values[i] = curPart.getString(i);
}
if(values[3].equals("image/jpeg"))
{
mms_image.add(GetMmsAttachment(values[0],values[12],values[4]));
}else{
mms_image.add("null");
}
}
so, please guide me how to get all the details using one cusor if possible.
You can try the solution and url provided here.
And may i know why you need 2 cursor ? I assume there is some mms without image attached so that's the reason you get a different count.
I have created a Project having many activities. One activity is to record the video, that is working fine. I can see the recorded video in my specified folder without restart my tablet.
But when I try to find all the videos in that folder in some other activity using query, see code below. Then I can't see my recorded video until I restart my tablet. I can see just old recorded videos before starting my tablet. I couldn't understand this strange behavior.
Can anyone put some light on this issue??
Thanks.
private void initVideosId() { // getting the videos id in Video Folder of SD Card
try {
// Here we set up a string array of the thumbnail ID column we want
// to get back
String[] proj = { _ID };
//Querying for the videos in VideoGallery folder of SD card
// Now we create the cursor pointing to the external thumbnail store
_cursor = managedQuery(_contentUri, proj, // Which columns to return
MEDIA_DATA + " like ? ", // WHERE clause; which rows to
// return (all rows)
new String[] { "%VideoGallery%" }, // WHERE clause selection
// arguments (none)
null); // Order-by clause (ascending by name)
int count = _cursor.getCount();
// We now get the column index of the thumbnail id
_columnIndex = _cursor.getColumnIndex(_ID);
// initialize
_videosId = new int[count];
// move position to first element
_cursor.moveToFirst();
for (int i = 0; i < count; i++) {
int id = _cursor.getInt(_columnIndex);
//
_videosId[i] = id;
//
_cursor.moveToNext();
//
}
} catch (Exception ex) {
showToast(ex.getMessage().toString());
}
}
If you stored the file on external storage, you need to use MediaScannerConnection to get the MediaStore to index that file, such as:
MediaScannerConnection.scanFile(
this,
new String[] {file.getAbsolutePath()},
null,
new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
// do something if you want
}
});