Loading images with Picasso is seemingly so easy, until I hit this roadblock. Not sure why! I can load photos from contacts via PHOTO_URI if the contacts only have a thumbnail, or, if I instead ask for PHOTO_THUMBNAIL_URI specifically.
#Override
public void bindView(View view, Context context, Cursor cursor) {
ImageView icon = (ImageView)view.findViewById(R.id.ContactImage);
String photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI));
if (photoUri == null) {
icon.setImageDrawable(null);
} else {
Picasso.with(context).load(photoUri).into(icon);
}
}
For what it's worth: if I use Picasso.with(context).load(photoUri).placeholder(R.drawable.placeholder).error(R.drawable.error).into(icon); then I see the placeholder image in the place of every contact who has a high res image. I never see an "error" picture. If I revert back to just using icon.setImageURI(Uri.parse(photoUri)); then I see the high res contact images again just fine. (But then I don't have a snazzy async caching picture loader!)
UPDATE: Thanks to #copolii and his answers below, the following now works flawlessly with Picasso 2.1.1:
#Override
public void bindView(View view, Context context, Cursor cursor) {
Long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
String photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI));
ImageView icon = (ImageView)view.findViewById(R.id.ContactImage);
if (photoUri == null) {
icon.setImageDrawable(null);
} else {
Picasso
.with(context)
.load(contactUri)
.into(icon);
}
}
This loads the higher-res photo, if there is one, and if not, shows the low-res photo, and if there is no photo set for a contact, it's set to a blank / null.
Have you tried using a contact uri?
That last boolean parameter in openContactPhotoInputStream promises to get you the high res photo if one is available.
Instead of using a photo uri use a contact uri or a contact lookup uri.
UPDATE
Since the question has been answered, I though I'd post the relevant details here:
A small test app is posted here (You need Android Studio): https://github.com/copolii/PicassoContactsTest
If you set both a placeholder and an error icon, the error icon is displayed for contacts who do not have a picture. I'd recommend setting the social face guy as your place-holder and no error icon. That way, your place-holder stays on if the contact has no picture.
If you do want to differentiate between the two, choose your error icon with the above in mind (i.e. don't use a big red OMFG error indicator).
--- Previous Content ---
Let me know if that helps.
I did the work for the contacts photo loading and unless I'm missing something, you should get the high resolution picture (API 14+) automatically:
if (SDK_INT < ICE_CREAM_SANDWICH) {
return openContactPhotoInputStream(contentResolver, uri);
} else {
return openContactPhotoInputStream(contentResolver, uri, true);
}
It seems that the openContactPhotoInputStream doesn't like the PHOTO_URI.
Android Docs: openContactPhotoInputStream
If the URIs are distinguishable I can easily add support for PHOTO_URI as well (I have to find out how to load it first though). I'm already determining if the given uri is a contact photo uri or a contact lookup uri (older android versions do not like lookup uris being fed into openContactPhotoInputStream so I have to dereference the lookup uri into a contact uri before passing it to openContactPhotoInputStream).
I hope this helps.
Related
I'm new to android developement and I'm supposed to use Java as the programming language. I have an app where I'm supposed to be able to capture images and the geographical location of the captured images and display these details. I am displaying the image in an imageView. I have a text file where I'm storing image links as well as the captured images. So, I basically have image links and captured images that are stored in an arraylist then to a text file.
Please feel free to ask for anything that I may have missed out in the question.
I tried using EXIFInterface method I found on a Stack Overflow response, I tried using Location provider but to no avail. Maybe where I'm placing the code is incorrect, as I said, I'm new to this. I tried watching YT videos and did some research online and I'm more confused than ever at this point. Another approach I tried using was capturing the current location of the device to an invisible textView then calling it to where the image name is being stored but this did not work either.
The EXIF method I tried:
`
try {
ExifInterface exifInterface = new ExifInterface(direct); //Direct is the filepath
Log.d("Latitude", exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
Log.d("Longitude", exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
} catch (IOException e) {
e.printStackTrace();
}
`
Location Provider method
#SuppressLint("MissingPermission")
private void showLocation() {
locationProvider.getLastLocation().addOnSuccessListener(this,
new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location != null) {
hiddenLoc.setText("Current location is: Lat:" + location.getLatitude()
+ "Lon: " + location.getLongitude());
}
}
});
}
`
EXIF location is an optional interface- most images won't have one. In fact many (most?) camera apps have stopped using it by default to protect user privacy. You can try it, but don't expect it to be there.
Your location code- lastLocation will return null unless location was already up and running (generally because another app was using it). You'd need to request location updates, rather than rely on lastLocation. Please note that this gets the location of the phone now not the location when a photo was taken. So this only works if you run it when you take the photo (the exif data gets the location where the photo was taken, if its there at all).
As known, ALBUM_ART was deprecated in Android 11. Google says that ContentResolver.loadThumbnail should be used instead. But i totally don't understand how to use it, especially what i should provide as first parameter, uri. Documentation says:
Uri: The item that should be visualized as a thumbnail. This value cannot be null.
What is that item and how can I get it? Is this Uri of the music file?
You need to pass in the Uri of the track.
//This will get you the uri of the track, if you already have the track id
Uri trackUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, trackId);
Bitmap bm = contentResolver.loadThumbnail(trackUri, new Size(512,512), null);
Set the bitmap to an ImageView
imageView.setImageBitmap(bm);
EDIT:
I asked a similar question a day before and I wrote this post based on information I gathered since that question -
First Cache Question. I know they are similar but this question is more concise to avoid extra information. I also didn't want to delete that post since it was answered, even though the answer didn't suffice.
I use a recyclerview to show photos I get from google places api. I know that I am not allowed to legally cache photos outside of runtime. I didn't know that was happening until I randomly opened my phone's gallery to see a lot of copies of the photos I get from google.
My first guess was that my use of Picasso was the issue so I added code to fix it,
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE)
finding these in StackOverflow was pretty simple, but this didn't fix anything except that now it seems to only download them once especially when I delete them. I believe I eliminated the possible issues and am outlining the last one in this question.
private Uri getImageUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}
#Override
public void onBindViewHolder(#NonNull VenueViewHolder venueViewHolder, int i) {
Collections.sort(vIAL, (o1, o2) -> o1.getVenueName().compareToIgnoreCase(o2.getVenueName()));
VenueItem currentItem = vIAL.get(i);
Picasso.with(context)
.load(getImageUri(context, currentItem.getVenueImage()))
.fit()
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE)
.into(venueViewHolder.vIV);
venueViewHolder.vTV.setText(currentItem.getVenueName());
Log.i(TAG, "The photo should have been on screen");
}
The URI method getImageUri is something I found as an answer to another problem I had, that I needed a URI to implement the Picasso library so that I can manipulate the photos before displaying them.
My question is - How do I remove the photos when the app closes?
UPDATE:
I changed my tactic to see what would happened and used Glide
#Override
public void onBindViewHolder(#NonNull VenueViewHolder venueViewHolder, int i) {
Collections.sort(vIAL, (o1, o2) -> o1.getVenueName().compareToIgnoreCase(o2.getVenueName()));
VenueItem currentItem = vIAL.get(i);
Glide.with(context)
.load(currentItem.getVenueImage())
.into(venueViewHolder.vIV);
venueViewHolder.vTV.setText(currentItem.getVenueName());
}
and it gave a fatal Error
E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 4344032)
This was one of the errors and it didn't occur the first time I ran this new code but it got worse the second and third time I ran it.
I shifted my code based on an answer I got early from #Padmini S but they used a url in the load part and I pass a bitmap because for the life of me I can't figure out how to get a URL from Google Places API instead of the code they provide in
Google Place Photos.
I'm relatively new to coding so this is me trying to piece together what I need to learn more. I'm just out of ideas of what to search for so I ask here,
based on the new information I gathered from the answer, can I replace
MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
in my code so that my photos don't get saved to my phone's photos?
Final Update
I ended up rewriting a lot of the code surrounding this and deleting this code. I ended up making an arraylist class to hold the array list for the duration of runtime and it let me remove most of the extra code I wrote out of ignorance.
I really don't know whether it'll solve your problem or not , but you are storing the images in device local storage here,
private Uri getImageUri(Context inContext, Bitmap inImage) {
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
}
I have never used google places api but usually apis will return you with an image url you can store that url in POJO class and display it directly in recyclerview row image view like I am doing in below code,
public void onBindViewHolder(#NonNull ViewHolder viewHolder, final int i) {
viewHolder.description.setText(links.get(i).getTitle());
Glide.with(context).load(links.get(i).getImage_url()).into(viewHolder.image);
}
This is a problem I found exclusively on Xiaomi Redmi Note 4 device (MIUI Global 8.1, Marshmallow)
So I'm making an app that generates a bitmap and saves it to FOOD folder. I have successfully generated the image and saved it to FOOD folder.
However, some of the images don't show on Gallery App. Specifically Xiaomi's Gallery App and Google's Photos. Note that some images DO show on Gallery App.
My question are:
How to show the images in Gallery after saving the image?
What exactly is causing this problem?
This really confuses me since it works on other devices with different OS.
This is what I have tried so far:
I've tried using the Intent.ACTION_MEDIA_SCANNER_SCAN_FILE and MediaScannerConnection.scanFile.
try {
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
MediaStore.Images.Media.insertImage(H5Environment.getContext().getContentResolver(), imagePath,
fileName, null);
} catch (Exception e) {
DanaLog.e(TAG, e);
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(new File(imagePath));
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
MediaScannerConnection.scanFile(this,
new String[]{imagePath},
new String[]{"image/png"},
new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
Log.d(TAG, "scan complete " + path);
}
});
I have also restarted the phone and it still doesn't show.
In Settings, I have enable Show hidden album but it doesn't show as well.
Current analysis:
I don't think there's a problem with the image itself because I can see it via File Manager.
The image path should also correct since some image is shown in the Gallery App.
Note:
I see a forum discussion on this and they suggest to 3rd party app such as Rescan SD Card!. But obviously, it was not the solution I was looking for.
try this for saveImage and notify Gallery
https://github.com/wuapnjie/StickerView/blob/master/sticker/src/main/java/com/xiaopo/flying/sticker/StickerUtils.java
I am working on an application that makes use of a dynamically populated list view of which each item contains an image that needs to be downloaded. Additionally, when clicking on any item in the list, it opens a detail page which once again shows the same image in addition to a few more.
As we know, it is best to use an async task to do the actual download and good practice to use a caching mechanism to both speed up image display as well as minimize actual data usage in multiple downloads.
After searching around for quite a bit, I ended up using the Google example code located at http://developer.android.com/resources/samples/XmlAdapters/src/com/example/android/xmladapters/ImageDownloader.html which claims to be both an async and caching method to download images.
The problem I am having, when called, this class does not appear to be caching the images correctly. So here goes ....
The portion of code in my custom class (that builds the list view) where I call the image download is this:
ImageDownloader getimage = new ImageDownloader();
getimage.download(logoURL, ivLogo);
With ImageDownloader being the class referenced above.
Images load, but it is apparent they are loading from the web each time. I added some logging into ImageDownloader.class to help track what is happening when. What I found, the first time the image is requested, it fails the first call to get from cache (as expected), getBitmapFromCache returns null and forceDownload is called.
public void download(String url, ImageView imageView, String cookie) {
resetPurgeTimer();
Bitmap bitmap = getBitmapFromCache(url);
Log.i(LOG_TAG, "Bitmap is: " + bitmap);
if (bitmap == null) {
Log.i(LOG_TAG, "Forcing Download");
forceDownload(url, imageView, cookie);
} else {
cancelPotentialDownload(url, imageView);
imageView.setImageBitmap(bitmap);
}
}
As execution progresses down the class to the point where it adds the image/url to the cache
private void addBitmapToCache(String url, Bitmap bitmap) {
Log.i(LOG_TAG, "Incoming Add Cache Info: URL: " + url + " Bitmap: " + bitmap);
if (bitmap != null) {
Log.i(LOG_TAG, "Entering the add image to cache section");
synchronized (sHardBitmapCache) {
sHardBitmapCache.put(url, bitmap);
Bitmap returnedbitmap = getBitmapFromCache(url);
Log.i(LOG_TAG, "Returned bitmap immediately after adding: " + bitmap);
}
}
}
I added logging to both verify the cache was added, and then performed a getBitmapFromCache, and the logs show the returned bitmap/url hash returns properly.
Incoming request to get Image, URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Trying Hard Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Hard Bitmap is: null
Trying Soft Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Return from initial entry call: null
Entering forced Download
Incoming Add Cache Info: URL:http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.pngBitmap: android.graphics.Bitmap#40762f28
Entering the add image to cache section
Trying Hard Cache withURL: http://www.yourlogoresources.com/wpcontent/uploads/2011/11/Wendys-logo.png
Hard Bitmap is: android.graphics.Bitmap#40762f28
Returned bitmap immediately after adding:android.graphics.Bitmap#40762f28
So it appears the cache is being populated properly, however, the next time that image is requested (ie when you scroll the listview), the cached image is not found again with getBitmapFromCache(url) returning null from the entry point download.
Incoming request to get Image, URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Trying Hard Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Hard Bitmap is: null
Trying Soft Cache with URL: http://www.yourlogoresources.com/wp-content/uploads/2011/11/Wendys-logo.png
Return from initial entry call: null
Entering forced Download
So I am at a loss here as to why it appears the cache is populated, but when it is checked again, nothing but null is returned.
EDIT
I ended up using a static reference to the ImageDownloader from my main Activity as follows:
public class myActivity extends Activity implements OnClickListener {
public final static ImageDownloader GetImage = new ImageDownloader();
Then called that like this anywhere I needed an ImageDownload:
myActivity.GetImage.download(logoURL, ivLogo);
Thanks for all the help folks.
Look into the code of ImageDownloader.
It is using a primary cache
private final HashMap<String, Bitmap> sHardBitmapCache
and a secondary static cache
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache
Entries kicked out of the primary cache are put into the secondary cache.
Now everytime you want to download an Image, if you create a new instance of ImageDownloader, you are effectively not using the caching mechanism because each ImageDownloader instance will only have that single image you downloaded using this instance.
PS: Your code in the question looked like you are using an instance per download.