Search in sources :

Example 6 with FormController

use of org.odk.collect.android.logic.FormController in project collect by opendatakit.

the class SavePointTask method doInBackground.

@Override
protected String doInBackground(Void... params) {
    synchronized (lock) {
        if (priority < lastPriorityUsed) {
            Timber.w("Savepoint thread (p=%d) was cancelled (a) because another one is waiting (p=%d)", priority, lastPriorityUsed);
            return null;
        }
        long start = System.currentTimeMillis();
        try {
            FormController formController = Collect.getInstance().getFormController();
            File temp = SaveToDiskTask.getSavepointFile(formController.getInstanceFile().getName());
            ByteArrayPayload payload = formController.getFilledInFormXml();
            if (priority < lastPriorityUsed) {
                Timber.w("Savepoint thread (p=%d) was cancelled (b) because another one is waiting (p=%d)", priority, lastPriorityUsed);
                return null;
            }
            // write out xml
            SaveToDiskTask.exportXmlFile(payload, temp.getAbsolutePath());
            long end = System.currentTimeMillis();
            Timber.i("Savepoint ms: %s to %s", Long.toString(end - start), temp.toString());
            return null;
        } catch (Exception e) {
            String msg = e.getMessage();
            Timber.e(e);
            return msg;
        }
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) ByteArrayPayload(org.javarosa.core.services.transport.payload.ByteArrayPayload) File(java.io.File)

Example 7 with FormController

use of org.odk.collect.android.logic.FormController in project collect by opendatakit.

the class SaveToDiskTask method updateInstanceDatabase.

private void updateInstanceDatabase(boolean incomplete, boolean canEditAfterCompleted) {
    FormController formController = Collect.getInstance().getFormController();
    // Update the instance database...
    ContentValues values = new ContentValues();
    if (instanceName != null) {
        values.put(InstanceColumns.DISPLAY_NAME, instanceName);
    }
    if (incomplete || !markCompleted) {
        values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_INCOMPLETE);
    } else {
        values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_COMPLETE);
    }
    // update this whether or not the status is complete...
    values.put(InstanceColumns.CAN_EDIT_WHEN_COMPLETE, Boolean.toString(canEditAfterCompleted));
    // If FormEntryActivity was started with an Instance, just update that instance
    if (Collect.getInstance().getContentResolver().getType(uri).equals(InstanceColumns.CONTENT_ITEM_TYPE)) {
        int updated = Collect.getInstance().getContentResolver().update(uri, values, null, null);
        if (updated > 1) {
            Timber.w("Updated more than one entry, that's not good: %s", uri.toString());
        } else if (updated == 1) {
            Timber.i("Instance successfully updated");
        } else {
            Timber.e("Instance doesn't exist but we have its Uri!! %s", uri.toString());
        }
    } else if (Collect.getInstance().getContentResolver().getType(uri).equals(FormsColumns.CONTENT_ITEM_TYPE)) {
        // If FormEntryActivity was started with a form, then it's likely the first time we're
        // saving.
        // However, it could be a not-first time saving if the user has been using the manual
        // 'save data' option from the menu. So try to update first, then make a new one if that
        // fails.
        String instancePath = formController.getInstanceFile().getAbsolutePath();
        String where = InstanceColumns.INSTANCE_FILE_PATH + "=?";
        String[] whereArgs = { instancePath };
        int updated = new InstancesDao().updateInstance(values, where, whereArgs);
        if (updated > 1) {
            Timber.w("Updated more than one entry, that's not good: %s", instancePath);
        } else if (updated == 1) {
            Timber.i("Instance found and successfully updated: %s", instancePath);
        // already existed and updated just fine
        } else {
            Timber.i("No instance found, creating");
            // Entry didn't exist, so create it.
            Cursor c = null;
            try {
                // retrieve the form definition...
                c = Collect.getInstance().getContentResolver().query(uri, null, null, null, null);
                c.moveToFirst();
                String formname = c.getString(c.getColumnIndex(FormsColumns.DISPLAY_NAME));
                String submissionUri = null;
                if (!c.isNull(c.getColumnIndex(FormsColumns.SUBMISSION_URI))) {
                    submissionUri = c.getString(c.getColumnIndex(FormsColumns.SUBMISSION_URI));
                }
                // add missing fields into values
                values.put(InstanceColumns.INSTANCE_FILE_PATH, instancePath);
                values.put(InstanceColumns.SUBMISSION_URI, submissionUri);
                if (instanceName != null) {
                    values.put(InstanceColumns.DISPLAY_NAME, instanceName);
                } else {
                    values.put(InstanceColumns.DISPLAY_NAME, formname);
                }
                String jrformid = c.getString(c.getColumnIndex(FormsColumns.JR_FORM_ID));
                String jrversion = c.getString(c.getColumnIndex(FormsColumns.JR_VERSION));
                values.put(InstanceColumns.JR_FORM_ID, jrformid);
                values.put(InstanceColumns.JR_VERSION, jrversion);
            } finally {
                if (c != null) {
                    c.close();
                }
            }
            uri = new InstancesDao().saveInstance(values);
        }
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) ContentValues(android.content.ContentValues) InstancesDao(org.odk.collect.android.dao.InstancesDao) Cursor(android.database.Cursor)

Example 8 with FormController

use of org.odk.collect.android.logic.FormController in project collect by opendatakit.

the class FormLoaderTask method doInBackground.

/**
 * Initialize {@link FormEntryController} with {@link FormDef} from binary or
 * from XML. If given an instance, it will be used to fill the {@link FormDef}.
 */
@Override
protected FECWrapper doInBackground(String... path) {
    errorMsg = null;
    final String formPath = path[0];
    final File formXml = new File(formPath);
    final FormDef formDef = createFormDefFromCacheOrXml(formXml);
    if (errorMsg != null || formDef == null) {
        return null;
    }
    // set paths to /sdcard/odk/forms/formfilename-media/
    final String formFileName = formXml.getName().substring(0, formXml.getName().lastIndexOf("."));
    final File formMediaDir = new File(formXml.getParent(), formFileName + "-media");
    externalDataManager = new ExternalDataManagerImpl(formMediaDir);
    // add external data function handlers
    ExternalDataHandler externalDataHandlerPull = new ExternalDataHandlerPull(externalDataManager);
    formDef.getEvaluationContext().addFunctionHandler(externalDataHandlerPull);
    try {
        loadExternalData(formMediaDir);
    } catch (Exception e) {
        Timber.e(e, "Exception thrown while loading external data");
        errorMsg = e.getMessage();
        return null;
    }
    if (isCancelled()) {
        // that means that the user has cancelled, so no need to go further
        return null;
    }
    // create FormEntryController from formdef
    final FormEntryModel fem = new FormEntryModel(formDef);
    final FormEntryController fec = new FormEntryController(fem);
    boolean usedSavepoint = false;
    try {
        Timber.i("Initializing form.");
        final long start = System.currentTimeMillis();
        usedSavepoint = initializeForm(formDef, fec);
        Timber.i("Form initialized in %.3f seconds.", (System.currentTimeMillis() - start) / 1000F);
    } catch (RuntimeException e) {
        Timber.e(e);
        if (e.getCause() instanceof XPathTypeMismatchException) {
            // this is a case of
            // https://bitbucket.org/m
            // .sundt/javarosa/commits/e5d344783e7968877402bcee11828fa55fac69de
            // the data are imported, the survey will be unusable
            // but we should give the option to the user to edit the form
            // otherwise the survey will be TOTALLY inaccessible.
            Timber.w("We have a syntactically correct instance, but the data threw an " + "exception inside JR. We should allow editing.");
        } else {
            errorMsg = e.getMessage();
            return null;
        }
    }
    // Remove previous forms
    ReferenceManager.instance().clearSession();
    processItemSets(formMediaDir);
    // This should get moved to the Application Class
    if (ReferenceManager.instance().getFactories().length == 0) {
        // this is /sdcard/odk
        ReferenceManager.instance().addReferenceFactory(new FileReferenceFactory(Collect.ODK_ROOT));
    }
    // Set jr://... to point to /sdcard/odk/forms/filename-media/
    ReferenceManager.instance().addSessionRootTranslator(new RootTranslator("jr://images/", "jr://file/forms/" + formFileName + "-media/"));
    ReferenceManager.instance().addSessionRootTranslator(new RootTranslator("jr://image/", "jr://file/forms/" + formFileName + "-media/"));
    ReferenceManager.instance().addSessionRootTranslator(new RootTranslator("jr://audio/", "jr://file/forms/" + formFileName + "-media/"));
    ReferenceManager.instance().addSessionRootTranslator(new RootTranslator("jr://video/", "jr://file/forms/" + formFileName + "-media/"));
    final FormController fc = new FormController(formMediaDir, fec, instancePath == null ? null : new File(instancePath));
    if (xpath != null) {
        // we are resuming after having terminated -- set index to this
        // position...
        FormIndex idx = fc.getIndexFromXPath(xpath);
        fc.jumpToIndex(idx);
    }
    if (waitingXPath != null) {
        FormIndex idx = fc.getIndexFromXPath(waitingXPath);
        fc.setIndexWaitingForData(idx);
    }
    data = new FECWrapper(fc, usedSavepoint);
    return data;
}
Also used : FormController(org.odk.collect.android.logic.FormController) FormEntryController(org.javarosa.form.api.FormEntryController) FormEntryModel(org.javarosa.form.api.FormEntryModel) ExternalDataHandlerPull(org.odk.collect.android.external.handler.ExternalDataHandlerPull) ExternalDataHandler(org.odk.collect.android.external.ExternalDataHandler) RootTranslator(org.javarosa.core.reference.RootTranslator) IOException(java.io.IOException) XPathTypeMismatchException(org.javarosa.xpath.XPathTypeMismatchException) ExternalDataManagerImpl(org.odk.collect.android.external.ExternalDataManagerImpl) FormDef(org.javarosa.core.model.FormDef) FileReferenceFactory(org.odk.collect.android.logic.FileReferenceFactory) FormIndex(org.javarosa.core.model.FormIndex) XPathTypeMismatchException(org.javarosa.xpath.XPathTypeMismatchException) File(java.io.File)

Example 9 with FormController

use of org.odk.collect.android.logic.FormController in project collect by opendatakit.

the class FormEntryActivity method showNextView.

/**
 * Determines what should be displayed on the screen. Possible options are:
 * a question, an ask repeat dialog, or the submit screen. Also saves
 * answers to the data model after checking constraints.
 */
private void showNextView() {
    state = null;
    try {
        FormController formController = getFormController();
        // get constraint behavior preference value with appropriate default
        String constraintBehavior = (String) GeneralSharedPreferences.getInstance().get(PreferenceKeys.KEY_CONSTRAINT_BEHAVIOR);
        if (formController != null && formController.currentPromptIsQuestion()) {
            // if constraint behavior says we should validate on swipe, do so
            if (constraintBehavior.equals(PreferenceKeys.CONSTRAINT_BEHAVIOR_ON_SWIPE)) {
                if (!saveAnswersForCurrentScreen(EVALUATE_CONSTRAINTS)) {
                    // A constraint was violated so a dialog should be showing.
                    beenSwiped = false;
                    return;
                }
            // otherwise, just save without validating (constraints will be validated on
            // finalize)
            } else {
                saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            }
        }
        View next;
        int originalEvent = formController.getEvent();
        int event = formController.stepToNextScreenEvent();
        // she will stay on the same screen)
        if (originalEvent == event && originalEvent == FormEntryController.EVENT_END_OF_FORM) {
            beenSwiped = false;
            return;
        }
        // Close timer events waiting for an end time
        formController.getTimerLogger().exitView();
        switch(event) {
            case FormEntryController.EVENT_QUESTION:
            case FormEntryController.EVENT_GROUP:
                // create a savepoint
                if ((++viewCount) % SAVEPOINT_INTERVAL == 0) {
                    nonblockingCreateSavePointData();
                }
                next = createView(event, true);
                showView(next, AnimationType.RIGHT);
                break;
            case FormEntryController.EVENT_END_OF_FORM:
            case FormEntryController.EVENT_REPEAT:
            case FormEntryController.EVENT_PROMPT_NEW_REPEAT:
                next = createView(event, true);
                showView(next, AnimationType.RIGHT);
                break;
            case FormEntryController.EVENT_REPEAT_JUNCTURE:
                Timber.i("Repeat juncture: %s", formController.getFormIndex().getReference());
                // skip repeat junctures until we implement them
                break;
            default:
                Timber.w("JavaRosa added a new EVENT type and didn't tell us... shame on them.");
                break;
        }
    } catch (JavaRosaException e) {
        Timber.d(e);
        createErrorDialog(e.getCause().getMessage(), DO_NOT_EXIT);
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) ODKView(org.odk.collect.android.views.ODKView) View(android.view.View) AdapterView(android.widget.AdapterView) TextView(android.widget.TextView) ListView(android.widget.ListView) FailedConstraint(org.odk.collect.android.logic.FormController.FailedConstraint) JavaRosaException(org.odk.collect.android.exception.JavaRosaException)

Example 10 with FormController

use of org.odk.collect.android.logic.FormController in project collect by opendatakit.

the class FormEntryActivity method createView.

/**
 * Creates a view given the View type and an event
 *
 * @param advancingPage -- true if this results from advancing through the form
 * @return newly created View
 */
private View createView(int event, boolean advancingPage) {
    FormController formController = getFormController();
    setTitle(formController.getFormTitle());
    formController.getTimerLogger().logTimerEvent(TimerLogger.EventTypes.FEC, event, formController.getFormIndex().getReference(), advancingPage, true);
    switch(event) {
        case FormEntryController.EVENT_BEGINNING_OF_FORM:
            return createViewForFormBeginning(event, true, formController);
        case FormEntryController.EVENT_END_OF_FORM:
            View endView = View.inflate(this, R.layout.form_entry_end, null);
            ((TextView) endView.findViewById(R.id.description)).setText(getString(R.string.save_enter_data_description, formController.getFormTitle()));
            // checkbox for if finished or ready to send
            final CheckBox instanceComplete = endView.findViewById(R.id.mark_finished);
            instanceComplete.setChecked(InstancesDaoHelper.isInstanceComplete(true));
            if (!(boolean) AdminSharedPreferences.getInstance().get(AdminKeys.KEY_MARK_AS_FINALIZED)) {
                instanceComplete.setVisibility(View.GONE);
            }
            // edittext to change the displayed name of the instance
            final EditText saveAs = endView.findViewById(R.id.save_name);
            // disallow carriage returns in the name
            InputFilter returnFilter = new InputFilter() {

                public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                    for (int i = start; i < end; i++) {
                        if (Character.getType((source.charAt(i))) == Character.CONTROL) {
                            return "";
                        }
                    }
                    return null;
                }
            };
            saveAs.setFilters(new InputFilter[] { returnFilter });
            if (formController.getSubmissionMetadata().instanceName == null) {
                // no meta/instanceName field in the form -- see if we have a
                // name for this instance from a previous save attempt...
                String uriMimeType = null;
                Uri instanceUri = getIntent().getData();
                if (instanceUri != null) {
                    uriMimeType = getContentResolver().getType(instanceUri);
                }
                if (saveName == null && uriMimeType != null && uriMimeType.equals(InstanceColumns.CONTENT_ITEM_TYPE)) {
                    Cursor instance = null;
                    try {
                        instance = getContentResolver().query(instanceUri, null, null, null, null);
                        if (instance != null && instance.getCount() == 1) {
                            instance.moveToFirst();
                            saveName = instance.getString(instance.getColumnIndex(InstanceColumns.DISPLAY_NAME));
                        }
                    } finally {
                        if (instance != null) {
                            instance.close();
                        }
                    }
                }
                if (saveName == null) {
                    // last resort, default to the form title
                    saveName = formController.getFormTitle();
                }
                // present the prompt to allow user to name the form
                TextView sa = endView.findViewById(R.id.save_form_as);
                sa.setVisibility(View.VISIBLE);
                saveAs.setText(saveName);
                saveAs.setEnabled(true);
                saveAs.setVisibility(View.VISIBLE);
                saveAs.addTextChangedListener(new TextWatcher() {

                    @Override
                    public void afterTextChanged(Editable s) {
                        saveName = String.valueOf(s);
                    }

                    @Override
                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    }

                    @Override
                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                    }
                });
            } else {
                // if instanceName is defined in form, this is the name -- no
                // revisions
                // display only the name, not the prompt, and disable edits
                saveName = formController.getSubmissionMetadata().instanceName;
                TextView sa = endView.findViewById(R.id.save_form_as);
                sa.setVisibility(View.GONE);
                saveAs.setText(saveName);
                saveAs.setEnabled(false);
                saveAs.setVisibility(View.VISIBLE);
            }
            // override the visibility settings based upon admin preferences
            if (!(boolean) AdminSharedPreferences.getInstance().get(AdminKeys.KEY_SAVE_AS)) {
                saveAs.setVisibility(View.GONE);
                TextView sa = endView.findViewById(R.id.save_form_as);
                sa.setVisibility(View.GONE);
            }
            // Create 'save' button
            endView.findViewById(R.id.save_exit_button).setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Collect.getInstance().getActivityLogger().logInstanceAction(this, "createView.saveAndExit", instanceComplete.isChecked() ? "saveAsComplete" : "saveIncomplete");
                    // Form is marked as 'saved' here.
                    if (saveAs.getText().length() < 1) {
                        ToastUtils.showShortToast(R.string.save_as_error);
                    } else {
                        saveDataToDisk(EXIT, instanceComplete.isChecked(), saveAs.getText().toString());
                    }
                }
            });
            if (showNavigationButtons) {
                backButton.setEnabled(allowMovingBackwards);
                nextButton.setEnabled(false);
            }
            return endView;
        case FormEntryController.EVENT_QUESTION:
        case FormEntryController.EVENT_GROUP:
        case FormEntryController.EVENT_REPEAT:
            releaseOdkView();
            // should only be a group here if the event_group is a field-list
            try {
                FormEntryPrompt[] prompts = formController.getQuestionPrompts();
                FormEntryCaption[] groups = formController.getGroupsForCurrentIndex();
                odkView = new ODKView(this, prompts, groups, advancingPage);
                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 e) {
                Timber.e(e);
                // this is badness to avoid a crash.
                try {
                    event = formController.stepToNextScreenEvent();
                    createErrorDialog(e.getMessage(), DO_NOT_EXIT);
                } catch (JavaRosaException e1) {
                    Timber.d(e1);
                    createErrorDialog(e.getMessage() + "\n\n" + e1.getCause().getMessage(), DO_NOT_EXIT);
                }
                return createView(event, advancingPage);
            }
            // Makes a "clear answer" menu pop up on long-click
            for (QuestionWidget qw : odkView.getWidgets()) {
                if (!qw.getFormEntryPrompt().isReadOnly()) {
                    // we want to enable paste option after long click on the EditText
                    if (qw instanceof StringWidget) {
                        for (int i = 0; i < qw.getChildCount(); i++) {
                            if (!(qw.getChildAt(i) instanceof EditText)) {
                                registerForContextMenu(qw.getChildAt(i));
                            }
                        }
                    } else {
                        registerForContextMenu(qw);
                    }
                }
            }
            if (showNavigationButtons) {
                backButton.setEnabled(!formController.isCurrentQuestionFirstInForm() && allowMovingBackwards);
                nextButton.setEnabled(true);
            }
            return odkView;
        case FormEntryController.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), EXIT);
            } catch (JavaRosaException e) {
                Timber.d(e);
                createErrorDialog(e.getCause().getMessage(), EXIT);
            }
            return createView(event, advancingPage);
    }
}
Also used : FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) ODKView(org.odk.collect.android.views.ODKView) FormEntryCaption(org.javarosa.form.api.FormEntryCaption) Cursor(android.database.Cursor) Uri(android.net.Uri) TextWatcher(android.text.TextWatcher) Editable(android.text.Editable) TextView(android.widget.TextView) StringWidget(org.odk.collect.android.widgets.StringWidget) QuestionWidget(org.odk.collect.android.widgets.QuestionWidget) FormController(org.odk.collect.android.logic.FormController) EditText(android.widget.EditText) InputFilter(android.text.InputFilter) ODKView(org.odk.collect.android.views.ODKView) View(android.view.View) AdapterView(android.widget.AdapterView) TextView(android.widget.TextView) ListView(android.widget.ListView) Spanned(android.text.Spanned) FailedConstraint(org.odk.collect.android.logic.FormController.FailedConstraint) CheckBox(android.widget.CheckBox) OnClickListener(android.view.View.OnClickListener) JavaRosaException(org.odk.collect.android.exception.JavaRosaException)

Aggregations

FormController (org.odk.collect.android.logic.FormController)42 FailedConstraint (org.odk.collect.android.logic.FormController.FailedConstraint)10 FormIndex (org.javarosa.core.model.FormIndex)8 JavaRosaException (org.odk.collect.android.exception.JavaRosaException)8 File (java.io.File)7 OnClickListener (android.view.View.OnClickListener)6 ODKView (org.odk.collect.android.views.ODKView)6 DialogInterface (android.content.DialogInterface)5 View (android.view.View)5 AdapterView (android.widget.AdapterView)5 ListView (android.widget.ListView)5 TextView (android.widget.TextView)5 IOException (java.io.IOException)4 FormEntryPrompt (org.javarosa.form.api.FormEntryPrompt)4 Collect (org.odk.collect.android.application.Collect)4 GDriveConnectionException (org.odk.collect.android.exception.GDriveConnectionException)4 ContentValues (android.content.ContentValues)3 Intent (android.content.Intent)3 Cursor (android.database.Cursor)3 FormEntryCaption (org.javarosa.form.api.FormEntryCaption)3