Search in sources :

Example 31 with FormController

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

the class FormEntryActivity method onSaveInstanceState.

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString(KEY_FORMPATH, formPath);
    FormController formController = getFormController();
    if (formController != null) {
        if (formController.getInstanceFile() != null) {
            outState.putString(KEY_INSTANCEPATH, getAbsoluteInstancePath());
        }
        outState.putString(KEY_XPATH, formController.getXPath(formController.getFormIndex()));
        FormIndex waiting = formController.getIndexWaitingForData();
        if (waiting != null) {
            outState.putString(KEY_XPATH_WAITING_FOR_DATA, formController.getXPath(waiting));
        }
        // save the instance to a temp path...
        nonblockingCreateSavePointData();
    }
    outState.putBoolean(NEWFORM, false);
    outState.putString(KEY_ERROR, errorMessage);
    outState.putString(KEY_SAVE_NAME, saveName);
    outState.putBoolean(KEY_AUTO_SAVED, autoSaved);
    if (currentView instanceof ODKView) {
        outState.putAll(((ODKView) currentView).getState());
        // This value is originally set in onCreate() method but if you only minimize the app or
        // block/unblock the screen, onCreate() method might not be called (if the activity is just paused
        // not stopped https://developer.android.com/guide/components/activities/activity-lifecycle.html)
        state = outState;
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) ODKView(org.odk.collect.android.views.ODKView) FormIndex(org.javarosa.core.model.FormIndex)

Example 32 with FormController

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

the class FormEntryActivity method onActivityResult.

@Override
protected void onActivityResult(int requestCode, int resultCode, final Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    FormController formController = getFormController();
    if (formController == null) {
        // try to save this callback data to the FormLoaderTask
        if (formLoaderTask != null && formLoaderTask.getStatus() != AsyncTask.Status.FINISHED) {
            formLoaderTask.setActivityResult(requestCode, resultCode, intent);
        } else {
            Timber.e("Got an activityResult without any pending form loader");
        }
        return;
    }
    if (resultCode == RESULT_CANCELED) {
        // request was canceled...
        if (requestCode != RequestCodes.HIERARCHY_ACTIVITY && getCurrentViewIfODKView() != null) {
            getCurrentViewIfODKView().cancelWaitingForBinaryData();
        }
        return;
    }
    // intent is needed for all requestCodes except of DRAW_IMAGE, ANNOTATE_IMAGE, SIGNATURE_CAPTURE, IMAGE_CAPTURE and HIERARCHY_ACTIVITY
    if (intent == null && requestCode != RequestCodes.DRAW_IMAGE && requestCode != RequestCodes.ANNOTATE_IMAGE && requestCode != RequestCodes.SIGNATURE_CAPTURE && requestCode != RequestCodes.IMAGE_CAPTURE && requestCode != RequestCodes.HIERARCHY_ACTIVITY) {
        Timber.w("The intent has a null value for requestCode: " + requestCode);
        ToastUtils.showLongToast(getString(R.string.null_intent_value));
        return;
    }
    // For handling results returned by the Zxing Barcode scanning library
    IntentResult barcodeScannerResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent);
    if (barcodeScannerResult != null) {
        if (barcodeScannerResult.getContents() == null) {
            // request was canceled...
            Timber.i("QR code scanning cancelled");
        } else {
            String sb = intent.getStringExtra("SCAN_RESULT");
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(sb);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            refreshCurrentView();
            return;
        }
    }
    switch(requestCode) {
        case RequestCodes.OSM_CAPTURE:
            String osmFileName = intent.getStringExtra("OSM_FILE_NAME");
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(osmFileName);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.EX_STRING_CAPTURE:
        case RequestCodes.EX_INT_CAPTURE:
        case RequestCodes.EX_DECIMAL_CAPTURE:
            String key = "value";
            boolean exists = intent.getExtras().containsKey(key);
            if (exists) {
                Object externalValue = intent.getExtras().get(key);
                if (getCurrentViewIfODKView() != null) {
                    getCurrentViewIfODKView().setBinaryData(externalValue);
                }
                saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            }
            break;
        case RequestCodes.EX_GROUP_CAPTURE:
            try {
                Bundle extras = intent.getExtras();
                if (getCurrentViewIfODKView() != null) {
                    getCurrentViewIfODKView().setDataForFields(extras);
                }
            } catch (JavaRosaException e) {
                Timber.e(e);
                createErrorDialog(e.getCause().getMessage(), DO_NOT_EXIT);
            }
            break;
        case RequestCodes.DRAW_IMAGE:
        case RequestCodes.ANNOTATE_IMAGE:
        case RequestCodes.SIGNATURE_CAPTURE:
        case RequestCodes.IMAGE_CAPTURE:
            /*
                 * We saved the image to the tempfile_path, but we really want it to
                 * be in: /sdcard/odk/instances/[current instnace]/something.jpg so
                 * we move it there before inserting it into the content provider.
                 * Once the android image capture bug gets fixed, (read, we move on
                 * from Android 1.6) we want to handle images the audio and video
                 */
            // The intent is empty, but we know we saved the image to the temp
            // file
            ImageConverter.execute(Collect.TMPFILE_PATH, getWidgetWaitingForBinaryData(), this);
            File fi = new File(Collect.TMPFILE_PATH);
            String instanceFolder = formController.getInstanceFile().getParent();
            String s = instanceFolder + File.separator + System.currentTimeMillis() + ".jpg";
            File nf = new File(s);
            if (!fi.renameTo(nf)) {
                Timber.e("Failed to rename %s", fi.getAbsolutePath());
            } else {
                Timber.i("Renamed %s to %s", fi.getAbsolutePath(), nf.getAbsolutePath());
            }
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(nf);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.ALIGNED_IMAGE:
            /*
                 * We saved the image to the tempfile_path; the app returns the full
                 * path to the saved file in the EXTRA_OUTPUT extra. Take that file
                 * and move it into the instance folder.
                 */
            String path = intent.getStringExtra(android.provider.MediaStore.EXTRA_OUTPUT);
            fi = new File(path);
            instanceFolder = formController.getInstanceFile().getParent();
            s = instanceFolder + File.separator + System.currentTimeMillis() + ".jpg";
            nf = new File(s);
            if (!fi.renameTo(nf)) {
                Timber.e("Failed to rename %s", fi.getAbsolutePath());
            } else {
                Timber.i("Renamed %s to %s", fi.getAbsolutePath(), nf.getAbsolutePath());
            }
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(nf);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.IMAGE_CHOOSER:
            /*
                 * We have a saved image somewhere, but we really want it to be in:
                 * /sdcard/odk/instances/[current instnace]/something.jpg so we move
                 * it there before inserting it into the content provider. Once the
                 * android image capture bug gets fixed, (read, we move on from
                 * Android 1.6) we want to handle images the audio and video
                 */
            showDialog(SAVING_IMAGE_DIALOG);
            Runnable runnable = () -> saveChosenImage(intent.getData());
            new Thread(runnable).start();
            break;
        case RequestCodes.AUDIO_CAPTURE:
        case RequestCodes.VIDEO_CAPTURE:
            Uri mediaUri = intent.getData();
            saveAudioVideoAnswer(mediaUri);
            String filePath = MediaUtils.getDataColumn(this, mediaUri, null, null);
            if (filePath != null) {
                new File(filePath).delete();
            }
            try {
                getContentResolver().delete(mediaUri, null, null);
            } catch (Exception e) {
                Timber.e(e);
            }
            break;
        case RequestCodes.AUDIO_CHOOSER:
        case RequestCodes.VIDEO_CHOOSER:
            saveAudioVideoAnswer(intent.getData());
            break;
        case RequestCodes.LOCATION_CAPTURE:
            String sl = intent.getStringExtra(LOCATION_RESULT);
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(sl);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.GEOSHAPE_CAPTURE:
            String gshr = intent.getStringExtra(GEOSHAPE_RESULTS);
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(gshr);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.GEOTRACE_CAPTURE:
            String traceExtra = intent.getStringExtra(GEOTRACE_RESULTS);
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(traceExtra);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.BEARING_CAPTURE:
            String bearing = intent.getStringExtra(BEARING_RESULT);
            if (getCurrentViewIfODKView() != null) {
                getCurrentViewIfODKView().setBinaryData(bearing);
            }
            saveAnswersForCurrentScreen(DO_NOT_EVALUATE_CONSTRAINTS);
            break;
        case RequestCodes.HIERARCHY_ACTIVITY:
            // refresh
            break;
    }
    refreshCurrentView();
}
Also used : FormController(org.odk.collect.android.logic.FormController) IntentResult(com.google.zxing.integration.android.IntentResult) Bundle(android.os.Bundle) File(java.io.File) Uri(android.net.Uri) JavaRosaException(org.odk.collect.android.exception.JavaRosaException) GDriveConnectionException(org.odk.collect.android.exception.GDriveConnectionException) JavaRosaException(org.odk.collect.android.exception.JavaRosaException)

Example 33 with FormController

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

the class FormEntryActivity method removeTempInstance.

// Cleanup when user exits a form without saving
private void removeTempInstance() {
    FormController formController = getFormController();
    if (formController != null && formController.getInstanceFile() != null) {
        SaveToDiskTask.removeSavepointFiles(formController.getInstanceFile().getName());
    }
    // if it's not already saved, erase everything
    if (!InstancesDaoHelper.isInstanceAvailable(getAbsoluteInstancePath())) {
        // delete media first
        String instanceFolder = formController.getInstanceFile().getParent();
        Timber.i("Attempting to delete: %s", instanceFolder);
        File file = formController.getInstanceFile().getParentFile();
        int images = MediaUtils.deleteImagesInFolderFromMediaProvider(file);
        int audio = MediaUtils.deleteAudioInFolderFromMediaProvider(file);
        int video = MediaUtils.deleteVideoInFolderFromMediaProvider(file);
        Timber.i("Removed from content providers: %d image files, %d audio files and %d audio files.", images, audio, video);
        FileUtils.purgeMediaPath(instanceFolder);
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) File(java.io.File) FailedConstraint(org.odk.collect.android.logic.FormController.FailedConstraint)

Example 34 with FormController

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

the class FormHierarchyActivity method refreshView.

public void refreshView() {
    try {
        FormController formController = Collect.getInstance().getFormController();
        // Record the current index so we can return to the same place if the user hits 'back'.
        currentIndex = formController.getFormIndex();
        // If we're not at the first level, we're inside a repeated group so we want to only
        // display
        // everything enclosed within that group.
        String contextGroupRef = "";
        formList = new ArrayList<HierarchyElement>();
        // node to display.
        if (formController.getEvent() == FormEntryController.EVENT_REPEAT) {
            contextGroupRef = formController.getFormIndex().getReference().toString(true);
            formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
        } else {
            FormIndex startTest = formController.stepIndexOut(currentIndex);
            // beginning.
            while (startTest != null && formController.getEvent(startTest) == FormEntryController.EVENT_GROUP) {
                startTest = formController.stepIndexOut(startTest);
            }
            if (startTest == null) {
                // check to see if the question is at the first level of the hierarchy. If it
                // is,
                // display the root level from the beginning.
                formController.jumpToIndex(FormIndex.createBeginningOfFormIndex());
            } else {
                // otherwise we're at a repeated group
                formController.jumpToIndex(startTest);
            }
            // beginning
            if (formController.getEvent() == FormEntryController.EVENT_REPEAT) {
                contextGroupRef = formController.getFormIndex().getReference().toString(true);
                formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
            }
        }
        int event = formController.getEvent();
        if (event == FormEntryController.EVENT_BEGINNING_OF_FORM) {
            // The beginning of form has no valid prompt to display.
            formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
            contextGroupRef = formController.getFormIndex().getReference().getParentRef().toString(true);
            path.setVisibility(View.GONE);
            jumpPreviousButton.setEnabled(false);
        } else {
            path.setVisibility(View.VISIBLE);
            path.setText(getCurrentPath());
            jumpPreviousButton.setEnabled(true);
        }
        // Refresh the current event in case we did step forward.
        event = formController.getEvent();
        // Big change from prior implementation:
        // 
        // The ref strings now include the instance number designations
        // i.e., [0], [1], etc. of the repeat groups (and also [1] for
        // non-repeat elements).
        // 
        // The contextGroupRef is now also valid for the top-level form.
        // 
        // The repeatGroupRef is null if we are not skipping a repeat
        // section.
        // 
        String repeatGroupRef = null;
        event_search: while (event != FormEntryController.EVENT_END_OF_FORM) {
            // get the ref to this element
            String currentRef = formController.getFormIndex().getReference().toString(true);
            // retrieve the current group
            String curGroup = (repeatGroupRef == null) ? contextGroupRef : repeatGroupRef;
            if (!currentRef.startsWith(curGroup)) {
                // We have left the current group
                if (repeatGroupRef == null) {
                    // We are done.
                    break;
                } else {
                    // exit the inner repeat group
                    repeatGroupRef = null;
                }
            }
            if (repeatGroupRef != null) {
                // We're in a repeat 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:
                    FormEntryPrompt fp = formController.getQuestionPrompt();
                    String label = fp.getLongText();
                    if (!fp.isReadOnly() || (label != null && label.length() > 0)) {
                        // show the question if it is an editable field.
                        // or if it is read-only and the label is not blank.
                        String answerDisplay = FormEntryPromptUtils.getAnswerText(fp, this, formController);
                        formList.add(new HierarchyElement(FormEntryPromptUtils.markQuestionIfIsRequired(label, fp.isRequired()), answerDisplay, null, Color.WHITE, QUESTION, fp.getIndex()));
                    }
                    break;
                case FormEntryController.EVENT_GROUP:
                    // ignore group events
                    break;
                case FormEntryController.EVENT_PROMPT_NEW_REPEAT:
                    // ignore it.
                    break;
                case FormEntryController.EVENT_REPEAT:
                    FormEntryCaption fc = formController.getCaptionPrompt();
                    // push this repeat onto the stack.
                    repeatGroupRef = currentRef;
                    if (fc.getMultiplicity() == 0) {
                        // Display the repeat header for the group.
                        HierarchyElement group = new HierarchyElement(fc.getLongText(), null, ContextCompat.getDrawable(getApplicationContext(), R.drawable.expander_ic_minimized), Color.WHITE, COLLAPSED, fc.getIndex());
                        formList.add(group);
                    }
                    String repeatLabel = mIndent + fc.getLongText();
                    if (fc.getFormElement().getChildren().size() == 1 && fc.getFormElement().getChild(0) instanceof GroupDef) {
                        formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
                        FormEntryCaption fc2 = formController.getCaptionPrompt();
                        if (fc2.getLongText() != null) {
                            repeatLabel = fc2.getLongText();
                        }
                    }
                    repeatLabel += " (" + (fc.getMultiplicity() + 1) + ")";
                    // Add this group name to the drop down list for this repeating group.
                    HierarchyElement h = formList.get(formList.size() - 1);
                    h.addChild(new HierarchyElement(repeatLabel, null, null, Color.WHITE, CHILD, fc.getIndex()));
                    break;
            }
            event = formController.stepToNextEvent(FormController.STEP_INTO_GROUP);
        }
        HierarchyListAdapter itla = new HierarchyListAdapter(this);
        itla.setListItems(formList);
        listView.setAdapter(itla);
        // set the controller back to the current index in case the user hits 'back'
        formController.jumpToIndex(currentIndex);
    } catch (Exception e) {
        Timber.e(e);
        createErrorDialog(e.getMessage());
    }
}
Also used : FormController(org.odk.collect.android.logic.FormController) FormEntryPrompt(org.javarosa.form.api.FormEntryPrompt) HierarchyElement(org.odk.collect.android.logic.HierarchyElement) 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)

Example 35 with FormController

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

the class SaveToDiskTask method doInBackground.

/**
 * Initialize {@link FormEntryController} with {@link org.javarosa.core.model.FormDef} from binary or from XML. If
 * given
 * an instance, it will be used to fill the {@link org.javarosa.core.model.FormDef}.
 */
@Override
protected SaveResult doInBackground(Void... nothing) {
    SaveResult saveResult = new SaveResult();
    FormController formController = Collect.getInstance().getFormController();
    publishProgress(Collect.getInstance().getString(R.string.survey_saving_validating_message));
    try {
        int validateStatus = formController.validateAnswers(markCompleted);
        if (validateStatus != FormEntryController.ANSWER_OK) {
            // validation failed, pass specific failure
            saveResult.setSaveResult(validateStatus, markCompleted);
            return saveResult;
        }
    } catch (Exception e) {
        Timber.e(e);
        // SCTO-825
        // that means that we have a bad design
        // save the exception to be used in the error dialog.
        saveResult.setSaveErrorMessage(e.getMessage());
        saveResult.setSaveResult(SAVE_ERROR, markCompleted);
        return saveResult;
    }
    // check if the "Cancel" was hit and exit.
    if (isCancelled()) {
        return null;
    }
    if (markCompleted) {
        formController.postProcessInstance();
    }
    Collect.getInstance().getActivityLogger().logInstanceAction(this, "save", Boolean.toString(markCompleted));
    // close all open databases of external data.
    Collect.getInstance().getExternalDataManager().close();
    // if there is a meta/instanceName field, be sure we are using the latest value
    // just in case the validate somehow triggered an update.
    String updatedSaveName = formController.getSubmissionMetadata().instanceName;
    if (updatedSaveName != null) {
        instanceName = updatedSaveName;
    }
    try {
        exportData(markCompleted);
        if (formController.getInstanceFile() != null) {
            removeSavepointFiles(formController.getInstanceFile().getName());
        }
        saveResult.setSaveResult(save ? SAVED_AND_EXIT : SAVED, markCompleted);
    } catch (EncryptionException e) {
        saveResult.setSaveErrorMessage(e.getMessage());
        saveResult.setSaveResult(ENCRYPTION_ERROR, markCompleted);
    } catch (Exception e) {
        Timber.e(e);
        saveResult.setSaveErrorMessage(e.getMessage());
        saveResult.setSaveResult(SAVE_ERROR, markCompleted);
    }
    return saveResult;
}
Also used : FormController(org.odk.collect.android.logic.FormController) EncryptionException(org.odk.collect.android.exception.EncryptionException) IOException(java.io.IOException) EncryptionException(org.odk.collect.android.exception.EncryptionException)

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