package org.odk.collect.android.jr.extensions; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import org.javarosa.core.model.Constants; import org.javarosa.core.model.FormDef; import org.javarosa.core.model.condition.EvaluationContext; import org.javarosa.core.model.condition.Recalculate; import org.javarosa.core.model.data.AnswerDataFactory; import org.javarosa.core.model.data.IAnswerData; import org.javarosa.core.model.data.StringData; import org.javarosa.core.model.instance.AbstractTreeElement; import org.javarosa.core.model.instance.FormInstance; import org.javarosa.core.model.instance.TreeElement; import org.javarosa.core.model.instance.TreeReference; import org.javarosa.core.util.externalizable.DeserializationException; import org.javarosa.core.util.externalizable.ExtUtil; import org.javarosa.core.util.externalizable.ExtWrapMap; import org.javarosa.core.util.externalizable.Externalizable; import org.javarosa.core.util.externalizable.PrototypeFactory; import org.odk.collect.android.utilities.FileUtils; import org.odk.collect.android.views.ODKView; import android.content.ComponentName; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Log; /** * @author ctsims * */ public class IntentCallout implements Externalizable { private String className; private Hashtable<String, TreeReference> refs; private Hashtable<String, TreeReference> responses; private FormDef form; private String type; private String component; private String data; private String buttonLabel; // Generic Extra from intent callout extensions public static final String INTENT_RESULT_VALUE = "odk_intent_data"; // Bundle of extra values public static final String INTENT_RESULT_BUNDLE = "odk_intent_bundle"; public IntentCallout() { } public IntentCallout(String className, Hashtable<String, TreeReference> refs, Hashtable<String, TreeReference> responses, String type, String component, String data, String buttonLabel) { this.className = className; this.refs = refs; this.responses = responses; this.type = type; this.component = component; this.data = data; this.buttonLabel = buttonLabel; } protected void attachToForm(FormDef form) { this.form = form; } public Intent generate(EvaluationContext ec) { Intent i = new Intent(); if(className != null){ i.setAction(className); if(component != null){ i.setComponent(new ComponentName(component, className)); } } if(type != null){ i.setType(type); } if(data != null){ i.setData(Uri.parse(data)); } for(Enumeration<String> en = refs.keys() ; en.hasMoreElements() ;) { String key = en.nextElement(); AbstractTreeElement e = ec.resolveReference(refs.get(key)); if(e != null && e.getValue() != null) { i.putExtra(key, e.getValue().uncast().getString()); } } return i; } public void processResponse(Intent intent, ODKView currentView, FormInstance instance, File destination) { String result = intent.getStringExtra(INTENT_RESULT_VALUE); ((ODKView) currentView).setBinaryData(result); //see if we have a return bundle Bundle response = intent.getBundleExtra(INTENT_RESULT_BUNDLE); //Load all of the data from the incoming bundle for(String key : responses.keySet()) { //See if the value exists at all, if not, skip it if(!response.containsKey(key)) { continue;} //Get our response value String responseValue = response.getString(key); if(key == null) { key = "";} //Figure out where it's going TreeReference ref = responses.get(key); EvaluationContext context = new EvaluationContext(form.getEvaluationContext(), ref); AbstractTreeElement node = context.resolveReference(ref); if(node == null) { //continue? } int dataType = node.getDataType(); //TODO: Handle file system errors in a way that is more visible to the user //See if this is binary data and we'll have to do something complex... if(dataType == Constants.DATATYPE_BINARY) { //We need to copy the binary data at this address into the appropriate location if(responseValue == null || responseValue.equals("")) { //If the response was blank, wipe out any data that was present before form.setValue(null, ref); continue; } //Otherwise, grab that file File src = new File(responseValue); if(!src.exists()) { //TODO: How hard should we be failing here? Log.w("FormEntryActivity-Callout", "ODK received a link to a file at " + src.toString() + " to be included in the form, but it was not present on the phone!"); //Wipe out any reference that exists form.setValue(null, ref); continue; } File newFile = new File(destination, src.getName()); //Looks like our source file exists, so let's go grab it FileUtils.copyFile(src, newFile); //That code throws no errors, so we have to manually check whether the copy worked. if(newFile.exists() && newFile.length() == src.length()) { form.setValue(new StringData(newFile.toString()), ref); continue; } else { Log.e("FormEntryActivity-Callout", "ODK Failed to property write a file to " + newFile.toString()); form.setValue(null, ref); continue; } } //otherwise, just load it up IAnswerData val = Recalculate.wrapData(responseValue, dataType); form.setValue(val == null ? null: AnswerDataFactory.templateByDataType(dataType).cast(val.uncast()), ref); } } /* * (non-Javadoc) * @see org.javarosa.core.util.externalizable.Externalizable#readExternal(java.io.DataInputStream, org.javarosa.core.util.externalizable.PrototypeFactory) */ @Override public void readExternal(DataInputStream in, PrototypeFactory pf) throws IOException, DeserializationException { className = ExtUtil.readString(in); refs = (Hashtable<String, TreeReference>)ExtUtil.read(in, new ExtWrapMap(String.class, TreeReference.class), pf); responses = (Hashtable<String, TreeReference>)ExtUtil.read(in, new ExtWrapMap(String.class, TreeReference.class), pf); } /* * (non-Javadoc) * @see org.javarosa.core.util.externalizable.Externalizable#writeExternal(java.io.DataOutputStream) */ @Override public void writeExternal(DataOutputStream out) throws IOException { ExtUtil.writeString(out, className); ExtUtil.write(out, new ExtWrapMap(refs)); ExtUtil.write(out, new ExtWrapMap(responses)); } public String getButtonLabel(){ return buttonLabel; } }