package org.commcare.activities.components;
import org.javarosa.form.api.FormController;
import org.commcare.views.QuestionsView;
import org.javarosa.core.model.Constants;
import org.javarosa.core.model.FormIndex;
import org.javarosa.form.api.FormEntryController;
import org.javarosa.form.api.FormEntryPrompt;
public class FormNavigationController {
public static class NavigationDetails {
public int totalQuestions = 0;
public int completedQuestions = 0;
public boolean relevantBeforeCurrentScreen = false;
public boolean isFirstScreen = false;
public int answeredOnScreen = 0;
public int requiredOnScreen = 0;
public int relevantAfterCurrentScreen = 0;
public FormIndex currentScreenExit = null;
public boolean isFormDone() {
return relevantAfterCurrentScreen == 0 &&
(requiredOnScreen == answeredOnScreen ||
requiredOnScreen < 1);
}
}
public static NavigationDetails calculateNavigationStatus(FormController formEntryController,
QuestionsView view) {
NavigationDetails details = new NavigationDetails();
FormIndex userFormIndex = formEntryController.getFormIndex();
FormIndex currentFormIndex = FormIndex.createBeginningOfFormIndex();
formEntryController.expandRepeats(currentFormIndex);
int event = formEntryController.getEvent(currentFormIndex);
// keep track of whether there is a question that exists before the
// current screen
boolean onCurrentScreen = false;
while (event != FormEntryController.EVENT_END_OF_FORM) {
int comparison = currentFormIndex.compareTo(userFormIndex);
if (comparison == 0) {
onCurrentScreen = true;
details.currentScreenExit = formEntryController.getNextFormIndex(currentFormIndex, true);
}
if (onCurrentScreen && currentFormIndex.equals(details.currentScreenExit)) {
onCurrentScreen = false;
}
// Figure out if there are any events before this screen (either
// new repeat or relevant questions are valid)
if (event == FormEntryController.EVENT_QUESTION
|| event == FormEntryController.EVENT_PROMPT_NEW_REPEAT) {
// Figure out whether we're on the last screen
if (!details.relevantBeforeCurrentScreen && !details.isFirstScreen) {
// We got to the current screen without finding a
// relevant question,
// I guess we're on the first one.
if (onCurrentScreen
&& !details.relevantBeforeCurrentScreen) {
details.isFirstScreen = true;
} else {
// We're using the built in steps (and they take
// relevancy into account)
// so if there are prompts they have to be relevant
details.relevantBeforeCurrentScreen = true;
}
}
}
if (event == FormEntryController.EVENT_QUESTION) {
FormEntryPrompt[] prompts = formEntryController.getQuestionPrompts(currentFormIndex);
if (!onCurrentScreen && details.currentScreenExit != null) {
details.relevantAfterCurrentScreen += prompts.length;
}
details.totalQuestions += prompts.length;
// Current questions are complete only if they're answered.
// Past questions are always complete.
// Future questions are never complete.
if (onCurrentScreen) {
for (FormEntryPrompt prompt : prompts) {
prompt = view.getOnScreenPrompt(prompt);
boolean isAnswered = prompt.getAnswerValue() != null
|| prompt.getDataType() == Constants.DATATYPE_NULL;
if (prompt.isRequired()) {
details.requiredOnScreen++;
if (isAnswered) {
details.answeredOnScreen++;
}
}
if (isAnswered) {
details.completedQuestions++;
}
}
} else if (comparison < 0) {
// For previous questions, consider all "complete"
details.completedQuestions += prompts.length;
// TODO: This doesn't properly capture state to
// determine whether we will end up out of the form if
// we hit back!
// Need to test _until_ we get a question that is
// relevant, then we can skip the relevancy tests
}
} else if (event == FormEntryController.EVENT_PROMPT_NEW_REPEAT) {
// If we've already passed the current screen, this repeat
// junction is coming up in the future and we will need to
// know
// about it
if (!onCurrentScreen && details.currentScreenExit != null) {
details.totalQuestions++;
details.relevantAfterCurrentScreen++;
} else {
// Otherwise we already passed it and it no longer
// affects the count
}
}
currentFormIndex = formEntryController.getNextFormIndex(currentFormIndex, FormEntryController.STEP_INTO_GROUP, false);
event = formEntryController.getEvent(currentFormIndex);
}
// Set form back to correct state
formEntryController.jumpToIndex(userFormIndex);
return details;
}
}