package com.radicaldynamic.gcmobile.android.build;
import java.util.ArrayList;
import java.util.Iterator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.method.DigitsKeyListener;
import android.text.method.QwertyKeyListener;
import android.text.method.TextKeyListener;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.radicaldynamic.groupinform.R;
import com.radicaldynamic.groupinform.application.Collect;
import com.radicaldynamic.groupinform.xform.Field;
import com.radicaldynamic.groupinform.xform.XForm;
public class FieldEditorActivity extends Activity
{
private static final String t = "FormBuilderElementEditor: ";
public static final String KEY_FIELDTYPE = "fieldtype";
public static final String KEY_SELECTDEFAULT = "selectinstancedefault";
private static final int REQUEST_ITEMLIST = 1;
private static final int REQUEST_TRANSLATIONS = 2;
private static final int MENU_ADVANCED = Menu.FIRST;
private static final int MENU_ITEMS = Menu.FIRST + 1;
private static final int MENU_HELP = Menu.FIRST + 2;
private AlertDialog mAlertDialog;
private Field mField = null;
private String mFieldType = null;
// Header
private TextView mHeaderType;
private ImageView mHeaderIcon;
// Common input elements
private EditText mLabel;
private Button mLabelI18n;
private EditText mHint;
private Button mHintI18n;
private EditText mDefaultValue;
private CheckBox mReadonly;
private CheckBox mRequired;
// Special hack to deal with the added complexity of select fields
private String mSelectInstanceDefault = "";
private ArrayAdapter<CharSequence> mSelectAppearanceSingleOptions;
private ArrayAdapter<CharSequence> mSelectAppearanceMultipleOptions;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.fb_field_editor);
// Retrieve field (if any)
mField = Collect.getInstance().getFormBuilderState().getField();
// Create a new field if one is needed (further init will occur in the field-specific method)
if (mField == null)
mField = new Field();
// If there is no instance state (e.g., this activity was loaded by another/this is not a flip)
if (savedInstanceState == null) {
Intent i = getIntent();
mFieldType = i.getStringExtra(KEY_FIELDTYPE);
// We store off the instance default string for select types (this is special)
if (mFieldType.equals("select"))
mSelectInstanceDefault = mField.getInstance().getDefaultValue();
} else {
if (savedInstanceState.containsKey(KEY_FIELDTYPE))
mFieldType = savedInstanceState.getString(KEY_FIELDTYPE);
if (savedInstanceState.containsKey(KEY_SELECTDEFAULT))
mSelectInstanceDefault = savedInstanceState.getString(KEY_SELECTDEFAULT);
}
// Set up header
mHeaderType = (TextView) findViewById(R.id.headerType);
mHeaderIcon = (ImageView) findViewById(R.id.headerIcon);
// Get a handle on common input elements
mLabel = (EditText) findViewById(R.id.label);
mLabelI18n = (Button) findViewById(R.id.labelI18n);
mHint = (EditText) findViewById(R.id.hint);
mHintI18n = (Button) findViewById(R.id.hintI18n);
mDefaultValue = (EditText) findViewById(R.id.defaultValue);
mReadonly = (CheckBox) findViewById(R.id.readonly);
mRequired = (CheckBox) findViewById(R.id.required);
// New strings in either the label or hint should begin with a capital by default
mLabel.setKeyListener(new QwertyKeyListener(TextKeyListener.Capitalize.SENTENCES, false));
mHint.setKeyListener(new QwertyKeyListener(TextKeyListener.Capitalize.SENTENCES, false));
// Access translations for label & hints
mLabelI18n.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(FieldEditorActivity.this, I18nList.class);
i.putExtra(I18nList.KEY_FIELDTEXT_TYPE, I18nList.KEY_LABEL);
i.putExtra(I18nList.KEY_TRANSLATION_ID, mField.getLabel().getRef());
startActivityForResult(i, REQUEST_TRANSLATIONS);
}
});
mHintI18n.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(new Intent(FieldEditorActivity.this, I18nList.class));
i.putExtra(I18nList.KEY_FIELDTEXT_TYPE, I18nList.KEY_DESCRIPTION);
i.putExtra(I18nList.KEY_TRANSLATION_ID, mField.getHint().getRef());
startActivityForResult(i, REQUEST_TRANSLATIONS);
}
});
// Set up listener to detect changes to read-only input element
mReadonly.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (((CheckBox) v).isChecked())
mRequired.setChecked(false);
}
});
// Set up listener to detect changes to required input element
mRequired.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (((CheckBox) v).isChecked())
mReadonly.setChecked(false);
}
});
// Prepare adapters for select appearance options
mSelectAppearanceSingleOptions =
ArrayAdapter.createFromResource(this, R.array.tf_select_appearance_single_options, android.R.layout.simple_spinner_item);
mSelectAppearanceSingleOptions.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSelectAppearanceMultipleOptions =
ArrayAdapter.createFromResource(this, R.array.tf_select_appearance_multiple_options, android.R.layout.simple_spinner_item);
mSelectAppearanceMultipleOptions.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
if (mFieldType.equals("barcode")) loadBarcodeElement();
else if (mFieldType.equals("date")) loadDateElement();
else if (mFieldType.equals("dateTime")) loadDateElement();
else if (mFieldType.equals("draw")) loadDrawElement(); // Note: draw is a virtual type - it will be turned into media once created
else if (mFieldType.equals("geopoint")) loadGeopointElement();
else if (mFieldType.equals("group")) loadGroupElement();
else if (mFieldType.equals("media")
&& mField.getAttributes().containsKey(XForm.Attribute.MEDIA_TYPE)
&& mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).contains("draw"))
loadDrawElement();
else if (mFieldType.equals("media")) loadMediaElement();
else if (mFieldType.equals("number")) loadNumberElement();
else if (mFieldType.equals("select")) loadSelectElement();
else if (mFieldType.equals("time")) loadDateElement();
else if (mFieldType.equals("text")) loadTextElement();
else {
Toast.makeText(getApplicationContext(), getString(R.string.tf_unable_to_edit_unknown_field_type), Toast.LENGTH_LONG).show();
if (Collect.Log.WARN) Log.w(Collect.LOGTAG, t + "unhandled field type");
finish();
}
/*
* Set header icon
*
* TODO: figure out a better way to do with without duplicating code from FormBuilderFieldListAdapter
*/
if (mField.getType().equals("group")) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_group));
} else if (mField.getType().equals("input")) {
Drawable icon = getDrawable(R.drawable.element_string);
try {
String specificType = mField.getBind().getType();
if (specificType.equals("barcode")) icon = getDrawable(R.drawable.element_barcode); else
if (specificType.equals("date")) icon = getDrawable(R.drawable.element_calendar); else
if (specificType.equals("dateTime")) icon = getDrawable(R.drawable.element_calendar); else
if (specificType.equals("decimal")) icon = getDrawable(R.drawable.element_number); else
if (specificType.equals("geopoint")) icon = getDrawable(R.drawable.element_location); else
if (specificType.equals("int")) icon = getDrawable(R.drawable.element_number); else
if (specificType.equals("time")) icon = getDrawable(R.drawable.element_calendar);
} catch (NullPointerException e){
// TODO: is this really a problem?
} finally {
mHeaderIcon.setImageDrawable(icon);
}
} else if (mField.getType().equals("repeat")) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_group));
} else if (mField.getType().equals("select")) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_selectmulti));
} else if (mField.getType().equals("select1")) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_selectsingle));
} else if (mField.getType().equals("upload")) {
if (mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).contains("draw"))
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_draw));
else
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_media));
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (requestCode) {
case REQUEST_ITEMLIST:
if (resultCode == RESULT_OK) {
// Preselected list items may have changed, store off this list
ArrayList<String> defaults = new ArrayList<String>();
Iterator<Field> it = Collect.getInstance().getFormBuilderState().getField().getChildren().iterator();
while (it.hasNext()) {
Field item = it.next();
// If an item is marked as a default (preselected)
if (item.isItemDefault()) {
defaults.add(item.getItemValue());
item.setItemDefault(false);
}
}
/*
* This will either be processed by saveSelectElement() or sent back to
* FormBuilderSelectItemList by way of onOptionsItemSelected()
*/
mSelectInstanceDefault = defaults.toString().replaceAll(",\\s", " ").replaceAll("[\\[\\]]", "");
}
break;
// User may have adjusted translations or removed them altogether, refresh accordingly
case REQUEST_TRANSLATIONS:
if (resultCode == RESULT_OK) {
if (mField.getLabel().isTranslated())
toggleEditText(mLabel, false);
else
toggleEditText(mLabel, true);
if (mField.getHint().isTranslated())
toggleEditText(mHint, false);
else
toggleEditText(mHint, true);
mLabel.setText(mField.getLabel().toString());
mHint.setText(mField.getHint().toString());
}
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_ITEMS, 0, getString(R.string.tf_list_items))
.setIcon(R.drawable.ic_menu_mark)
.setEnabled(mFieldType.equals("select") ? true : false);
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
// Save and exit
if (saveChanges()) {
setResult(RESULT_OK);
finish();
} else {
Toast.makeText(getApplicationContext(), getString(R.string.tf_unable_to_save_unknown_field_type), Toast.LENGTH_LONG).show();
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId()) {
// TODO: launch the advanced properties editor
case MENU_ADVANCED:
break;
// Launch the form builder select item editor
case MENU_ITEMS:
Intent i = new Intent(this, SelectFieldList.class);
/*
* Use the state of the select radio option to determine whether to indicate to
* the select item list which mode the select list is operating in.
*
* This makes sense because the user may have switched select modes but may
* not have saved the field yet, so we cannot determine this from the field itself.
*/
final CheckBox optionMultiple = (CheckBox) findViewById(R.id.selectFieldMultiple);
i.putExtra(SelectFieldList.KEY_SINGLE, !optionMultiple.isChecked());
i.putExtra(SelectFieldList.KEY_DEFAULT, mSelectInstanceDefault);
startActivityForResult(i, REQUEST_ITEMLIST);
break;
// TODO: display field-specific help text
case MENU_HELP:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString(KEY_FIELDTYPE, mFieldType);
outState.putString(KEY_SELECTDEFAULT, mSelectInstanceDefault);
// Save this specific field state for orientation changes & select item editor
Collect.getInstance().getFormBuilderState().setField(mField);
}
// See loadSelectElement() for further information on this dialog
private void createSelectChangeDialog()
{
mAlertDialog = new AlertDialog.Builder(this)
.setCancelable(false)
.setIcon(R.drawable.ic_dialog_alert)
.setTitle(R.string.tf_change_select_type)
.setMessage(R.string.tf_change_select_type_msg)
.setPositiveButton(R.string.tf_yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_selectsingle));
Spinner optionAppearance = (Spinner) findViewById(R.id.selectFieldAppearance);
optionAppearance.setAdapter(mSelectAppearanceSingleOptions);
mSelectInstanceDefault = "";
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Return to a multiple select field type
CheckBox optionMultiple = (CheckBox) findViewById(R.id.selectFieldMultiple);
optionMultiple.setChecked(true);
dialog.cancel();
}
}).create();
mAlertDialog.show();
}
/*
* The field editor layout file includes all possible input elements for all field types.
* Since this will not make sense to the end user our approach is to simply hide unneeded
* fields based on the human field type that was passed to this activity when it was started.
*
* This method exists to make the hiding of input elements easier.
*/
private void disableFormComponent(int componentResource)
{
ViewGroup component = (ViewGroup) findViewById(componentResource);
component.setVisibility(View.GONE);
}
// Convenience method
private Drawable getDrawable(int image)
{
return getResources().getDrawable(image);
}
/*
* Initialize input elements that are likely to appear for all field types.
* This should be done AFTER any primary initialization of newly created fields.
*/
private void loadCommonAttributes()
{
if (mField.getLabel().isTranslated())
toggleEditText(mLabel, false);
if (mField.getHint().isTranslated())
toggleEditText(mHint, false);
mLabel.setText(mField.getLabel().toString());
mHint.setText(mField.getHint().toString());
mDefaultValue.setText(mField.getInstance().getDefaultValue());
if (mField.getBind().isReadonly())
mReadonly.setChecked(true);
if (mField.getBind().isRequired())
mRequired.setChecked(true);
}
private void loadBarcodeElement()
{
updateTitle(getString(R.string.tf_element_barcode));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("input");
mField.getBind().setType(mFieldType);
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
disableFormComponent(R.id.readonlyLayout);
}
private void loadDateElement()
{
updateTitle(getString(R.string.tf_element_date));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("input");
mField.getBind().setType(mFieldType);
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
final RadioButton dateOnly = (RadioButton) findViewById(R.id.dateTypeDateOnly);
final RadioButton timeOnly = (RadioButton) findViewById(R.id.dateTypeTimeOnly);
final RadioButton dateAndTime = (RadioButton) findViewById(R.id.dateTypeDateAndTime);
if (mField.getBind().getType().equals("date"))
dateOnly.setChecked(true);
else if (mField.getBind().getType().equals("time"))
timeOnly.setChecked(true);
else if (mField.getBind().getType().equals("dateTime"))
dateAndTime.setChecked(true);
// TODO: we should probably display a "default" date using the date widget
}
private void loadDrawElement()
{
updateTitle(getString(R.string.tf_element_draw));
// "draw" is really a special kind of media field for which we want a different interface
mFieldType = "media";
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("upload");
mField.getAttributes().put(XForm.Attribute.MEDIA_TYPE, "draw/*");
mField.getBind().setType("binary");
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.defaultValueInput);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
// final RadioButton drawAnnotate = (RadioButton) findViewById(R.id.drawTypeAnnotate);
final RadioButton drawSignature = (RadioButton) findViewById(R.id.drawTypeSignature);
final RadioButton drawSketch = (RadioButton) findViewById(R.id.drawTypeSketch);
if (mField.getAttributes().get(XForm.Attribute.APPEARANCE) == null) {
drawSketch.setChecked(true);
} else {
if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals("annotate")) {
// drawAnnotate.setChecked(true);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals("signature")) {
drawSignature.setChecked(true);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals("sketch")) {
drawSketch.setChecked(true);
}
}
}
private void loadGeopointElement()
{
updateTitle(getString(R.string.tf_element_geopoint));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("input");
mField.getBind().setType(mFieldType);
mField.setEmpty(false);
}
if (mField.getAttributes().containsKey(XForm.Attribute.APPEARANCE) &&
mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.MAP)) {
CheckBox mapsOption = (CheckBox) findViewById(R.id.geopointMaps);
mapsOption.setChecked(true);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.defaultValueInput);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
disableFormComponent(R.id.readonlyLayout);
}
private void loadGroupElement()
{
updateTitle(getString(R.string.tf_element_group));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("group");
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.defaultValueInput);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.hintInput);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
disableFormComponent(R.id.readonlyLayout);
disableFormComponent(R.id.requiredLayout);
final RadioButton groupRegular = (RadioButton) findViewById(R.id.groupTypeRegular);
final RadioButton groupRepeated = (RadioButton) findViewById(R.id.groupTypeRepeated);
final RadioButton groupScreen = (RadioButton) findViewById(R.id.groupTypeScreen);
// Initialize group type selection
if (Field.isRepeatedGroup(mField)) {
groupRepeated.setChecked(true);
} else {
if (mField.getAttributes().containsKey(XForm.Attribute.APPEARANCE) &&
mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.FIELD_LIST))
{
groupScreen.setChecked(true);
} else {
groupRegular.setChecked(true);
}
}
}
private void loadMediaElement()
{
updateTitle(getString(R.string.tf_element_media));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("upload");
mField.getBind().setType("binary");
mField.getAttributes().put(XForm.Attribute.MEDIA_TYPE, "image/*");
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.defaultValueInput);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
disableFormComponent(R.id.readonlyLayout);
// Set up listener for radio buttons so that they influence the field type
OnClickListener radioListener = new OnClickListener() {
public void onClick(View v) {
RadioButton rb = (RadioButton) v;
switch (rb.getId()) {
case R.id.mediaTypeAudio: break;
case R.id.mediaTypeImage: break;
case R.id.mediaTypeVideo: break;
}
}
};
final RadioButton radioAudio = (RadioButton) findViewById(R.id.mediaTypeAudio);
final RadioButton radioImage = (RadioButton) findViewById(R.id.mediaTypeImage);
final RadioButton radioVideo = (RadioButton) findViewById(R.id.mediaTypeVideo);
radioAudio.setOnClickListener(radioListener);
radioImage.setOnClickListener(radioListener);
radioVideo.setOnClickListener(radioListener);
// Initialize media type selection
if (mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).equals("audio/*"))
radioAudio.setChecked(true);
else if (mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).equals("image/*"))
radioImage.setChecked(true);
else if (mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).equals("video/*"))
radioVideo.setChecked(true);
}
private void loadNumberElement()
{
updateTitle(getString(R.string.tf_element_number));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("input");
mField.getBind().setType("int");
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
final EditText defaultValue = (EditText) findViewById(R.id.defaultValue);
// Set up listener for radio buttons so that they influence the field type
OnClickListener radioListener = new OnClickListener() {
public void onClick(View v) {
RadioButton rb = (RadioButton) v;
switch (rb.getId()) {
case R.id.numberTypeInteger:
defaultValue.setKeyListener(new DigitsKeyListener(false, false));
// Remove any occurrences of a decimal
if (defaultValue.getText().toString().contains(".")) {
String txt = defaultValue.getText().toString();
defaultValue.setText(txt.replace(".", ""));
}
break;
case R.id.numberTypeDecimal:
defaultValue.setKeyListener(new DigitsKeyListener(false, true));
break;
}
}
};
final RadioButton radioInteger = (RadioButton) findViewById(R.id.numberTypeInteger);
final RadioButton radioDecimal = (RadioButton) findViewById(R.id.numberTypeDecimal);
radioInteger.setOnClickListener(radioListener);
radioDecimal.setOnClickListener(radioListener);
// Initialize number type selection
if (mField.getBind().getType().equals("int")) {
radioInteger.setChecked(true);
// false, false supports only integer input
defaultValue.setKeyListener(new DigitsKeyListener(false, false));
} else {
radioDecimal.setChecked(true);
// false, true supports decimal input
defaultValue.setKeyListener(new DigitsKeyListener(false, true));
}
}
private void loadSelectElement()
{
updateTitle(getString(R.string.tf_element_select));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType(mFieldType);
mField.getBind().setType(mFieldType);
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.defaultValueInput);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.readonlyLayout);
final CheckBox optionMultiple = (CheckBox) findViewById(R.id.selectFieldMultiple);
final Spinner optionAppearance = (Spinner) findViewById(R.id.selectFieldAppearance);
// Set up listener to detect changes to read-only input element
optionMultiple.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (((CheckBox) v).isChecked()) {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_selectmulti));
optionAppearance.setAdapter(mSelectAppearanceMultipleOptions);
} else {
/*
* Single selects may only have one preselected default. This presents a problem
* if the user is switching from a multiple to a single default and requires user
* intervention.
*/
if (mSelectInstanceDefault.split("\\s+").length > 1) {
createSelectChangeDialog();
} else {
mHeaderIcon.setImageDrawable(getDrawable(R.drawable.element_selectsingle));
optionAppearance.setAdapter(mSelectAppearanceSingleOptions);
}
}
}
});
// Initialize select type
if (mField.getType().equals("select")) {
optionMultiple.setChecked(true);
optionAppearance.setAdapter(mSelectAppearanceMultipleOptions);
} else {
optionMultiple.setChecked(false);
optionAppearance.setAdapter(mSelectAppearanceSingleOptions);
}
// Default appearance
optionAppearance.setSelection(0);
// Switch to selected appearance option
if (mField.getAttributes().get(XForm.Attribute.APPEARANCE) != null) {
if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.MINIMAL)) {
optionAppearance.setSelection(1);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.LIST)) {
optionAppearance.setSelection(2);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.LIST_NOLABEL)) {
optionAppearance.setSelection(3);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.QUICK)) {
optionAppearance.setSelection(4);
} else if (mField.getAttributes().get(XForm.Attribute.APPEARANCE).equals(XForm.Value.AUTOCOMPLETE)) {
optionAppearance.setSelection(5);
}
}
}
private void loadTextElement()
{
updateTitle(getString(R.string.tf_element_text));
// Further initialize newly created fields
if (mField.isEmpty()) {
mField.setType("input");
mField.getBind().setType("string");
mField.setEmpty(false);
}
loadCommonAttributes();
disableFormComponent(R.id.dateFieldTypeSelection);
disableFormComponent(R.id.drawFieldTypeSelection);
disableFormComponent(R.id.geopointFieldTypeSelection);
disableFormComponent(R.id.groupFieldTypeSelection);
disableFormComponent(R.id.mediaFieldTypeSelection);
disableFormComponent(R.id.numberFieldTypeSelection);
disableFormComponent(R.id.selectFieldTypeSelection);
}
/*
* Save any changes that the user has made to the form field
*/
private boolean saveChanges()
{
// Ensure that a label is present
if (mLabel.getText().toString().replaceAll("\\s+", "").length() == 0) {
mLabel.setText("No label for " + mFieldType);
}
// Save common attributes
if (mField.getLabel().isTranslated()) {
// Clear out any value that may have been stored
mField.getLabel().setValue(null);
} else {
mField.setLabel(mLabel.getText().toString().trim());
}
if (mField.getHint().isTranslated()) {
// Clear out any value that may have been stored
mField.getHint().setValue(null);
} else {
mField.setHint(mHint.getText().toString().trim());
}
mField.getInstance().setDefaultValue(mDefaultValue.getText().toString().trim());
if (mReadonly.isChecked())
mField.getBind().setReadonly(true);
else
mField.getBind().setReadonly(false);
if (mRequired.isChecked())
mField.getBind().setRequired(true);
else
mField.getBind().setRequired(false);
// Save (control) field-specific properties
if (mFieldType.equals("barcode")) saveBarcodeElement();
else if (mFieldType.equals("date")) saveDateElement();
else if (mFieldType.equals("dateTime")) saveDateElement();
else if (mFieldType.equals("geopoint")) saveGeopointElement();
else if (mFieldType.equals("group")) saveGroupElement();
else if (mFieldType.equals("media") && mField.getAttributes().get(XForm.Attribute.MEDIA_TYPE).contains("draw"))
saveDrawElement();
else if (mFieldType.equals("media")) saveMediaElement();
else if (mFieldType.equals("number")) saveNumberElement();
else if (mFieldType.equals("select")) saveSelectElement();
else if (mFieldType.equals("text")) saveTextElement();
else if (mFieldType.equals("time")) saveDateElement();
else {
if (Collect.Log.WARN) Log.w(Collect.LOGTAG, t + "unhandled field type");
return false;
}
// Mark the field as having been saved
mField.setSaved(true);
Collect.getInstance().getFormBuilderState().setField(mField);
return true;
}
private void saveBarcodeElement()
{
}
private void saveDateElement()
{
final RadioButton dateOnly = (RadioButton) findViewById(R.id.dateTypeDateOnly);
final RadioButton timeOnly = (RadioButton) findViewById(R.id.dateTypeTimeOnly);
final RadioButton dateAndTime = (RadioButton) findViewById(R.id.dateTypeDateAndTime);
if (dateOnly.isChecked()) {
mField.getBind().setType("date");
} else if (timeOnly.isChecked()) {
mField.getBind().setType("time");
} else if (dateAndTime.isChecked()) {
mField.getBind().setType("dateTime");
}
}
private void saveDrawElement()
{
// final RadioButton drawAnnotate = (RadioButton) findViewById(R.id.drawTypeAnnotate);
final RadioButton drawSignature = (RadioButton) findViewById(R.id.drawTypeSignature);
final RadioButton drawSketch = (RadioButton) findViewById(R.id.drawTypeSketch);
if (drawSignature.isChecked()) {
mField.getAttributes().put(XForm.Attribute.APPEARANCE, "signature");
} else if (drawSketch.isChecked()) {
mField.getAttributes().put(XForm.Attribute.APPEARANCE, "sketch");
}
}
private void saveGeopointElement()
{
CheckBox mapsOption = (CheckBox) findViewById(R.id.geopointMaps);
if (mapsOption.isChecked()) {
mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.MAP);
} else {
mField.getAttributes().remove(XForm.Attribute.APPEARANCE);
}
}
/*
* FIXME: update children references after changing between group types
*/
private void saveGroupElement()
{
final RadioButton groupRegular = (RadioButton) findViewById(R.id.groupTypeRegular);
final RadioButton groupRepeated = (RadioButton) findViewById(R.id.groupTypeRepeated);
final RadioButton groupScreen = (RadioButton) findViewById(R.id.groupTypeScreen);
if (groupRegular.isChecked()) {
// Changing from a repeated group to a regular group involves work
if (Field.isRepeatedGroup(mField)) {
// Move any children of the repeated group to the regular group
mField.getChildren().addAll(mField.getRepeat().getChildren());
// Remove the repeat tag itself
if (mField.getChildren().size() > 0)
mField.getChildren().remove(0);
}
} else if (groupRepeated.isChecked()) {
// Changing from a regular group to a repeated group involves work
if (!Field.isRepeatedGroup(mField)) {
ArrayList<Field> regularGroupChildren = new ArrayList<Field>();
// Store off the children and remove them from the group
if (!mField.getChildren().isEmpty()) {
regularGroupChildren = mField.getChildren();
mField.getChildren().clear();
}
// Create the repeat field and add it to the group
Field repeat = new Field();
repeat.setType("repeat");
mField.getChildren().add(repeat);
mField.getRepeat().setParent(mField);
if (!regularGroupChildren.isEmpty())
mField.getRepeat().getChildren().addAll(regularGroupChildren);
}
}
if (groupScreen.isChecked()) {
mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.FIELD_LIST);
} else {
// Make sure the multiple questions per screen attribute is cleared
if (mField.getAttributes().containsKey(XForm.Attribute.APPEARANCE)) {
mField.getAttributes().remove(XForm.Attribute.APPEARANCE);
}
}
}
private void saveMediaElement()
{
final RadioButton radioAudio = (RadioButton) findViewById(R.id.mediaTypeAudio);
final RadioButton radioImage = (RadioButton) findViewById(R.id.mediaTypeImage);
final RadioButton radioVideo = (RadioButton) findViewById(R.id.mediaTypeVideo);
if (radioAudio.isChecked()) {
mField.getAttributes().put(XForm.Attribute.MEDIA_TYPE, "audio/*");
} else if (radioImage.isChecked()) {
mField.getAttributes().put(XForm.Attribute.MEDIA_TYPE, "image/*");
} else if (radioVideo.isChecked()) {
mField.getAttributes().put(XForm.Attribute.MEDIA_TYPE, "video/*");
}
}
private void saveNumberElement()
{
final RadioButton radioInteger = (RadioButton) findViewById(R.id.numberTypeInteger);
if (radioInteger.isChecked()) {
mField.getBind().setType("int");
} else {
mField.getBind().setType("decimal");
}
}
private void saveSelectElement()
{
final CheckBox optionMultiple = (CheckBox) findViewById(R.id.selectFieldMultiple);
final Spinner optionAppearance = (Spinner) findViewById(R.id.selectFieldAppearance);
if (optionMultiple.isChecked()) {
mField.setType("select");
mField.getBind().setType("select");
} else {
mField.setType("select1");
mField.getBind().setType("select1");
}
switch (optionAppearance.getSelectedItemPosition()) {
case 0: mField.getAttributes().remove(XForm.Attribute.APPEARANCE); break;
case 1: mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.MINIMAL); break;
case 2: mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.LIST); break;
case 3: mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.LIST_NOLABEL); break;
case 4: mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.QUICK); break;
case 5: mField.getAttributes().put(XForm.Attribute.APPEARANCE, XForm.Value.AUTOCOMPLETE); break;
}
mField.getInstance().setDefaultValue(mSelectInstanceDefault);
}
private void saveTextElement()
{
}
private void toggleEditText(EditText v, Boolean b)
{
v.setEnabled(b);
v.setFocusable(b);
v.setFocusableInTouchMode(b);
}
private void updateTitle(String title)
{
mHeaderType.setText(title);
if (mField.isNewField())
setTitle(getString(R.string.tf_add_new) + " " + title);
else
setTitle(getString(R.string.tf_edit) + " " + title);
}
}