Search in sources :

Example 6 with FormEntryPrompt

use of org.javarosa.form.api.FormEntryPrompt in project collect by opendatakit.

the class FormEntryActivity method createView.

/**
 * Creates and returns a new view based on the event type passed in. The view returned is
 * of type {@link View} if the event passed in represents the end of the form or of type
 * {@link ODKView} otherwise.
 *
 * @param advancingPage -- true if this results from advancing through the form
 * @return newly created View
 */
private View createView(int event, boolean advancingPage) {
    releaseOdkView();
    FormController formController = getFormController();
    String formTitle = formController.getFormTitle();
    setTitle(formTitle);
    if (event != FormEntryController.EVENT_QUESTION) {
        formController.getAuditEventLogger().logEvent(AuditEvent.getAuditEventTypeFromFecType(event), formController.getFormIndex(), true, null, System.currentTimeMillis(), null);
    }
    switch(event) {
        case FormEntryController.EVENT_BEGINNING_OF_FORM:
            return createViewForFormBeginning(formController);
        case FormEntryController.EVENT_END_OF_FORM:
            return createViewForFormEnd(formController);
        case FormEntryController.EVENT_QUESTION:
        case FormEntryController.EVENT_GROUP:
        case FormEntryController.EVENT_REPEAT:
            // should only be a group here if the event_group is a field-list
            try {
                AuditUtils.logCurrentScreen(formController, formController.getAuditEventLogger(), System.currentTimeMillis());
                FormEntryCaption[] groups = formController.getGroupsForCurrentIndex();
                FormEntryPrompt[] prompts = formController.getQuestionPrompts();
                odkView = createODKView(advancingPage, prompts, groups);
                odkView.setWidgetValueChangedListener(this);
                Timber.i("Created view for group %s %s", groups.length > 0 ? groups[groups.length - 1].getLongText() : "[top]", prompts.length > 0 ? prompts[0].getQuestionText() : "[no question]");
            } catch (RuntimeException | RepeatsInFieldListException e) {
                if (e instanceof RuntimeException) {
                    Timber.e(e);
                }
                // this is badness to avoid a crash.
                try {
                    event = formController.stepToNextScreenEvent();
                    createErrorDialog(e.getMessage(), false);
                } catch (JavaRosaException e1) {
                    Timber.d(e1);
                    createErrorDialog(e.getMessage() + "\n\n" + e1.getCause().getMessage(), false);
                }
                return createView(event, advancingPage);
            }
            if (showNavigationButtons) {
                updateNavigationButtonVisibility();
            }
            return odkView;
        case EVENT_PROMPT_NEW_REPEAT:
            createRepeatDialog();
            return new EmptyView(this);
        default:
            Timber.e("Attempted to create a view that does not exist.");
            // this is badness to avoid a crash.
            try {
                event = formController.stepToNextScreenEvent();
                createErrorDialog(getString(R.string.survey_internal_error), true);
            } catch (JavaRosaException e) {
                Timber.d(e);
                createErrorDialog(e.getCause().getMessage(), true);
            }
            return createView(event, advancingPage);
    }
}
Also used : FormController(org.odk.collect.android.javarosawrapper.FormController) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) RepeatsInFieldListException(org.odk.collect.android.javarosawrapper.RepeatsInFieldListException) FormEntryCaption(org.javarosa.form.api.FormEntryCaption) JavaRosaException(org.odk.collect.android.exception.JavaRosaException)

Example 7 with FormEntryPrompt

use of org.javarosa.form.api.FormEntryPrompt in project collect by opendatakit.

the class FormHierarchyActivity method refreshView.

/**
 * @see #refreshView()
 */
private void refreshView(boolean isGoingUp) {
    try {
        FormController formController = Collect.getInstance().getFormController();
        // Save the current index so we can return to the problematic question
        // in the event of an error.
        currentIndex = formController.getFormIndex();
        elementsToDisplay = new ArrayList<>();
        jumpToHierarchyStartIndex();
        updateOptionsMenu();
        int event = formController.getEvent();
        if (event == FormEntryController.EVENT_BEGINNING_OF_FORM && !shouldShowRepeatGroupPicker()) {
            // The beginning of form has no valid prompt to display.
            groupPathTextView.setVisibility(View.GONE);
        } else {
            groupPathTextView.setVisibility(View.VISIBLE);
            groupPathTextView.setText(getCurrentPath());
        }
        // Refresh the current event in case we did step forward.
        event = formController.getEvent();
        // Ref to the parent group that's currently being displayed.
        // 
        // Because of the guard conditions below, we will skip
        // everything until we exit this group.
        TreeReference visibleGroupRef = null;
        while (event != FormEntryController.EVENT_END_OF_FORM) {
            // get the ref to this element
            TreeReference currentRef = formController.getFormIndex().getReference();
            // retrieve the current group
            TreeReference curGroup = (visibleGroupRef == null) ? contextGroupRef : visibleGroupRef;
            if (curGroup != null && !curGroup.isParentOf(currentRef, false)) {
                // We have left the current group
                if (visibleGroupRef == null) {
                    // We are done.
                    break;
                } else {
                    // exit the inner group
                    visibleGroupRef = null;
                }
            }
            if (visibleGroupRef != null) {
                // We're in a group within the one we want to list
                // skip this question/group/repeat and move to the next index.
                event = formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
                continue;
            }
            switch(event) {
                case FormEntryController.EVENT_QUESTION:
                    {
                        // Nothing but repeat group instances should show up in the picker.
                        if (shouldShowRepeatGroupPicker()) {
                            break;
                        }
                        FormEntryPrompt fp = formController.getQuestionPrompt();
                        String label = fp.getShortText();
                        String answerDisplay = FormEntryPromptUtils.getAnswerText(fp, this, formController);
                        elementsToDisplay.add(new HierarchyElement(FormEntryPromptUtils.markQuestionIfIsRequired(label, fp.isRequired()), answerDisplay, null, HierarchyElement.Type.QUESTION, fp.getIndex()));
                        break;
                    }
                case FormEntryController.EVENT_GROUP:
                    {
                        if (!formController.isGroupRelevant()) {
                            break;
                        }
                        // Nothing but repeat group instances should show up in the picker.
                        if (shouldShowRepeatGroupPicker()) {
                            break;
                        }
                        FormIndex index = formController.getFormIndex();
                        // Only display groups with a specific appearance attribute.
                        if (!formController.isDisplayableGroup(index)) {
                            break;
                        }
                        // Don't render other groups' children.
                        if (contextGroupRef != null && !contextGroupRef.isParentOf(currentRef, false)) {
                            break;
                        }
                        visibleGroupRef = currentRef;
                        FormEntryCaption caption = formController.getCaptionPrompt();
                        HierarchyElement groupElement = new HierarchyElement(caption.getShortText(), getString(R.string.group_label), ContextCompat.getDrawable(this, R.drawable.ic_folder_open), HierarchyElement.Type.VISIBLE_GROUP, caption.getIndex());
                        elementsToDisplay.add(groupElement);
                        // Skip to the next item outside the group.
                        event = formController.stepOverGroup();
                        continue;
                    }
                case FormEntryController.EVENT_PROMPT_NEW_REPEAT:
                    {
                        // ignore it.
                        break;
                    }
                case FormEntryController.EVENT_REPEAT:
                    {
                        boolean forPicker = shouldShowRepeatGroupPicker();
                        // Only break to exclude non-relevant repeat from picker
                        if (!formController.isGroupRelevant() && forPicker) {
                            break;
                        }
                        visibleGroupRef = currentRef;
                        // Don't render other groups' children.
                        if (contextGroupRef != null && !contextGroupRef.isParentOf(currentRef, false)) {
                            break;
                        }
                        FormEntryCaption fc = formController.getCaptionPrompt();
                        if (forPicker) {
                            // Don't render other groups' instances.
                            String repeatGroupPickerRef = repeatGroupPickerIndex.getReference().toString(false);
                            if (!currentRef.toString(false).equals(repeatGroupPickerRef)) {
                                break;
                            }
                            int itemNumber = fc.getMultiplicity() + 1;
                            // e.g. `friends > 1`
                            String repeatLabel = fc.getShortText() + " > " + itemNumber;
                            // If the child of the group has a more descriptive label, use that instead.
                            if (fc.getFormElement().getChildren().size() == 1 && fc.getFormElement().getChild(0) instanceof GroupDef) {
                                formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
                                String itemLabel = formController.getCaptionPrompt().getShortText();
                                if (itemLabel != null) {
                                    // e.g. `1. Alice`
                                    repeatLabel = itemNumber + ".\u200E " + itemLabel;
                                }
                            }
                            HierarchyElement instance = new HierarchyElement(repeatLabel, null, null, HierarchyElement.Type.REPEAT_INSTANCE, fc.getIndex());
                            elementsToDisplay.add(instance);
                        } else if (fc.getMultiplicity() == 0) {
                            // Display the repeat header for the group.
                            HierarchyElement group = new HierarchyElement(fc.getShortText(), getString(R.string.repeatable_group_label), ContextCompat.getDrawable(this, R.drawable.ic_repeat), HierarchyElement.Type.REPEATABLE_GROUP, fc.getIndex());
                            elementsToDisplay.add(group);
                        }
                        break;
                    }
            }
            event = formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
        }
        recyclerView.setAdapter(new HierarchyListAdapter(elementsToDisplay, this::onElementClick));
        formController.jumpToIndex(currentIndex);
        // that use presentation groups to display labels).
        if (isDisplayingSingleGroup() && !screenIndex.isBeginningOfFormIndex()) {
            if (isGoingUp) {
                // Back out once more.
                goUpLevel();
            } else {
                // Enter automatically.
                formController.jumpToIndex(elementsToDisplay.get(0).getFormIndex());
                refreshView();
            }
        }
    } catch (Exception e) {
        Timber.e(e);
        createErrorDialog(e.getMessage());
    }
}
Also used : FormController(org.odk.collect.android.javarosawrapper.FormController) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) HierarchyElement(org.odk.collect.android.logic.HierarchyElement) TreeReference(org.javarosa.core.model.instance.TreeReference) HierarchyListAdapter(org.odk.collect.android.adapters.HierarchyListAdapter) FormEntryCaption(org.javarosa.form.api.FormEntryCaption) FormIndex(org.javarosa.core.model.FormIndex) GroupDef(org.javarosa.core.model.GroupDef) JavaRosaException(org.odk.collect.android.exception.JavaRosaException)

Example 8 with FormEntryPrompt

use of org.javarosa.form.api.FormEntryPrompt in project collect by opendatakit.

the class ODKView method setDataForFields.

/**
 * Saves answers for the widgets in this view. Called when the widgets are in an intent group.
 */
public void setDataForFields(Bundle bundle) throws JavaRosaException {
    FormController formController = Collect.getInstance().getFormController();
    if (formController == null) {
        return;
    }
    if (bundle != null) {
        Set<String> keys = bundle.keySet();
        for (String key : keys) {
            Object answer = bundle.get(key);
            if (answer == null) {
                continue;
            }
            for (QuestionWidget questionWidget : widgets) {
                FormEntryPrompt prompt = questionWidget.getFormEntryPrompt();
                TreeReference treeReference = (TreeReference) prompt.getFormElement().getBind().getReference();
                if (treeReference.getNameLast().equals(key)) {
                    switch(prompt.getDataType()) {
                        case Constants.DATATYPE_TEXT:
                            formController.saveAnswer(prompt.getIndex(), ExternalAppsUtils.asStringData(answer));
                            ((StringWidget) questionWidget).setDisplayValueFromModel();
                            questionWidget.showAnswerContainer();
                            break;
                        case Constants.DATATYPE_INTEGER:
                            formController.saveAnswer(prompt.getIndex(), ExternalAppsUtils.asIntegerData(answer));
                            ((StringWidget) questionWidget).setDisplayValueFromModel();
                            questionWidget.showAnswerContainer();
                            break;
                        case Constants.DATATYPE_DECIMAL:
                            formController.saveAnswer(prompt.getIndex(), ExternalAppsUtils.asDecimalData(answer));
                            ((StringWidget) questionWidget).setDisplayValueFromModel();
                            questionWidget.showAnswerContainer();
                            break;
                        case Constants.DATATYPE_BINARY:
                            try {
                                Uri uri;
                                if (answer instanceof Uri) {
                                    uri = (Uri) answer;
                                } else if (answer instanceof String) {
                                    uri = Uri.parse(bundle.getString(key));
                                } else {
                                    throw new RuntimeException("The value for " + key + " must be a URI but it is " + answer);
                                }
                                permissionsProvider.requestReadUriPermission((Activity) getContext(), uri, getContext().getContentResolver(), new PermissionListener() {

                                    @Override
                                    public void granted() {
                                        File destFile = FileUtils.createDestinationMediaFile(formController.getInstanceFile().getParent(), ContentUriHelper.getFileExtensionFromUri(uri));
                                        // TODO might be better to use QuestionMediaManager in the future
                                        FileUtils.saveAnswerFileFromUri(uri, destFile, getContext());
                                        ((WidgetDataReceiver) questionWidget).setData(destFile);
                                        questionWidget.showAnswerContainer();
                                    }

                                    @Override
                                    public void denied() {
                                    }
                                });
                            } catch (Exception | Error e) {
                                Timber.w(e);
                            }
                            break;
                        default:
                            throw new RuntimeException(getContext().getString(R.string.ext_assign_value_error, treeReference.toString(false)));
                    }
                    break;
                }
            }
        }
    }
}
Also used : FormController(org.odk.collect.android.javarosawrapper.FormController) PermissionListener(org.odk.collect.permissions.PermissionListener) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) Uri(android.net.Uri) ExternalParamsException(org.odk.collect.android.exception.ExternalParamsException) ActivityNotFoundException(android.content.ActivityNotFoundException) PlaybackFailedException(org.odk.collect.audioclips.PlaybackFailedException) JavaRosaException(org.odk.collect.android.exception.JavaRosaException) TreeReference(org.javarosa.core.model.instance.TreeReference) StringWidget(org.odk.collect.android.widgets.StringWidget) QuestionWidget(org.odk.collect.android.widgets.QuestionWidget) File(java.io.File)

Example 9 with FormEntryPrompt

use of org.javarosa.form.api.FormEntryPrompt in project collect by opendatakit.

the class ODKView method addIntentLaunchButton.

/**
 * Adds a button to launch an intent if the group displayed by this view is an intent group.
 * An intent group launches an intent and receives multiple values from the launched app.
 */
private void addIntentLaunchButton(Context context, FormEntryPrompt[] questionPrompts, FormEntryCaption c, String intentString) {
    final String buttonText;
    final String errorString;
    String v = c.getSpecialFormQuestionText("buttonText");
    buttonText = (v != null) ? v : context.getString(R.string.launch_app);
    v = c.getSpecialFormQuestionText("noAppErrorString");
    errorString = (v != null) ? v : context.getString(R.string.no_app);
    // set button formatting
    MaterialButton launchIntentButton = findViewById(R.id.launchIntentButton);
    launchIntentButton.setText(buttonText);
    launchIntentButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, QuestionFontSizeUtils.getQuestionFontSize() + 2);
    launchIntentButton.setVisibility(VISIBLE);
    launchIntentButton.setOnClickListener(view -> {
        String intentName = ExternalAppsUtils.extractIntentName(intentString);
        Map<String, String> parameters = ExternalAppsUtils.extractParameters(intentString);
        Intent i = new Intent(intentName);
        if (i.resolveActivity(Collect.getInstance().getPackageManager()) == null) {
            Intent launchIntent = Collect.getInstance().getPackageManager().getLaunchIntentForPackage(intentName);
            if (launchIntent != null) {
                // Make sure FLAG_ACTIVITY_NEW_TASK is not set because it doesn't work with startActivityForResult
                launchIntent.setFlags(0);
                i = launchIntent;
            }
        }
        try {
            ExternalAppsUtils.populateParameters(i, parameters, c.getIndex().getReference());
            for (FormEntryPrompt p : questionPrompts) {
                IFormElement formElement = p.getFormElement();
                if (formElement instanceof QuestionDef) {
                    TreeReference reference = (TreeReference) formElement.getBind().getReference();
                    IAnswerData answerValue = p.getAnswerValue();
                    Object value = answerValue == null ? null : answerValue.getValue();
                    switch(p.getDataType()) {
                        case Constants.DATATYPE_TEXT:
                        case Constants.DATATYPE_INTEGER:
                        case Constants.DATATYPE_DECIMAL:
                        case Constants.DATATYPE_BINARY:
                            i.putExtra(reference.getNameLast(), (Serializable) value);
                            break;
                    }
                }
            }
            ((Activity) getContext()).startActivityForResult(i, RequestCodes.EX_GROUP_CAPTURE);
        } catch (ExternalParamsException e) {
            Timber.e(e, "ExternalParamsException");
            ToastUtils.showShortToast(getContext(), e.getMessage());
        } catch (ActivityNotFoundException e) {
            Timber.d(e, "ActivityNotFoundExcept");
            ToastUtils.showShortToast(getContext(), errorString);
        }
    });
}
Also used : IAnswerData(org.javarosa.core.model.data.IAnswerData) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) ComponentActivity(androidx.activity.ComponentActivity) Activity(android.app.Activity) Intent(android.content.Intent) MaterialButton(com.google.android.material.button.MaterialButton) IFormElement(org.javarosa.core.model.IFormElement) ExternalParamsException(org.odk.collect.android.exception.ExternalParamsException) ActivityNotFoundException(android.content.ActivityNotFoundException) TreeReference(org.javarosa.core.model.instance.TreeReference) QuestionDef(org.javarosa.core.model.QuestionDef)

Example 10 with FormEntryPrompt

use of org.javarosa.form.api.FormEntryPrompt in project collect by opendatakit.

the class ODKView method getAnswers.

/**
 * @return a HashMap of answers entered by the user for this set of widgets
 */
public HashMap<FormIndex, IAnswerData> getAnswers() {
    HashMap<FormIndex, IAnswerData> answers = new LinkedHashMap<>();
    for (QuestionWidget q : widgets) {
        /*
             * The FormEntryPrompt has the FormIndex, which is where the answer gets stored. The
             * QuestionWidget has the answer the user has entered.
             */
        FormEntryPrompt p = q.getFormEntryPrompt();
        answers.put(p.getIndex(), q.getAnswer());
    }
    return answers;
}
Also used : IAnswerData(org.javarosa.core.model.data.IAnswerData) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) FormIndex(org.javarosa.core.model.FormIndex) QuestionWidget(org.odk.collect.android.widgets.QuestionWidget) LinkedHashMap(java.util.LinkedHashMap)

Aggregations

FormEntryPrompt (org.javarosa.form.api.FormEntryPrompt)114 Test (org.junit.Test)92 MockFormEntryPromptBuilder (org.odk.collect.android.support.MockFormEntryPromptBuilder)45 Clip (org.odk.collect.audioclips.Clip)13 FormIndex (org.javarosa.core.model.FormIndex)10 File (java.io.File)9 SelectChoice (org.javarosa.core.model.SelectChoice)7 StringData (org.javarosa.core.model.data.StringData)7 IFormElement (org.javarosa.core.model.IFormElement)5 QuestionDef (org.javarosa.core.model.QuestionDef)5 FormController (org.odk.collect.android.javarosawrapper.FormController)5 GroupDef (org.javarosa.core.model.GroupDef)4 QuestionDetails (org.odk.collect.android.formentry.questions.QuestionDetails)4 DatePickerDetails (org.odk.collect.android.logic.DatePickerDetails)4 ArrayList (java.util.ArrayList)3 DateData (org.javarosa.core.model.data.DateData)3 IAnswerData (org.javarosa.core.model.data.IAnswerData)3 TreeReference (org.javarosa.core.model.instance.TreeReference)3 LocalDateTime (org.joda.time.LocalDateTime)3 AudioControllerView (org.odk.collect.android.audio.AudioControllerView)3