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;
}
}
}
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);
}
}
}
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;
}
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);
}
}
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);
}
}
Aggregations