Is there a better way to load a picture in android studio? - android

I am making an app using Java in android studio, and in it I want users to have the option to set a custom profile picture. I'm using firebase storage to store the images for the PFPs, and taking them from there every time I load it, but it always takes around half a second or so to load the profile picture: gif of the problem
Here's my code for loading the pfp in the homepage (I am using de.hdodenhof.circleimageview.CircleImageView to make the ImageView circular):
public class HomePage extends AppCompatActivity {
de.hdodenhof.circleimageview.CircleImageView pfpImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_page);
pfpImage = findViewById(R.id.pfpImg);
Intent intent = getIntent();
if (intent != null && intent.hasExtra("UserEmail")) {
String userEmail = intent.getExtras().getString("UserEmail");
StorageReference userPfp = DBRef.refStorage.child("ProfileImages/Users/" + userEmail.replace(".",",")); //DBRef is a class I created to reference all Firebase related objects.
userPfp.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Picasso.with(HomePage.this).load(uri).into(pfpImage); // I am using com.squareup.picasso:picasso:2.5.2 to load the image from the Uri.
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(HomePage.this, "Profile Picture Loading Failed", Toast.LENGTH_SHORT).show();
}
});
}
else
pfpImage.setImageResource(R.drawable.doggo);
}
}
Is there some better way to load/store the pictures so they appear when the page loads?
EDIT: I managed to negate this by not loading from Firebase every time the activity is opened, but only the first time and saving it, using an idea from this post

There is other library named glide that can load picture faster when the page loads.
Here is the link for the reference.
https://guides.codepath.com/android/Displaying-Images-with-the-Glide-Library

Related

Android ListView constantly reloading image elements imported from firebase

In an android app I have made, the home page shows a list a games in a listview, each row being custom made to include a textview and an imageview and some buttons. The imageview's src comes from downloading th image from my google cloud firebase, and the functionality of this works well, but when the listview is scrolled through there is an issue. The images seem to be unloaded when scrolled away from, which causes a bit of lag when they are reloaded once scrolled back to. I imagine this is built in to prevent a listview from loading many high resolution images and keeping them loaded, but for my list, keeping the images loaded won't be a problem. Is there a way I can turn this off and just keep the images loaded? Here is a video of the problem:
https://www.youtube.com/watch?v=FYYJWZcvBy4&feature=youtu.be
here is the code of the listview and getting the image from firebase:
public void showData(DataSnapshot dataSnapshot){
TextView toolbarTitle = findViewById(R.id.toolbarTitle);
toolbarTitle.setText("Popular Games");
ArrayList<GameInformation> PopularGames = new ArrayList<>();
Iterator<DataSnapshot> items = dataSnapshot.child("Games").getChildren().iterator();
PopularGames.clear();
while(items.hasNext()){
GameInformation game = new GameInformation();
DataSnapshot item = items.next();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
Iterator<DataSnapshot> itemsDeep1 = item.child("consoles").getChildren().iterator();
while(itemsDeep1.hasNext()){
DataSnapshot itemDeep = itemsDeep1.next();
game.setConsoles(itemDeep.getValue(String.class));
}
Iterator<DataSnapshot> itemsDeep2 = item.child("genres").getChildren().iterator();
while(itemsDeep2.hasNext()){
DataSnapshot itemDeep = itemsDeep2.next();
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());
ArrayAdapter adapter = new CustomListAdapter(this, PopularGames);
mListView.setAdapter(adapter);
}
getting the image:
storageRef.child("Games/"+singleGame.getPicturePath()+".jpg").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
Glide.with(getContext()).load(uri).into(gameImageID);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Handle any errors
}
});
The delay you're facing is because there are 2 operations (each one takes a few secs to complete) being performed when you get each image:
The image is read from Firebase Storage using getDownloadUrl()
The image is displayed with Glide
I recommend that when you store the images, save their Download URL to the database instead of saving their path. This way, you'd no longer need to call for getDownloadURL(). You'd simply load the image with a single operation:
Glide.with(getContext()).load(singleGame.getPicturePath()).into(gameImageID);
Not related to the question:
You can simplify your code by using a forEach loop (instead of while) in your Iterators:
PopularGames.clear();
for(DataSnapshot item : dataSnapshot.child("Games").getChildren()){
GameInformation game = new GameInformation();
game.setGameName(item.child("Name").getValue(String.class));
game.setActiveLobbies(Integer.parseInt(item.child("Live Lobbies").getValue().toString()));
game.setPicturePath(item.child("FilePathName").getValue().toString());
for(DataSnapshot itemDeep: item.child("consoles").getChildren()){
game.setConsoles(itemDeep.getValue(String.class));
}
for(DataSnapshot itemDeep: item.child("genres").getChildren()){
game.setGenres(itemDeep.getValue(String.class));
}
if (game.getActiveLobbies() == 1){
PopularGames.add(game);
}
}
Log.d(TAG, "Hello Here" + PopularGames.size());

How to load GIF file from gallery || Android studio

How to load GIF file from gallery. As it can be lay from accessing the drawable or using the web url. But how to access it from gallery. not like the path i.e. file//folder1//images// file.gif
istead i need to load it via onclick> open gallery(show all files i.e. images, gif etc)> choose one gif and then it open into my app(GifImageView)
In your build.gradle file:
Compile this dependency.
dependencies {
compile 'com.felipecsl:gifimageview:2.1.0'
}
In your Activity class:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gifView = (GifImageView) findViewById(R.id.gifImageView);
gifView.setBytes(bitmapData);
}
#Override
protected void onStart() {
super.onStart();
gifView.startAnimation();
}
#Override
protected void onStop() {
super.onStop();
gifView.stopAnimation();
}
If you need to post-process the GIF frames, you can do that via GifImageView.setOnFrameAvailable() . You can see an example of that in the sample app included on the repository.
gifImageView.setOnFrameAvailable(new GifImageView.OnFrameAvailable() {
#Override
public Bitmap onFrameAvailable(Bitmap bitmap) {
return blurFilter.blur(bitmap);
}
});
You can also reset an animation to play again from the beginning gifImageView.resetAnimation(); or show a specific frame of the animation gifImageView.gotoFrame(3);

Access cached images (Picasso)

I'm having a loading screen where I fetch data belonging to the logged in user. Among this data is their profile images (three of them).
What I want to do is to cache the images when I download them with their URL in Loading screen. Then in the next activity I want to access the cached images to display them in that activity. So basically load all images in one activity to be used in another one. This way (I figured) I won't have to make a http request everytime user enters the activity where their profile image is shown. Which gives a better user experience.
So it's the matter of getting the images from cache to screen that I can't work out, cause I think I've loaded them into cache correctly, seen below.
Here's my current method in Loading Activity:
Backendless.Data.of(UserFileMapping.class).find(new AsyncCallback<BackendlessCollection<UserFileMapping>>() {
#Override
public void handleResponse(BackendlessCollection<UserFileMapping> response) {
Iterator<UserFileMapping> iterator = response.getCurrentPage().iterator();
while(iterator.hasNext()){
UserFileMapping fileMapping = iterator.next();
String profile1 = fileMapping.profile_url;
String profile2 = fileMapping.profile_url_2;
String profile3 = fileMapping.profile_url_3;
Picasso.with(getApplicationContext())
.load(profile1)
.fetch(new Callback() {
#Override
public void onSuccess() {
continue_button.setVisibility(View.VISIBLE);
loading_text.setText("Done!");
}
#Override
public void onError() {
//Make toast
}
});
}
}
#Override
public void handleFault(BackendlessFault fault) {
System.out.println("FAULT:" + fault.getCode());
}
});
It's the issue with downloading all three images at once as well, but that isn't as important as the caching question. If you have a good idea on this in addition to the cache issue I would gladly like to hear it too.

Fresco: Use current image displayed in Drawee as a placeholder for next request

I play multiple images sequentially on the same SimpleDraweeView, the issue is that when submitting a new imageURI request, theSimpleDrweeView will remove the current displayed image and replace it with nothing until the URI is downloaded. So it will leave gaps in the playing sequence ( you could think of what I'm trying to do is cartoon animation using local photos). What I would like for the SimpleDrweeView to leave the current image as is until the new one is downloaded and then just swap it when it's ready.
I tried using the low-res/high-res scheme from this ticket to put the old uri as a placeholder but that didn't work (had the same effect as before).
This is what I have now:
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri /* local image */);
And this is what I tried so far (didn't work):
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
Uri lowResUri, highResUri;
DraweeController controller = Fresco.newDraweeControllerBuilder().setTapToRetryEnabled(true)
.setLowResImageRequest(ImageRequest.fromUri((Uri) draweeView.getTag())) /*naive way to test the low/high res feature*/
.setImageRequest(ImageRequest.fromUri(uri))
.setOldController(draweeView.getController())
.build();
draweeView.setTag(uri);
draweeView.setController(controller);
I am part of the Fresco team and may be able to help.
It is strange that you experience the same issue with low-res/high-res combination. If the image is currently displayed, it means it should be in the bitmap memory cache, which in turn means that it should be able to load immediately when set as a low-res image the next time you are switching to the next frame.
Are you sure that you are setting the correct uri as low-res image? (Uri) draweeView.getTag() looks kind of suspicious. I would double check that part.
If the uri is indeed correct, but the image is not in the bitmap cache anymore, it would be worth investigating why the image that is visible is not cached anymore, as we have explicit logic in place that should prevent evicting visible images. See how to track this with verbose logging here.
If all of the above fails, third options is to actually implement your own DataSource. I can help with that, but this might be somewhat involving. The basic idea is to implement a DataSource that wraps another DataSource that actually provides the image. Then you could do something like this:
// keep this instance somewhere
mMyDataSourceSupplier = new MyDataSourceSupplier();
// build controller by specifying your custom datasource supplier instead of specifying any URIs.
Fresco.newDraweeControllerBuilder()
.setDataSourceSupplier(mMyDataSourceSupplier)
.build()
// later, when you need to change the image do
mMyDataSourceSupplier.setUri(nextUri);
// this is just an outline
class MyDataSourceSupplier implements Supplier<DataSource<CloseableReference<CloseableImage>>> {
private Uri mCurrentUri;
private DataSource<CloseableReference<CloseableImage>> mCurrentDataSource;
public void setUri(Uri uri) {
mCurrentUri = uri;
if (mCurrentDatasource != null) {
mCurrentDataSource.setUri(uri);
}
}
#Override
public DataSource<CloseableReference<CloseableImage>> get() {
mCurrentDataSource = new MyDataSource();
mCurrentDataSource.setUri(uri);
return mCurrentDataSource;
}
private class MyDataSource extends AbstractDataSource<CloseableReference<CloseableImage>> {
private DataSource mUnderlyingDataSource;
#Override
protected void closeResult(#Nullable CloseableReference<CloseableImage> result) {
CloseableReference.closeSafely(result);
}
#Override
#Nullable
public CloseableReference<CloseableImage> getResult() {
return CloseableReference.cloneOrNull(super.getResult());
}
#Override
public boolean close() {
if (mUnderlyingDataSource != null) {
mUnderlyingDataSource.close();
mUnderlyingDataSource = null;
}
return super.close();
}
public void setUri(Uri uri) {
if (mUnderlyingDataSource != null) {
mUnderlyingDataSource.close();
mUnderlyingDataSource = null;
}
if (uri != null && !isClosed()) {
mUnderlyingDataSource = Fresco.getImagePipeline().fetchDecodedImage(ImageRequest.fromUri(uri), null);
mUnderlyingDataSource.subscribe(new BaseDataSubscriber {
#Override
protected void onNewResultImpl(DataSource<List<CloseableReference<CloseableImage>>> dataSource) {
MyDataSource.super.setResult(dataSource.getResult(), false);
}
});
}
}
}
}

Picasso errors on callback

I've noticed that occasionally images won't load in my app through picasso and that picasso is in fact erring. I am using two images per list item in a list view. Here's the picasso code:
Picasso.with(DashboardActivity.this).load(status).into(iv_customer_status_pic, new Callback() {
#Override public void onSuccess() {
Log.d("Debug", "Picasso Success");
}
#Override public void onError() {
Log.d("Debug", "Picasso Errored");
}
});
How can I ensure that the images are loaded, I don't want them to error and then make them disappear. Also why does it error? Is there a timeout? I noticed on more powerful devices it happens less.
The reasons why it fails might because of no Internet connection and Invalid Image URL.
With regard to the error handling refer to nPn's answer.
The reason the onError() callback for Picasso.with().load().into(target, callback) exists is because there is no 100% guarantee the load will be successful. For example if you are trying to load from a uri and you don't have an internet connection, the load will not be successful.
You can somehow attempt a re-try (which I think is already built into Picasso), but ultimately, you need to handle the case were the load fails (for whatever reason). One option would be to load a "default" image, like a generic "profile picture" if you were trying to load a specific users profile picture.
If you move the implementation of the callbacks to a separate class , or even the containing class you should be able to retry from the onError() call back. Here is what I am thinking:
class ContainingClass implements Callback.EmptyCallback
private int mRetryAttempts = 0;
#Override
public void onError() {
if (mRetryAttempts < 2) {
mRetryAttempts++;
// try again
} else {
mRetryAttempts = 0;
}
}
#Override
public void onSuccess() {
mRetryAttempts = 0;
}

Categories

Resources