package com.radicaldynamic.gcmobile.android.build; import java.util.Iterator; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.text.InputFilter; import android.text.Spanned; import android.text.method.QwertyKeyListener; import android.text.method.TextKeyListener; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.radicaldynamic.groupinform.R; import com.radicaldynamic.groupinform.adapters.FormBuilderSelectItemListAdapter; import com.radicaldynamic.groupinform.application.Collect; import com.radicaldynamic.groupinform.views.TouchListView; import com.radicaldynamic.groupinform.xform.Field; public class SelectFieldList extends ListActivity { @SuppressWarnings("unused") private static final String t = "SelectFieldList: "; private static final int MENU_ADD = Menu.FIRST; private static final int MENU_IMPORT = Menu.FIRST + 1; private static final int DIALOG_EDIT_ITEM = 1; private static final int RESULT_TRANSLATIONS = 1; private static final int RESULT_IMPORT = 2; public static final String KEY_SINGLE = "singleselect"; public static final String KEY_DEFAULT = "instancedefault"; private FormBuilderSelectItemListAdapter mAdapter; private Builder mAlertDialog; private View mAlertDialogView; private Field mItem; // Currently selected item to be added/edited private boolean mSingleSelect; // Whether this is a single select field (see KEY_SINGLE) private TouchListView.DropListener onDrop = new TouchListView.DropListener() { @Override public void drop(int from, int to) { Field item = mAdapter.getItem(from); mAdapter.remove(item); mAdapter.insert(item, to); } }; private TouchListView.RemoveListener onRemove = new TouchListView.RemoveListener() { @Override public void remove(int which) { final Field item = mAdapter.getItem(which); mAlertDialog = new AlertDialog.Builder(SelectFieldList.this); mAlertDialog.setCancelable(false); mAlertDialog.setIcon(R.drawable.ic_dialog_alert); mAlertDialog.setTitle(R.string.tf_confirm_removal); mAlertDialog.setMessage(getString(R.string.tf_confirm_removal_msg, item.getLabel().toString())); mAlertDialog.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { mAdapter.remove(item); Toast.makeText( getApplicationContext(), getString(R.string.tf_removed_with_param, item.getLabel().toString()), Toast.LENGTH_SHORT).show(); // Trigger a refresh of the view (and display any pertenent messages) if (mAdapter.isEmpty()) refreshView(); } }); mAlertDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); mAlertDialog.show(); } }; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_CANCELED) return; switch (requestCode) { case RESULT_IMPORT: refreshView(); break; case RESULT_TRANSLATIONS: // Reflect any changes to the label translation in the dialog EditText label = (EditText) mAlertDialogView.findViewById(R.id.label); label.setText(mItem.getLabel().toString()); if (mItem.getLabel().isTranslated()) toggleEditText(label, false); else toggleEditText(label, true); break; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle(getString(R.string.app_name) + " > " + getString(R.string.tf_list_items)); setContentView(R.layout.fb_main); // Remove things from this view that are not needed findViewById(R.id.pathText).setVisibility(View.GONE); findViewById(R.id.jumpPreviousButton).setVisibility(View.GONE); if (savedInstanceState == null) { Intent intent = getIntent(); if (intent != null) { // Determine if this is a single or multiple select type (see key comments) mSingleSelect = intent.getBooleanExtra(KEY_SINGLE, false); // Set preselected states for list items Iterator<Field> it = Collect.getInstance().getFormBuilderState().getField().getChildren().iterator(); while (it.hasNext()) { Field i = it.next(); // Default to "off" i.setItemDefault(false); /* * Items must be explicitly enabled/disabled because * the defaults may have changed since last loading */ for (String def : intent.getStringExtra(KEY_DEFAULT).split("\\s+")) { if (i.getItemValue().equals(def)) i.setItemDefault(true); } } } } else { mSingleSelect = savedInstanceState.getBoolean(KEY_SINGLE); Object data = getLastNonConfigurationInstance(); if (data instanceof Field) { mItem = (Field) data; } } } public Dialog onCreateDialog(int id) { mAlertDialog = new AlertDialog.Builder(this); switch (id) { case DIALOG_EDIT_ITEM: mAlertDialog.setInverseBackgroundForced(true); mAlertDialog.setCancelable(false); // Attach the layout to this mAlertDialog LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); mAlertDialogView = inflater.inflate(R.layout.fb_item_dialog, null); final EditText label = (EditText) mAlertDialogView.findViewById(R.id.label); final Button labelI18n = (Button) mAlertDialogView.findViewById(R.id.labelI18n); final EditText value = (EditText) mAlertDialogView.findViewById(R.id.value); final CheckBox preselected = (CheckBox) mAlertDialogView.findViewById(R.id.preselected); // Labels default to upper case characters at the beginning label.setKeyListener(new QwertyKeyListener(TextKeyListener.Capitalize.SENTENCES, false)); // Access translations for label & hints labelI18n.setOnClickListener(new OnClickListener() { public void onClick(View v) { // For use by translation activity Collect.getInstance().getFormBuilderState().setItem(mItem); // Forward to translation screen Intent i = new Intent(SelectFieldList.this, I18nList.class); i.putExtra(I18nList.KEY_FIELDTEXT_TYPE, I18nList.KEY_ITEM_LABEL); i.putExtra(I18nList.KEY_TRANSLATION_ID, mItem.getLabel().getRef()); startActivityForResult(i, RESULT_TRANSLATIONS); } }); // Prevent spaces from being entered into the value field value.setFilters(new InputFilter[] { new NoSpaces() }); /* * If the user moves to input a value and one is not already provided then * attempt to prepopulate this field by using the label as a source and * removing all spaces & other special characters. */ value.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus == false) return; if (value.getText().toString().length() == 0 && label.getText().toString().length() > 0) value.setText(label.getText().toString().replaceAll("[^a-zA-Z0-9_-]", "")); } }); if (mItem.isEmpty()) { mAlertDialog.setTitle(getText(R.string.tf_create_list_item)); } else { mAlertDialog.setTitle(getText(R.string.tf_edit_list_item)); label.setText(mItem.getLabel().toString()); value.setText(mItem.getItemValue()); // Translated labels should not be accessible if (mItem.getLabel().isTranslated()) toggleEditText(label, false); else toggleEditText(label, true); if (mItem.isItemDefault()) preselected.setChecked(true); } mAlertDialog.setView(mAlertDialogView); mAlertDialog.setPositiveButton(getText(R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface mAlertDialog, int whichButton) { boolean error = false; String labelText = label.getText().toString().trim(); String valueText = value.getText().toString().trim(); // Both label and value are required if ((!mItem.getLabel().isTranslated() && labelText.length() == 0) || valueText.length() == 0) { Toast.makeText(getApplicationContext(), getString(R.string.tf_item_missing_label_or_value), Toast.LENGTH_LONG).show(); // Preserve user input if (!mItem.getLabel().isTranslated()) mItem.setLabel(labelText); mItem.setItemValue(valueText); mItem.setItemDefault(preselected.isChecked()); error = true; } // Ensure uniqueness of value Iterator<Field> it = Collect.getInstance().getFormBuilderState().getField().getChildren().iterator(); // User input not preserved here (doing so makes the routine much more complex) while (it.hasNext()) { Field i = it.next(); if (i.getItemValue().equals(value.getText().toString()) && !i.getLabel().toString().equals(mItem.getLabel().toString())) { Toast.makeText(getApplicationContext(), getString(R.string.tf_item_value_not_unique), Toast.LENGTH_LONG).show(); error = true; } } if (error) { removeDialog(DIALOG_EDIT_ITEM); showDialog(DIALOG_EDIT_ITEM); } else { if (!mItem.getLabel().isTranslated()) mItem.setLabel(labelText); mItem.setItemValue(valueText); // Special handling for the preselected checkbox (this is sensitive to the select type) if (preselected.isChecked()) { // Ensure that no other item is selected if (mSingleSelect) { Iterator<Field> itemIterator = Collect.getInstance().getFormBuilderState().getField().getChildren().iterator(); while (itemIterator.hasNext()) itemIterator.next().setItemDefault(false); } mItem.setItemDefault(true); } else { mItem.setItemDefault(false); } // Add new items to the list if (mItem.isEmpty()) { mItem.setEmpty(false); Collect.getInstance().getFormBuilderState().getField().getChildren().add(mItem); } removeDialog(DIALOG_EDIT_ITEM); refreshView(); } } }); mAlertDialog.setNegativeButton(getText(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { removeDialog(DIALOG_EDIT_ITEM); } }); break; } return mAlertDialog.show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0, MENU_ADD, 0, getString(R.string.tf_create_list_item)).setIcon(R.drawable.ic_menu_add); menu.add(0, MENU_IMPORT, 0, getString(R.string.tf_import_list)).setIcon(R.drawable.ic_menu_attachment); return true; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: setResult(RESULT_OK); finish(); return true; } return super.onKeyDown(keyCode, event); } @Override protected void onListItemClick(ListView listView, View view, int position, long id) { mItem = (Field) getListAdapter().getItem(position); showDialog(DIALOG_EDIT_ITEM); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ADD: mItem = new Field(); mItem.setType("item"); showDialog(DIALOG_EDIT_ITEM); return true; case MENU_IMPORT: Intent intent = new Intent(this, SelectFieldImportActivity.class); startActivityForResult(intent, RESULT_IMPORT); break; } return super.onOptionsItemSelected(item); } @Override public void onPause() { super.onPause(); // TODO? } @Override protected void onResume() { super.onResume(); refreshView(); } @Override public Object onRetainNonConfigurationInstance() { return mItem; } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(KEY_SINGLE, mSingleSelect); } private static class NoSpaces implements InputFilter { @Override public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { return source.subSequence(start, end).toString().replaceAll("\\s+", ""); } } private void toggleEditText(EditText v, Boolean b) { v.setEnabled(b); v.setFocusable(b); v.setFocusableInTouchMode(b); } /* * Refresh the view (displaying the currently active field * or the top level of the form if no field is currently active) */ private void refreshView() { mAdapter = new FormBuilderSelectItemListAdapter(getApplicationContext(), Collect.getInstance().getFormBuilderState().getField().getChildren()); // Provide a hint to users if the list is empty if (mAdapter.isEmpty()) { TextView nothingToDisplay = (TextView) findViewById(R.id.nothingToDisplay); nothingToDisplay.setVisibility(View.VISIBLE); } else { TextView nothingToDisplay = (TextView) findViewById(R.id.nothingToDisplay); nothingToDisplay.setVisibility(View.INVISIBLE); } setListAdapter(mAdapter); TouchListView tlv = (TouchListView) getListView(); tlv.setDropListener(onDrop); tlv.setRemoveListener(onRemove); } }