package org.commcare.activities.components; import android.animation.Animator; import android.animation.AnimatorInflater; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Rect; import android.os.Build; import android.util.Log; import android.util.Pair; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import org.commcare.activities.CommCareActivity; import org.commcare.dalvik.R; import org.javarosa.form.api.FormController; import org.commcare.utils.MarkupUtil; import org.commcare.views.ClippingFrame; import org.commcare.views.QuestionsView; import org.commcare.views.UserfacingErrorHandling; import org.commcare.views.widgets.QuestionWidget; import org.javarosa.xpath.XPathException; import java.util.ArrayList; public class FormNavigationUI { /** * Update progress bar's max and value, and the various buttons and navigation cues * associated with navigation */ public static void updateNavigationCues(CommCareActivity activity, FormController formController, QuestionsView view) { if (view == null) { return; } updateFloatingLabels(activity, view); FormNavigationController.NavigationDetails details; try { details = FormNavigationController.calculateNavigationStatus(formController, view); } catch (XPathException e) { UserfacingErrorHandling.logErrorAndShowDialog(activity, e, true); return; } ProgressBar progressBar = (ProgressBar)activity.findViewById(R.id.nav_prog_bar); ImageButton nextButton = (ImageButton)activity.findViewById(R.id.nav_btn_next); ImageButton prevButton = (ImageButton)activity.findViewById(R.id.nav_btn_prev); ClippingFrame finishButton = (ClippingFrame)activity. findViewById(R.id.nav_btn_finish); if (!details.relevantBeforeCurrentScreen) { prevButton.setImageResource(R.drawable.icon_close_darkwarm); prevButton.setTag(FormEntryConstants.NAV_STATE_QUIT); } else { prevButton.setImageResource(R.drawable.icon_chevron_left_brand); prevButton.setTag(FormEntryConstants.NAV_STATE_BACK); } //Apparently in Android 2.3 setting the drawable resource for the progress bar //causes it to lose it bounds. It's a bit cheaper to keep them around than it //is to invalidate the view, though. Rect bounds = progressBar.getProgressDrawable().getBounds(); //Save the drawable bound Log.i("Questions", "Total questions: " + details.totalQuestions + " | Completed questions: " + details.completedQuestions); progressBar.setMax(details.totalQuestions); if (details.isFormDone()) { setDoneState(nextButton, activity, finishButton, details, progressBar); } else { setMoreQuestionsState(nextButton, activity, finishButton, details, progressBar); } progressBar.getProgressDrawable().setBounds(bounds); //Set the bounds to the saved value } private static void setDoneState(ImageButton nextButton, Context context, final ClippingFrame finishButton, FormNavigationController.NavigationDetails details, ProgressBar progressBar) { if (nextButton.getTag() == null) { setFinishVisible(finishButton); } else if (!FormEntryConstants.NAV_STATE_DONE.equals(nextButton.getTag())) { nextButton.setTag(FormEntryConstants.NAV_STATE_DONE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { expandAndShowFinishButton(context, finishButton); } else { setFinishVisible(finishButton); } } progressBar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_full)); progressBar.setProgress(details.totalQuestions); Log.i("Questions", "Form complete"); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private static void expandAndShowFinishButton(Context context, final ClippingFrame finishButton) { Animator animator = AnimatorInflater.loadAnimator(context, R.animator.grow_in_visible); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { finishButton.setVisibility(View.VISIBLE); finishButton.setClipHeight(0); finishButton.setClipWidth(0); } @Override public void onAnimationEnd(Animator animation) { finishButton.setClipHeight(1); finishButton.setClipWidth(1); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animator.setTarget(finishButton); animator.start(); } private static void setMoreQuestionsState(ImageButton nextButton, Context context, ClippingFrame finishButton, FormNavigationController.NavigationDetails details, ProgressBar progressBar) { if (!FormEntryConstants.NAV_STATE_NEXT.equals(nextButton.getTag())) { nextButton.setTag(FormEntryConstants.NAV_STATE_NEXT); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { finishButton.setVisibility(View.GONE); } } progressBar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_modern)); progressBar.setProgress(details.completedQuestions); } public static void animateFinishArrow(final CommCareActivity activity) { final View coverView = activity.findViewById(R.id.form_entry_cover); Animation growShrinkAnimation = AnimationUtils.loadAnimation(activity, R.anim.grow_shrink); growShrinkAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { coverView.setVisibility(View.VISIBLE); activity.setMainScreenBlocked(true); } @Override public void onAnimationEnd(Animation animation) { coverView.setVisibility(View.GONE); activity.setMainScreenBlocked(false); } @Override public void onAnimationRepeat(Animation animation) { } }); View finishButton = activity.findViewById(R.id.nav_image_finish); finishButton.startAnimation(growShrinkAnimation); } private static void setFinishVisible(ClippingFrame finishButton) { finishButton.setVisibility(View.VISIBLE); finishButton.setClipWidth(1); finishButton.setClipHeight(1); } enum FloatingLabel { good("floating-good", R.drawable.label_floating_good, R.color.cc_attention_positive_text), caution("floating-caution", R.drawable.label_floating_caution, R.color.cc_light_warm_accent_color), bad("floating-bad", R.drawable.label_floating_bad, R.color.cc_attention_negative_color); final String label; final int resourceId; final int colorId; FloatingLabel(String label, int resourceId, int colorId) { this.label = label; this.resourceId = resourceId; this.colorId = colorId; } public String getAppearance() { return label; } } private static void updateFloatingLabels(CommCareActivity activity, QuestionsView currentView) { //TODO: this should actually be set up to scale per screen size. ArrayList<Pair<CharSequence, FloatingLabel>> smallLabels = new ArrayList<>(); ArrayList<Pair<CharSequence, FloatingLabel>> largeLabels = new ArrayList<>(); FloatingLabel[] labelTypes = FloatingLabel.values(); for (QuestionWidget widget : currentView.getWidgets()) { String hint = widget.getPrompt().getAppearanceHint(); if (hint == null) { continue; } for (FloatingLabel type : labelTypes) { if (type.getAppearance().equals(hint)) { CharSequence widgetText = widget.getPrompt().getQuestionText(); String markdownWidgetText = widget.getPrompt().getMarkdownText(); if (markdownWidgetText != null) { widgetText = MarkupUtil.returnMarkdown(activity, markdownWidgetText); } if (widgetText != null && widgetText.length() < 15) { smallLabels.add(new Pair<>(widgetText, type)); } else { largeLabels.add(new Pair<>(widgetText, type)); } } } } final ViewGroup parent = (ViewGroup)activity.findViewById(R.id.form_entry_label_layout); parent.removeAllViews(); int pixels = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, activity.getResources().getDisplayMetrics()); int minHeight = 7 * pixels; //Ok, now go ahead and add all of the small labels for (int i = 0; i < smallLabels.size(); i = i + 2) { if (i + 1 < smallLabels.size()) { LinearLayout.LayoutParams lpp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); final LinearLayout layout = new LinearLayout(activity); layout.setOrientation(LinearLayout.HORIZONTAL); layout.setLayoutParams(lpp); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 1); TextView left = (TextView)View.inflate(activity, R.layout.component_floating_label, null); left.setLayoutParams(lp); left.setText(smallLabels.get(i).first + "; " + smallLabels.get(i + 1).first); left.setBackgroundResource(smallLabels.get(i).second.resourceId); left.setPadding(pixels, 2 * pixels, pixels, 2 * pixels); left.setTextColor(smallLabels.get(i).second.colorId); left.setMinimumHeight(minHeight); layout.addView(left); parent.addView(layout); } else { largeLabels.add(smallLabels.get(i)); } } for (int i = 0; i < largeLabels.size(); ++i) { final TextView view = (TextView)View.inflate(activity, R.layout.component_floating_label, null); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); view.setLayoutParams(lp); view.setPadding(pixels, 2 * pixels, pixels, 2 * pixels); view.setText(largeLabels.get(i).first); view.setBackgroundResource(largeLabels.get(i).second.resourceId); view.setTextColor(largeLabels.get(i).second.colorId); view.setMinimumHeight(minHeight); parent.addView(view); } } }