Search code examples
androidfirebasefirebase-realtime-databaseandroid-recyclerviewfirebaseui

How to manage multiple download with Firebase and RecyclerView


I have a RecyclerView and FirebaseRecyclerAdapter with items. Each item has reference to some files in Firebase storage. each item has download button. I use storageReference.getFile(location) to download files from firebase storage. When I click on download button and wait for the download to finish, the checked icon (green icon) is showing correctly at specific position.

My problem is when I download multiple items at same time (when I click on multiple items), the green icon is not displays at appropriate position on every item.

How can I manage multiple download on every item simultaneously so that the green icon can be shown at specific position when specific item download finished?

The code in OnBindViewHolder behind the download button.

holder.imgDocumentChecker.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(final View view) {
        holder.imgDocumentChecker.setVisibility(View.GONE);
        holder.txtProgress.setVisibility(View.VISIBLE);
        holder.progressBar.setVisibility(View.VISIBLE);
        final Document item = getItem(holder.getAdapterPosition());
        final File parent = fileHelper.parentFolderGenerator(item);
        counter = 0;
        if (item.getFirebaseStorageFiles() != null && !item.getFirebaseStorageFiles().isEmpty()) {
            if (parent.listFiles() == null) {
                final String[] files = item.getFirebaseStorageFiles().split("____");
                if (files != null && files.length > 0) {
                    for (int i = 0; i < files.length; i++) {
                        String fileName = files[i].substring(files[i].lastIndexOf("/") + 1);
                        String storageFileUrl = Constants.ROOT_FIREBASE_STORAGE_URL + item.getDocFolder() + File.separator + files[i];
                        if (!parent.exists()) {
                            parent.mkdirs();
                        }

                        File localFile = new File(parent, fileName);
                        StorageReference storageReference = storage.getReferenceFromUrl(storageFileUrl);
                        storageReference.getFile(localFile)
                          .addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
                            @Override
                            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                                counter++;
                                int valeur = (100 / files.length) * counter;
                                holder.progressBar.setProgress(valeur);
                                holder.txtProgress.setText(valeur + "");
                                Log.d("progressvalues:",  "valeur:"+valeur+"|counter:"+counter+"|lengh:"+files.length);

                                if (counter == files.length) {
                                    holder.txtProgress.setVisibility(View.GONE);
                                    holder.progressBar.setVisibility(View.GONE);
                                    holder.imgDocumentChecker.setVisibility(View.VISIBLE);
                                    holder.imgDocumentChecker.setImageResource(R.drawable.done_ic);
                                }
                            }
                        }).addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
                            @Override
                            public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
                                double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
                            }
                        }).addOnFailureListener(new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                holder.txtProgress.setVisibility(View.GONE);
                                holder.progressBar.setVisibility(View.GONE);
                            }
                        });
                    }
                }
            } else {
                startDocumentContentActivity(item, getRef(position));
            }
        }
    }
});

Please help.


Solution

  • Like mentioned in the comments this request shouldn't be done in the RecyclerView.Adapter, and should definitely not be made in the onBindViewHolder.

    You should abstract this to a separate class, a Presenter or the framework's own ViewModel, that being said:

    Once that request is made and the new data comes in, you should update your adapter's data model and notify of these changes.

    Reporting a click (that would eventually trigger a request) should be done via an interface where you could have your Adapter:

    public interface Listener {
      void onItemClicked(YourModelPojo item);
    }
    

    And your Activity/Fragment/Presenter however abstract you want to do would implement this Listener interface.

    So your adapter's

    holder.imgDocumentChecker.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View view) {
            // Update the UI the request is being made and report to listener
            listener.onItemClicked(data.get(getAdapterPosition()));
    }
    

    Then your adapter should also have an update item on the adapter's data and notify of this change. Since your model changed your bindViewHolder would update the UI correctly.

    Of course, you have to implement these UI changes (data downloading/updated/etc.)


    I've written some articles that you can rely on (with open source code):