Search code examples
androidmultithreadinglistviewandroid-asynctaskbox-api

UI stuck while trying to show progress of each file upload in listview


I am trying to show the progress of multiple files while I upload them to Box.com in a ListView. I am using a Donut Progress bar I found from this link https://github.com/lzyzsd/CircleProgress to show the progress for each item in the list.

However, I can't seem to update the UI while I am uploading. The UI is stuck and does not even respond to clicks by the user. The progress bars for each item starts with 0% and only changes to 100% once every upload has finished. All percentage in between are not shown since the UI is stuck.

I am currently using a custom view for each item in the listview. I did that because I wanted each view to implement a progress listener. Then I added each view to their respective async tasks and update the progress from there.

The following is my code:

Adapter I am using for the ListView

public class SurveyListAdapter extends BaseAdapter {

private LayoutInflater inflater;
private LinkedList<File> files;
private static LinkedList<String> savedIDs;
private ViewHolder holder;
private BoxApiFile mFileApi;
private HomeActivity homeActivity;
private int [] progress;
private int mScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
private ListView listView;
private File file;

public class ViewHolder {
    TextView fileIDTextView;
    TextView dateModifiedTextView;
    DonutProgress fileUploadProgress;
    CustomSurveyView customSurveyView;
    UploadToBoxTask uploadTask;
}

public SurveyListAdapter(HomeActivity homeActivity, LinkedList<File> files, ListView listView) {
    this.homeActivity = homeActivity;
    inflater = LayoutInflater.from(homeActivity);
    this.files = files;
    savedIDs = new LinkedList<>();
    progress = new int [files.size()];
    this.listView = listView;
}

public int getCount() {
    return files.size();
}

public File getItem(int position) {
    return files.get(position);
}

public long getItemId(int position) {
    return position;
}

public View getView(int position, View convertView, ViewGroup parent) {
    file = files.get(position);
    holder = null;
    if(convertView == null) {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.survey_list_view, null);
        holder.customSurveyView = (CustomSurveyView) convertView.findViewById(R.id.customSurveyView);
        holder.fileUploadProgress = holder.customSurveyView.getDonutProgress();
        holder.fileIDTextView = holder.customSurveyView.getFileIDTextView();
        holder.dateModifiedTextView = holder.customSurveyView.getDateModifiedTextView();
        holder.uploadTask = new UploadToBoxTask(this, holder.customSurveyView, file);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    String fileName = file.getName();
    String fileShouldStartWith = HomeActivity.FILE_SHOULD_START_WITH;
    String fileShouldEndWith = HomeActivity.FILE_SHOULD_END_WITH;

    String ID = fileName.substring(fileShouldStartWith.length(), fileName.length() - fileShouldEndWith.length()).trim();
    savedIDs.add(ID);

    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy   hh:mm a");
    String lastDateModified = sdf.format(file.lastModified());

    holder.fileIDTextView.setText("Subject ID " + ID);
    holder.dateModifiedTextView.setText(lastDateModified);

    return convertView;
}

public View getViewByPosition(int pos) {
    final int firstListItemPosition = listView.getFirstVisiblePosition();
    final int lastListItemPosition = firstListItemPosition + listView.getChildCount() - 1;

    if (pos < firstListItemPosition || pos > lastListItemPosition ) {
        return listView.getAdapter().getView(pos, null, listView);
    } else {
        final int childIndex = pos - firstListItemPosition;
        return listView.getChildAt(childIndex);
    }
}

public static LinkedList<String> getSavedIDs() {
    return savedIDs;
}

public void syncWithBox(BoxApiFile mFileApi){
    this.mFileApi = mFileApi;

    ExecutorService executor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < files.size(); i++) {
        ViewHolder holder = (ViewHolder) getViewByPosition(i).getTag();
        holder.fileUploadProgress.setVisibility(View.VISIBLE);
        holder.uploadTask.executeOnExecutor(executor);}

    executor.shutdown();

    while (!executor.isTerminated()) { }

    System.out.println("Finished uploading");
}

private class UploadToBoxTask extends AsyncTask<Void, Integer, Void>{

    SurveyListAdapter surveyListAdapter;
    BoxUploadProgressListener progressListener;
    File uploadFile;

    public UploadToBoxTask(SurveyListAdapter surveyListAdapter, BoxUploadProgressListener progressListener, File uploadFile){
        this.surveyListAdapter = surveyListAdapter;
        this.progressListener = progressListener;
        this.uploadFile = uploadFile;
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            final BoxRequestsFile.UploadFile request = mFileApi.getUploadRequest(uploadFile, BoxConstants.ROOT_FOLDER_ID);
            request.setProgressListener(new ProgressListener() {
                @Override
                public void onProgressChanged(long numBytes, long totalBytes) {
                    publishProgress((int) (100 * (numBytes/totalBytes)));
                }
            });
            final BoxFile uploadFileInfo = request.send();
            showToast("Uploaded " + uploadFileInfo.getName());
            //loadRootFolder();
        } catch (BoxException e) {
            BoxError error = e.getAsBoxError();
            if (error != null && error.getStatus() == HttpURLConnection.HTTP_CONFLICT) {
                ArrayList<BoxEntity> conflicts = error.getContextInfo().getConflicts();
                if (conflicts != null && conflicts.size() == 1 && conflicts.get(0) instanceof BoxFile) {
                    //uploadNewVersion((BoxFile) conflicts.get(0), position, adapter);
                    publishProgress(100);
                    return null;
                }
            }
            showToast("Upload failed");
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... integers){
        progressListener.onProgressUpdate(integers[0]);
        showToast(String.valueOf(integers[0]));
    }

    @Override
    protected void onPostExecute(Void param){

    }
}
}

The custom view I created:

public class CustomSurveyView extends RelativeLayout  implements BoxUploadProgressListener{
View rootView;
LinearLayout linearLayout;
TextView fileIDTextView;
TextView dateModifiedTextView;
DonutProgress donutProgress;

public CustomSurveyView(Context context) {
    super(context);
    init(context);
}

public CustomSurveyView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

private void init(Context context) {
    rootView = inflate(context, R.layout.custom_survey_view, this);
    linearLayout = rootView.findViewById(R.id.fileInfoLayout);
    fileIDTextView = rootView.findViewById(R.id.fileIDTextView);
    dateModifiedTextView = rootView.findViewById(R.id.dateModifiedTextView);
    donutProgress = rootView.findViewById(R.id.fileUploadProgressBar);
}

@Override
public void onProgressUpdate(int progress) {
    donutProgress.setVisibility(View.VISIBLE);
    ObjectAnimator anim = ObjectAnimator.ofInt(donutProgress, "progress", donutProgress.getProgress(), progress);
    donutProgress.setProgress(progress);
    anim.setInterpolator(new DecelerateInterpolator());
    anim.setDuration(500);
    anim.start();
    invalidate();
}

public LinearLayout getLinearLayout() {
    return linearLayout;
}

public TextView getFileIDTextView() {
    return fileIDTextView;
}

public TextView getDateModifiedTextView() {
    return dateModifiedTextView;
}

public DonutProgress getDonutProgress() {
    return donutProgress;
}
}

Progress listener interface I created

public interface BoxUploadProgressListener {
    void onProgressUpdate(int progress);
}

This is what my layout looks like

app layout

Please help correctly update the UI with the progress of each upload. I've been at this for two days now.


Solution

  • I found the problem. Its the while loop I put after the executor. It kept on running till all tasks were finished.