Search code examples
androidlaravelandroid-recyclerviewpicassoandroid-glide

Displaying images in RecyclerView using Picasso (or Glide) and retrofit, for authenticated users only


I have been using recyclerview to display my contacts and show their basic data including user images. It works as it should, Picasso is great and I get a very cool looking list.

Right now I am trying to disable users to view images until validated - ie I want to disable viewing images to anyone who is not an authenticated user. I have done this on my services and disabled anyone except my laravel app to open the image link. Now I face one issue - when I return image it is not a link, but it is in a byte[] format, and I cannot display it using Picasso. I could try and convert it to Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); and then convert bitmap to drawable like: Drawable drawable = new BitmapDrawable(getResources(), bitmap);, and then display drawable as image imageView.setImageDrawable(drawable); but reading into this I found quite a few drawbacks.

My question would be what is the best way to display image using picasso or glide, and retrofit to return images for authenticated users only. Is there a different way I can return image from laravel service? Is there a way to use the byte format with either picasso or glide? Or is there another format I can convert my byte[] to display in the recyclerview, that would not negatively influence performance?

Currently displaying image with Picasso in my ContactsAdapter:

@Override
public void onBindViewHolder(ContactsAdapter.ContactsViewHolder holder, int position) {
    Contact contact = contactList.get(position);

    holder.userName.setText(contact.getUserName());

    TextDrawable.IBuilder builder = TextDrawable.builder()
            .beginConfig()
            .withBorder(0)
            .toUpperCase()
            .endConfig()
            .round();

    ColorGenerator generator = ColorGenerator.MATERIAL;
    int color = generator.getColor(contact.getUserId());
    textDrawable = builder.build(contactList.get(position).getUserName().substring(0, 1), color);
    holder.thumbNail.setImageDrawable(textDrawable);

    Picasso.with(context)
            .load(AppConfig.URL_PROFILE_PHOTO + contact.getThumbnailUrl()) // DISK URL WITH USER IMAGE NAME
            .placeholder(textDrawable)
            .error(textDrawable)
            .transform(new CircleTransform())
            .into(holder.thumbNail);
}

Laravel service converts link to image like this:

return Image::make($storagePath)->response();

I extracted the ProfileImage method and tried to modify it to use the service I got:

private void ProfileImage(HashMap<String, String> user, String name, String id) {
    TokenManager tokenManager = TokenManager.getInstance(getSharedPreferences("prefs", MODE_PRIVATE));
    ApiInterface apiService = ApiClient.createServiceWithAuth(ApiInterface.class, tokenManager);

    ColorGenerator generator = ColorGenerator.MATERIAL;
    int color = generator.getColor(id);
    String firstLetter = name.substring(0, 1);
    TextDrawable textDrawable = TextDrawable.builder().buildRect(firstLetter, color);
    holder.thumbNail.setImageDrawable(textDrawable);

    Call<ResponseBody> call = apiService.getUserImage(id);
    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {

            if (response.isSuccessful()) {
                byte[] bytes = new byte[0];
                try {
                    bytes = response.body().bytes();

                    Picasso.with(getApplicationContext())
                            .load(/** I CANNOT USE THE URL HERE */)
                            .placeholder(textDrawable)
                            .error(textDrawable)
                            .centerCrop()
                            .fit()
                            .into(holder.thumbNail);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                if (response.code() == 401) {
                    finish();
                } else {
                    Log.d(TAG, "There was an error: " + response);
                }
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });
}

Solution

  • If you can switch to Glide, you can use Glide.with().load(byte[] model) directly

    bytes = response.body().bytes();
    
    Glide.with(getApplicationContext())
         .load(bytes)
         .placeholder(textDrawable)
         .error(textDrawable)
         .centerCrop()
         .fitCenter()
         .into(holder.thumbNail);