package org.commcare.tasks; import android.animation.Animator; import android.animation.ObjectAnimator; import android.os.Build; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.widget.ProgressBar; import org.commcare.activities.SyncCapableCommCareActivity; import org.commcare.dalvik.R; import org.commcare.interfaces.UiLoadedListener; /** * A DataSubmissionListener that updates a progress bar in the given activity * * @author Aliza Stone */ public class FormSubmissionProgressBarListener implements DataSubmissionListener, UiLoadedListener { private static final long MIN_PROGRESS_BAR_DURATION_PER_ITEM = 1000; private static final long MAX_TOTAL_PROGRESS_BAR_DURATION = 5000; private int totalItems; private int maxProgress; private int currentProgress; private long sizeOfCurrentItem; private long startTime; private ProgressBar submissionProgressBar; private SyncCapableCommCareActivity containingActivity; public FormSubmissionProgressBarListener(SyncCapableCommCareActivity activityContainingProgressBar) { this.containingActivity = activityContainingProgressBar; } /** * Called when the associated ProcessAndSendTask's connecting activity changes */ public void attachToNewActivity(SyncCapableCommCareActivity newActivity) { if (newActivity != containingActivity) { this.containingActivity = newActivity; showProgressBarInActivity(this.currentProgress); } } @Override public void beginSubmissionProcess(int totalItems) { this.totalItems = totalItems; // Give each item 100 units of progress to use this.maxProgress = totalItems * 100; this.startTime = System.currentTimeMillis(); showProgressBarInActivity(0); } private void showProgressBarInActivity(final int progressToSet) { this.containingActivity.runOnUiThread(new Runnable() { @Override public void run() { submissionProgressBar = (ProgressBar)containingActivity.findViewById(R.id.submission_progress_bar); if (submissionProgressBar == null) { // Means that the activity has not finished loading its UI yet, so we have to wait containingActivity.setUiLoadedListener(FormSubmissionProgressBarListener.this); } else { submissionProgressBar.setVisibility(View.VISIBLE); submissionProgressBar.setMax(maxProgress); submissionProgressBar.setProgress(progressToSet); } } }); } @Override public void startSubmission(int itemNumber, long sizeOfItem) { sizeOfCurrentItem = sizeOfItem; } @Override public void notifyProgress(final int itemNumber, final long progress) { containingActivity.runOnUiThread(new Runnable() { @Override public void run() { int nextProgress = getProgressToReport(itemNumber, progress); if (nextProgress > FormSubmissionProgressBarListener.this.currentProgress) { submissionProgressBar.setProgress(nextProgress); FormSubmissionProgressBarListener.this.currentProgress = nextProgress; } } }); } private int getProgressToReport(int itemNumber, long progressForCurrentItem) { int progressPercentForPriorItems = 100 * itemNumber; int progressPercentForCurrentItem = (int)Math.floor((progressForCurrentItem * 1.0 / sizeOfCurrentItem) * 100); int actualProgressPercent = progressPercentForPriorItems + progressPercentForCurrentItem; long timeElapsed = System.currentTimeMillis() - startTime; final int maxAllowedProgressByTime = (int)Math.floor((timeElapsed * 1.0 / getIdealDuration()) * maxProgress); return Math.min(actualProgressPercent, maxAllowedProgressByTime); } @Override public void endSubmissionProcess(final boolean success) { containingActivity.runOnUiThread(new Runnable() { @Override public void run() { if (success && submissionProgressBar.getProgress() < maxProgress) { finishAnimatingProgressBar(); } else { submissionProgressBar.setVisibility(View.GONE); } } }); } private void finishAnimatingProgressBar() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ObjectAnimator animation = ObjectAnimator.ofInt(submissionProgressBar, "progress", submissionProgressBar.getProgress(), maxProgress); animation.setDuration(getFinishAnimationDuration()); animation.setInterpolator(new DecelerateInterpolator()); animation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { containingActivity.runOnUiThread(new Runnable() { @Override public void run() { submissionProgressBar.setVisibility(View.GONE); } }); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animation.start(); } else { submissionProgressBar.setProgress(maxProgress); submissionProgressBar.setVisibility(View.GONE); } } private int getFinishAnimationDuration() { int progressRemaining = maxProgress - submissionProgressBar.getProgress(); double proportionRemaining = progressRemaining * 1.0 / maxProgress; return (int)Math.floor(getIdealDuration() * proportionRemaining); } private long getIdealDuration() { return Math.min(MIN_PROGRESS_BAR_DURATION_PER_ITEM * totalItems, MAX_TOTAL_PROGRESS_BAR_DURATION); } @Override public void onUiLoaded() { showProgressBarInActivity(this.currentProgress); // we only want to trigger this once this.containingActivity.removeUiLoadedListener(); } }