package com.applang; import static com.applang.Util.*; import static com.applang.Util1.*; import static com.applang.Util2.*; import static com.applang.VelocityUtil.*; import com.applang.berichtsheft.R; import java.util.Arrays; import java.util.Collection; import java.util.Locale; import java.util.regex.MatchResult; import org.json.JSONObject; import android.annotation.SuppressLint; import android.app.Dialog; import android.os.AsyncTask; import android.text.Editable; import android.text.TextWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; public class ConstructDialogs extends Dialogs { public static final String CONSTRUCT_ACTION = "com.applang.action.CONSTRUCT"; public static final int DIALOG_CONSTRUCT = 11; private static final String LAYOUT_ID = "id"; private static final String FIELD_ID = "id_2"; private static final String LABEL_ID = "id_1"; private static final String TEXT = "text"; private static final String COMMENT = "comment"; private static final String OPTIONAL = "optional"; private static final String ARGUMENT_TYPE = "type"; private static final String ARGUMENT = "argument"; private static final String SECOND_FIELD = "second"; private LinearLayout contentView; private ScrollView scrollView; private String[] defaults; @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_CONSTRUCT: defaults = defaultValues.toArray(new String[0]); mDialog = new Dialog(this) { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK)) { return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK)) { return true; } return super.onKeyUp(keyCode, event); } }; mDialog.setTitle(prompt); contentView = contentPane(); contentView.addView(header()); contentView.addView(scrollView = form()); mDialog.setContentView(contentView); makeSuggestions(); validate(); return mDialog; } return null; } private View focused = null; private LinearLayout contentPane(Integer... params) { LinearLayout vertLayout = Util1.linearLayout(this, LinearLayout.VERTICAL, param(LayoutParams.FILL_PARENT, 0, params), param(LayoutParams.FILL_PARENT, 1, params)); return vertLayout; } private boolean isExtensible() { return values.length > 0 && UNKNOWN.equals(values[values.length - 1]); } private RelativeLayout header() { // RelativeLayout relLayout = relativeLayout(this, // LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); RelativeLayout relLayout = (RelativeLayout) LayoutInflater.from(this) .inflate(isExtensible() ? R.layout.construct_form_header2 : R.layout.construct_form_header, null); // ImageButton im = new ImageButton(this); // im.setId(R.id.button4); // im.setImageResource(R.drawable.dropdown); // layoutParams = // relativeLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, // halfMargin, halfMargin, halfMargin, halfMargin); // layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); // layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); // relLayout.addView(im, layoutParams); ImageButton im = (ImageButton) relLayout.findViewById(R.id.button1); im.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (focused != null) { if (focused instanceof AutoCompleteTextView) { AutoCompleteTextView actv = (AutoCompleteTextView) focused; if (actv.isPopupShowing()) actv.dismissDropDown(); else actv.showDropDown(); } else focused.requestFocus(); } } }); // Button btn = new Button(this); // btn.setId(R.id.ok); // btn.setText(android.R.string.ok); // btn.setPadding(halfpadding, 0, halfpadding, 0); // layoutParams = // relativeLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, // halfMargin, halfMargin, halfMargin, halfMargin); // layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); // layoutParams.addRule(RelativeLayout.LEFT_OF, R.id.cancel); // relLayout.addView(btn, layoutParams); Button btn = (Button) relLayout.findViewById(R.id.ok); btn.setEnabled(false); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (formValidate(true)) { Object result = formEvaluate(); mDialog.dismiss(); _finish(RESULT_OK, result); } } }); // btn = new Button(this); // btn.setId(R.id.cancel); // btn.setText(android.R.string.cancel); // layoutParams = // relativeLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, // halfMargin, halfMargin, halfMargin, halfMargin); // layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); // layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); // relLayout.addView(btn, layoutParams); btn = (Button) relLayout.findViewById(R.id.cancel); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mDialog.cancel(); _finish(RESULT_CANCELED, null); } }); if (isExtensible()) { btn = (Button) relLayout.findViewById(R.id.button2); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ValMap map = (ValMap) fieldMaps.get(-1); String arg = (String) map.get(ARGUMENT); MatchResult m = argumentSuffix(arg); int index = toInt(1, m.group()); arg = arg.substring(0, m.start()) + (++index) + arg.substring(m.end()); if (addField(arg)) { index = fieldMaps.size(); makeSuggestions(index); enableMinus(); } } }); btn = (Button) relLayout.findViewById(R.id.button3); btn.setEnabled(false); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ValMap map = (ValMap) fieldMaps.get(-1); View layout = getLayout(map); if (layout != null) { ((ViewGroup)layout.getParent()).removeView(layout); fieldMaps.remove(map); enableMinus(); } } }); } return relLayout; } private void enableMinus() { Button minus = (Button) contentView.findViewById(R.id.button3); int size = fieldMaps.size(); int length = values.length; minus.setEnabled(size >= length); } private void enableOK(boolean enabled) { Button ok = (Button) contentView.findViewById(R.id.ok); ok.setEnabled(enabled); } private ScrollView form() { scrollView = new ScrollView(this); scrollView.setLayoutParams( new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); LinearLayout vertLayout = contentPane(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); vertLayout.setId(android.R.id.content); scrollView.addView(vertLayout); for (int i = 0; i < values.length; i++) { String argument = values[i]; if (!UNKNOWN.equals(argument)) addField(argument); } return scrollView; } private ValList fieldMaps = new ValList(); private boolean addField(String argument) { ValMap map = formLine(argument); boolean retval = map != null; if (retval) fieldMaps.add(map); return retval; } private ValMap formLine(String argument) { int index = fieldMaps.size(); ValMap map = new ValMap(); String type = argumentType(argument); boolean canDefault = Character.isLowerCase(type.charAt(0)); LinearLayout linearLayout = canDefault ? lineLayoutWithDefault(argument, index, map) : lineLayout(true, argument, index, map); LinearLayout vertLayout = (LinearLayout) scrollView.findViewById(android.R.id.content); vertLayout.addView(linearLayout); return map; } private LinearLayout lineLayoutWithDefault(String argument, final int index, ValMap map) { LinearLayout vertLayout = contentPane(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); LinearLayout horzLayout = lineLayout(true, argument, index, map); int id = horzLayout.getId(); horzLayout.setId(View.NO_ID); vertLayout.addView(horzLayout); vertLayout.setId(id); String type = argumentType(argument); MatchResult m = argumentSuffix(argument); int suffix = toInt(1, m.group()); argument = "default" + suffix + (isType(0, type) ? ARGUMENT_TYPER + DATA_TYPES[EXPR_TYPE_INDEX] : argument.substring(m.end())); ValMap map2 = new ValMap(); horzLayout = lineLayout(false, optionalize(argument), index, map2); map.put(SECOND_FIELD, map2); vertLayout.addView(horzLayout); return vertLayout; } private LinearLayout lineLayout(final boolean simple, String argument, final int index, ValMap map) { int id = 10 * (1 + index); final String type = argumentType(argument); boolean optional = isOptionalArgument(argument); // LinearLayout horzLayout = linearLayout(this, // LinearLayout.HORIZONTAL, // LayoutParams.FILL_PARENT, // LayoutParams.WRAP_CONTENT); // final TextView label = optional ? // new CheckBox(this) : // new TextView(this); LinearLayout horzLayout = (LinearLayout) LayoutInflater.from(this) .inflate(optional ? R.layout.construct_form_line1 : R.layout.construct_form_line, null); if (simple) horzLayout.setId(id); TextView label = (TextView) horzLayout.findViewById(R.id.textView1); String name = argumentName(argument); label.setText(name); label.setId(id + (simple ? 1 : 3)); if (optional) { CheckBox checkBox = (CheckBox) label; checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton view, boolean isChecked) { View v = scrollView.findViewById(view.getId() + 1); if (v != null) { v.setEnabled(isChecked); if (isChecked) { if (simple) makeSuggestions(index); else attachSuggestions(suggestionList(type), v); } validate(); } } }); checkBox.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { CheckBox cb = (CheckBox) v; boolean flag = cb.isChecked(); int id = cb.getId(); do { id = id - (flag ? 10 : -10); v = scrollView.findViewById(id); if (v instanceof CheckBox) { cb = (CheckBox) v; if (flag ^ cb.isChecked()) cb.setChecked(flag); } } while (v != null); } }); } // horzLayout.addView( // label, // linearLayoutParams(LayoutParams.WRAP_CONTENT, // LayoutParams.WRAP_CONTENT, margin, halfMargin, // halfMargin, halfMargin)); // boolean listAlike = false; // isTypeLike(56, type); // final AutoCompleteTextView field = listAlike ? // new MultiAutoCompleteTextView(this) : // new AutoCompleteTextView(this); // field.setThreshold(1); // if (listAlike) // ((MultiAutoCompleteTextView)field) // .setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer()); final AutoCompleteTextView field = (AutoCompleteTextView) horzLayout.findViewById(R.id.autoCompleteTextView1); field.setText(param("", index, defaults)); field.setId(id + (simple ? 2 : 4)); field.setEnabled(!optional); field.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (v.equals(focused) && !hasFocus) focused = null; if (v.equals(field) && hasFocus) focused = field; } }); field.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { validate(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }); // horzLayout.addView( // field, // linearLayoutParams(LayoutParams.FILL_PARENT, // LayoutParams.WRAP_CONTENT, halfMargin, // halfMargin, margin, halfMargin)); map.put(ARGUMENT, argument); map.put(LAYOUT_ID, horzLayout.getId()); map.put(LABEL_ID, label.getId()); map.put(FIELD_ID, field.getId()); map.put(ARGUMENT_TYPE, type); map.put(OPTIONAL, optional); return horzLayout; } private void addComment(ValMap map, String prefix, boolean append, String comment) { String old = valueOrElse("", map.get(COMMENT)); if (append) comment = old + "\n" + comment; else comment += "\n" + old; if (notNullOrEmpty(prefix)) comment = prefix + " : " + comment; map.put(COMMENT, strip(Constraint.START, comment, "\n")); } private boolean isTypeLike(int typeIndex, String type) { if (typeIndex == 56) return isType(5, type) || isType(6, type); else return isType(typeIndex, type); } private boolean anyTypeAllowed(String type) { return ANY.equals(type); } private Boolean isOk; private Boolean isContentValid(final ValMap map, Object... params) { final String type = getType(map); String text = getText(map); String prefix = param_String("", 0, params); isOk = param_Boolean(true, 1, params); if (!isOk) return false; String[] messages = getResources().getStringArray(R.array.construct_validation_messages); if (text.length() < 1) { addComment(map, prefix, true, messages[8]); return false; } if (isTypeLike(IDENTIFIER_TYPE_INDEX, type)) { isOk = compliesWith(IDENTIFIER_TYPE_INDEX, text); if (!isOk) addComment(map, prefix, true, messages[IDENTIFIER_TYPE_INDEX]); return isOk; } if (compliesWith(0, text)) return true; else if (isTypeLike(0, type)) { addComment(map, prefix, true, messages[0]); return false; } boolean allowed = anyTypeAllowed(type); for (int t = 1; t < IDENTIFIER_TYPE_INDEX; t++) { boolean required = isTypeLike(t, type); if (required || allowed) { if (compliesWith(t, text)) return true; else if (required) { addComment(map, prefix, true, messages[t]); return false; } } } if (isTypeLike(56, type)) { try { walkJSON(null, new JSONObject(text), new Function<Object>() { public Object apply(Object...params) { Object[] path = param(null, 0, params); Object value = param(null, 1, params); String name = Arrays.toString(path); map.put(TEXT, value.toString()); map.put(ARGUMENT_TYPE, ANY); isOk &= isContentValid(map, name, isOk); map.remove(TEXT); map.put(ARGUMENT_TYPE, type); return value; } }); } catch (Exception e) { addComment(map, prefix, true, e.getMessage()); return false; } if (!isOk) return false; } if (allowed) { addComment(map, prefix, true, messages[9]); return false; } return true; } private ValList suggestionList(final String type) { ValList list = new ValList(); if (type.length() < 1) list.add(0, UNKNOWN); else { Object[] dummies = getDummies(0); if (!isTypeLike(IDENTIFIER_TYPE_INDEX, type)) list.addAll(Arrays.asList(dummies)); for (int t = 1; t < DATA_TYPES.length; t++) { if (isTypeLike(t, type)) { dummies = getDummies(t); list.addAll(0, Arrays.asList(dummies)); } } } Predicate<Object> isExcluded = new Predicate<Object>() { @Override public boolean apply(Object object) { if (!isType(3, type) && MATH_TOOL.equals(object)) return true; if (!isType(URI_TYPE_INDEX, type) && object.toString().toLowerCase(Locale.getDefault()).endsWith("uri")) return true; return false; } }; Collection<Object> suggestions = userContext.suggestions(isExcluded, true); list.addAll(0, suggestions); return list; } private void attachSuggestions(ValMap map) { if (map != null) { ValList list = (ValList) map.get("suggest"); attachSuggestions(list, getField(map)); } } private void attachSuggestions(ValList list, View vw) { if (list != null) { ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, list.toArray(new String[0])); if (vw instanceof AutoCompleteTextView) ((AutoCompleteTextView)vw).setAdapter(adapter); } } private void makeSuggestions(Integer... indices) { new SuggestionTask().execute(indices); } @SuppressLint("NewApi") private class SuggestionTask extends AsyncTask<Integer, ValMap, ValMap> { @Override protected ValMap doInBackground(Integer... index) { for (int i = param(0, 0, index); i < fieldMaps.size(); i++) { ValMap fieldMap = (ValMap) fieldMaps.get(i); if (isInactive(fieldMap)) continue; String type = getType(fieldMap); fieldMap.put("suggest", suggestionList(type)); if (index.length > 0) return fieldMap; else publishProgress(fieldMap); } return null; } @Override protected void onProgressUpdate(ValMap... maps) { attachSuggestions(maps[0]); } @Override protected void onPostExecute(ValMap result) { super.onPostExecute(result); if (result != null) onProgressUpdate(result); } } private void validate() { new ValidationTask().execute(); } @SuppressLint("NewApi") private class ValidationTask extends AsyncTask<Integer, Boolean, Boolean> { @Override protected Boolean doInBackground(Integer... params) { return formValidate(false); } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); enableOK(result); } } private View getLayout(ValMap map) { Integer id = (Integer) map.get(LAYOUT_ID); return contentView.findViewById(id); } private View getField(ValMap map) { Integer id = (Integer) map.get(FIELD_ID); return contentView.findViewById(id); } private String getText(ValMap map) { if (map.containsKey(TEXT)) return map.get(TEXT).toString(); else { TextView field = (TextView) getField(map); return field.getText().toString(); } } private String getType(ValMap map) { return map.get(ARGUMENT_TYPE).toString(); } private boolean isInactive(ValMap map) { Boolean optional = valueOrElse(false, map.get(OPTIONAL)); if (optional) { Integer id = (Integer) map.get(LABEL_ID); CheckBox label = (CheckBox) contentView.findViewById(id); return label.isChecked() == false; } return false; } private boolean fieldValidate(boolean toast, boolean valid, ValMap fieldMap) { if (isInactive(fieldMap)) return valid; valid &= isContentValid(fieldMap); if (!valid) { if (toast) { Toast.makeText(ConstructDialogs.this, fieldMap.get(COMMENT).toString(), Toast.LENGTH_SHORT).show(); getField(fieldMap).requestFocus(); } fieldMap.remove(COMMENT); return false; } if (fieldMap.containsKey(SECOND_FIELD)) { fieldMap = (ValMap) fieldMap.get(SECOND_FIELD); return fieldValidate(toast, valid, fieldMap); } return true; } protected boolean formValidate(boolean toast) { boolean valid = true; for (int i = 0; i < fieldMaps.size(); i++) { ValMap fieldMap = (ValMap) fieldMaps.get(i); valid = fieldValidate(toast, valid, fieldMap); if (!valid) break; } return valid; } private Object formEvaluate() { ValList list = new ValList(); for (int i = 0; i < fieldMaps.size(); i++) { ValMap fieldMap = (ValMap) fieldMaps.get(i); if (isInactive(fieldMap)) continue; list.add(getText(fieldMap)); } return list; } }