package org.commcare.views.widgets; import android.content.Context; import android.content.Intent; import android.util.Log; import org.commcare.adapters.ComboboxAdapter; import org.commcare.android.javarosa.AndroidXFormExtensions; import org.commcare.android.javarosa.IntentCallout; import org.commcare.logic.PendingCalloutInterface; import org.commcare.utils.AndroidArrayDataSource; import org.javarosa.core.model.ComboboxFilterRule; import org.javarosa.core.model.Constants; import org.javarosa.core.model.FormDef; import org.javarosa.core.model.FuzzyMatchFilterRule; import org.javarosa.core.model.MultiWordFilterRule; import org.javarosa.core.model.QuestionDataExtension; import org.javarosa.core.model.StandardFilterRule; import org.javarosa.core.model.condition.EvaluationContext; import org.javarosa.core.services.locale.Localization; import org.javarosa.form.api.FormEntryPrompt; import org.javarosa.xform.util.CalendarUtils; /** * Convenience class that handles creation of widgets. * * @author Carl Hartung (carlhartung@gmail.com) */ public class WidgetFactory { private final FormDef formDef; private final PendingCalloutInterface pendingCalloutInterface; public WidgetFactory(FormDef formDef, PendingCalloutInterface pendingCalloutInterface) { this.formDef = formDef; this.pendingCalloutInterface = pendingCalloutInterface; } /** * Returns the appropriate QuestionWidget for the given FormEntryPrompt. * @param fep prompt element to be rendered * @param context Android context */ public QuestionWidget createWidgetFromPrompt(FormEntryPrompt fep, Context context) { QuestionWidget questionWidget; String appearance = fep.getAppearanceHint(); switch (fep.getControlType()) { case Constants.CONTROL_INPUT: if (appearance != null && appearance.startsWith("intent:")) { questionWidget = buildIntentWidget(appearance, fep, context); break; } case Constants.CONTROL_SECRET: questionWidget = buildBasicWidget(appearance, fep, context); break; case Constants.CONTROL_IMAGE_CHOOSE: if (appearance != null && appearance.equals("signature")) { questionWidget = new SignatureWidget(context, fep, pendingCalloutInterface); } else { questionWidget = new ImageWidget(context, fep, pendingCalloutInterface); } break; case Constants.CONTROL_AUDIO_CAPTURE: if (appearance != null && appearance.contains("prototype")) { questionWidget = new CommCareAudioWidget(context, fep, pendingCalloutInterface); } else { questionWidget = new AudioWidget(context, fep, pendingCalloutInterface); } break; case Constants.CONTROL_VIDEO_CAPTURE: questionWidget = new VideoWidget(context, fep, pendingCalloutInterface); break; case Constants.CONTROL_SELECT_ONE: questionWidget = buildSelectOne(appearance, fep, context); break; case Constants.CONTROL_SELECT_MULTI: questionWidget = buildSelectMulti(appearance, fep, context); break; case Constants.CONTROL_TRIGGER: questionWidget = new TriggerWidget(context, fep, appearance); break; default: questionWidget = new StringWidget(context, fep, false); break; } // Apply all of the QuestionDataExtensions registered with this widget's associated // QuestionDef to the widget for (QuestionDataExtension extension : fep.getQuestion().getExtensions()) { questionWidget.applyExtension(extension); } return questionWidget; } private QuestionWidget buildBasicWidget(String appearance, FormEntryPrompt fep, Context context) { switch (fep.getDataType()) { case Constants.DATATYPE_DATE_TIME: return new DateTimeWidget(context, fep); case Constants.DATATYPE_DATE: // Need to override CalendarUtil's month localizer CalendarUtils.setArrayDataSource(new AndroidArrayDataSource(context)); if (appearance != null && appearance.toLowerCase().equals("ethiopian")) { return new EthiopianDateWidget(context, fep); } else if (appearance != null && appearance.toLowerCase().equals("nepali")) { return new NepaliDateWidget(context, fep); } else if(appearance != null && appearance.toLowerCase().contains("gregorian")){ return new DatePrototypeFactory().getWidget(context, fep, appearance.toLowerCase()); } else { return new DateWidget(context, fep); } case Constants.DATATYPE_TIME: return new TimeWidget(context, fep); case Constants.DATATYPE_LONG: return new IntegerWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET, 2); case Constants.DATATYPE_DECIMAL: return new DecimalWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET); case Constants.DATATYPE_INTEGER: return new IntegerWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET, 1); case Constants.DATATYPE_GEOPOINT: return new GeoPointWidget(context, fep, pendingCalloutInterface); case Constants.DATATYPE_BARCODE: IntentCallout intentCallout = new IntentCallout("com.google.zxing.client.android.SCAN", null, null, null, null, null, Localization.get("intent.barcode.get"), Localization.get("intent.barcode.update"), appearance); intentCallout.attachToForm(formDef); Intent intent = intentCallout.generate(formDef.getEvaluationContext()); return new BarcodeWidget(context, fep, intent, intentCallout, pendingCalloutInterface); case Constants.DATATYPE_TEXT: if (appearance != null && (appearance.equalsIgnoreCase("numbers") || appearance.equalsIgnoreCase("numeric"))) { return new StringNumberWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET); } else { return new StringWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET); } default: return new StringWidget(context, fep, fep.getControlType() == Constants.CONTROL_SECRET); } } private IntentWidget buildIntentWidget(String appearance, FormEntryPrompt fep, Context context) { String intentId = appearance.substring("intent:".length()); IntentCallout ic = formDef.getExtension(AndroidXFormExtensions.class).getIntent(intentId, formDef); //Hm, so what do we do if no callout is found? Error? For now, fail fast if (ic == null) { throw new RuntimeException("No intent callout could be found for requested id " + intentId + "!"); } //NOTE: No path specific stuff for now Intent i = ic.generate(new EvaluationContext(formDef.getEvaluationContext(),fep.getIndex().getReference())); return new IntentWidget(context, fep, i, ic, pendingCalloutInterface); } private static QuestionWidget buildSelectOne(String appearance, FormEntryPrompt fep, Context context) { if (appearance != null && appearance.contains("compact")) { return buildCompactSelectOne(appearance, fep, context); } else if (appearance != null && appearance.equals("minimal")) { return new SpinnerWidget(context, fep); } else if (appearance != null && appearance.contains("combobox")) { return buildComboboxSelectOne(appearance, fep, context); } else if (appearance != null && appearance.equals("quick")) { return new SelectOneAutoAdvanceWidget(context, fep); } else if (appearance != null && appearance.equals("list")) { return new ListWidget(context, fep, true); } else if (appearance != null && appearance.equals("list-nolabel")) { return new ListWidget(context, fep, false); } else if (appearance != null && appearance.equals("label")) { return new LabelWidget(context, fep); } else { return new SelectOneWidget(context, fep); } } private static QuestionWidget buildCompactSelectOne(String appearance, FormEntryPrompt fep, Context context) { int numColumns = -1; try { numColumns = Integer.parseInt(appearance.substring(appearance.indexOf('-') + 1)); } catch (Exception e) { // Do nothing, leave numColumns as -1 Log.e("WidgetFactory", "Exception parsing numColumns"); } if (appearance.contains("quick")) { return new GridWidget(context, fep, numColumns, true); } else { return new GridWidget(context, fep, numColumns, false); } } private static QuestionWidget buildComboboxSelectOne(String appearance, FormEntryPrompt fep, Context context) { ComboboxFilterRule filterRule; if (appearance.contains("multiword")) { filterRule = new MultiWordFilterRule(); } else if (appearance.contains("fuzzy")) { filterRule = new FuzzyMatchFilterRule(); } else { filterRule = new StandardFilterRule(); } return new ComboboxWidget(context, fep, filterRule); } private static QuestionWidget buildSelectMulti(String appearance, FormEntryPrompt fep, Context context) { if (appearance != null && appearance.contains("compact")) { int numColumns = -1; try { numColumns = Integer.parseInt(appearance.substring(appearance.indexOf('-') + 1)); } catch (Exception e) { // Do nothing, leave numColumns as -1 Log.e("WidgetFactory", "Exception parsing numColumns"); } return new GridMultiWidget(context, fep, numColumns); } else if (appearance != null && appearance.equals("minimal")) { return new SpinnerMultiWidget(context, fep); } else if (appearance != null && appearance.equals("list")) { return new ListMultiWidget(context, fep, true); } else if (appearance != null && appearance.equals("list-nolabel")) { return new ListMultiWidget(context, fep, false); } else if (appearance != null && appearance.equals("label")) { return new LabelWidget(context, fep); } else { return new SelectMultiWidget(context, fep); } } }