added FormWrapper and form field validation

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/forms/FormBooleanFieldWrapper.java   | 25 
src/main/java/eu/siacs/conversations/ui/forms/FormFieldFactory.java          |  2 
src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java          | 24 
src/main/java/eu/siacs/conversations/ui/forms/FormJidSingleFieldWrapper.java |  2 
src/main/java/eu/siacs/conversations/ui/forms/FormTextFieldWrapper.java      | 29 
src/main/java/eu/siacs/conversations/ui/forms/FormWrapper.java               | 66 
src/main/java/eu/siacs/conversations/xmpp/forms/Field.java                   | 15 
src/main/res/values/strings.xml                                              |  1 
8 files changed, 160 insertions(+), 4 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/forms/FormBooleanFieldWrapper.java 🔗

@@ -2,6 +2,7 @@ package eu.siacs.conversations.ui.forms;
 
 import android.content.Context;
 import android.widget.CheckBox;
+import android.widget.CompoundButton;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -16,6 +17,13 @@ public class FormBooleanFieldWrapper extends FormFieldWrapper {
 	protected FormBooleanFieldWrapper(Context context, Field field) {
 		super(context, field);
 		checkBox = (CheckBox) view.findViewById(R.id.field);
+		checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+			@Override
+			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+				checkBox.setError(null);
+				invokeOnFormFieldValuesEdited();
+			}
+		});
 	}
 
 	@Override
@@ -33,7 +41,22 @@ public class FormBooleanFieldWrapper extends FormFieldWrapper {
 
 	@Override
 	public boolean validates() {
-		return checkBox.isChecked() || !field.isRequired();
+		if (checkBox.isChecked() || !field.isRequired()) {
+			return true;
+		} else {
+			checkBox.setError(context.getString(R.string.this_field_is_required));
+			checkBox.requestFocus();
+			return false;
+		}
+	}
+
+	@Override
+	public boolean edited() {
+		if (field.getValues().size() == 0) {
+			return checkBox.isChecked();
+		} else {
+			return super.edited();
+		}
 	}
 
 	@Override

src/main/java/eu/siacs/conversations/ui/forms/FormFieldFactory.java 🔗

@@ -20,7 +20,7 @@ public class FormFieldFactory {
 		typeTable.put("boolean", FormBooleanFieldWrapper.class);
 	}
 
-	public static FormFieldWrapper createFromField(Context context, Field field) {
+	protected static FormFieldWrapper createFromField(Context context, Field field) {
 		Class clazz = typeTable.get(field.getType());
 		if (clazz == null) {
 			clazz = FormTextFieldWrapper.class;

src/main/java/eu/siacs/conversations/ui/forms/FormFieldWrapper.java 🔗

@@ -4,11 +4,13 @@ import android.content.Context;
 import android.text.SpannableString;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import java.util.List;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.xmpp.forms.Field;
 
@@ -17,6 +19,7 @@ public abstract class FormFieldWrapper {
 	protected final Context context;
 	protected final Field field;
 	protected final View view;
+	protected OnFormFieldValuesEdited onFormFieldValuesEditedListener;
 
 	protected FormFieldWrapper(Context context, Field field) {
 		this.context = context;
@@ -57,6 +60,23 @@ public abstract class FormFieldWrapper {
 		return spannableString;
 	}
 
+	protected void invokeOnFormFieldValuesEdited() {
+		Log.d(Config.LOGTAG, "invoke on form field values edited");
+		if (this.onFormFieldValuesEditedListener != null) {
+			this.onFormFieldValuesEditedListener.onFormFieldValuesEdited();
+		} else {
+			Log.d(Config.LOGTAG,"listener is null");
+		}
+	}
+
+	public boolean edited() {
+		return !field.getValues().equals(getValues());
+	}
+
+	public void setOnFormFieldValuesEditedListener(OnFormFieldValuesEdited listener) {
+		this.onFormFieldValuesEditedListener = listener;
+	}
+
 	protected static <F extends FormFieldWrapper> FormFieldWrapper createFromField(Class<F> c, Context context, Field field) {
 		try {
 			return c.getDeclaredConstructor(Context.class, Field.class).newInstance(context,field);
@@ -65,4 +85,8 @@ public abstract class FormFieldWrapper {
 			return null;
 		}
 	}
+
+	public interface OnFormFieldValuesEdited {
+		void onFormFieldValuesEdited();
+	}
 }

src/main/java/eu/siacs/conversations/ui/forms/FormTextFieldWrapper.java 🔗

@@ -1,7 +1,9 @@
 package eu.siacs.conversations.ui.forms;
 
 import android.content.Context;
+import android.text.Editable;
 import android.text.InputType;
+import android.text.TextWatcher;
 import android.widget.EditText;
 import android.widget.TextView;
 
@@ -22,6 +24,21 @@ public class FormTextFieldWrapper extends FormFieldWrapper {
 		if ("text-private".equals(field.getType())) {
 			editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
 		}
+		editText.addTextChangedListener(new TextWatcher() {
+			@Override
+			public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+			}
+
+			@Override
+			public void onTextChanged(CharSequence s, int start, int before, int count) {
+				editText.setError(null);
+				invokeOnFormFieldValuesEdited();
+			}
+
+			@Override
+			public void afterTextChanged(Editable s) {
+			}
+		});
 	}
 
 	@Override
@@ -38,14 +55,22 @@ public class FormTextFieldWrapper extends FormFieldWrapper {
 	public List<String> getValues() {
 		List<String> values = new ArrayList<>();
 		for (String line : getValue().split("\\n")) {
-			values.add(line);
+			if (line.length() > 0) {
+				values.add(line);
+			}
 		}
 		return values;
 	}
 
 	@Override
 	public boolean validates() {
-		return getValue().trim().length() > 0 || !field.isRequired();
+		if (getValue().trim().length() > 0 || !field.isRequired()) {
+			return true;
+		} else {
+			editText.setError(context.getString(R.string.this_field_is_required));
+			editText.requestFocus();
+			return false;
+		}
 	}
 
 	@Override

src/main/java/eu/siacs/conversations/ui/forms/FormWrapper.java 🔗

@@ -0,0 +1,66 @@
+package eu.siacs.conversations.ui.forms;
+
+import android.content.Context;
+import android.widget.LinearLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.siacs.conversations.xmpp.forms.Data;
+import eu.siacs.conversations.xmpp.forms.Field;
+
+public class FormWrapper {
+
+	private final LinearLayout layout;
+
+	private final Data form;
+
+	private final List<FormFieldWrapper> fieldWrappers = new ArrayList<>();
+
+	private FormWrapper(Context context, LinearLayout linearLayout, Data form) {
+		this.form = form;
+		this.layout = linearLayout;
+		this.layout.removeAllViews();
+		for(Field field : form.getFields()) {
+			FormFieldWrapper fieldWrapper = FormFieldFactory.createFromField(context,field);
+			if (fieldWrapper != null) {
+				layout.addView(fieldWrapper.getView());
+				fieldWrappers.add(fieldWrapper);
+			}
+		}
+	}
+
+	public Data submit() {
+		for(FormFieldWrapper fieldWrapper : fieldWrappers) {
+			fieldWrapper.submit();
+		}
+		this.form.submit();
+		return this.form;
+	}
+
+	public boolean validates() {
+		boolean validates = true;
+		for(FormFieldWrapper fieldWrapper : fieldWrappers) {
+			validates &= fieldWrapper.validates();
+		}
+		return validates;
+	}
+
+	public void setOnFormFieldValuesEditedListener(FormFieldWrapper.OnFormFieldValuesEdited listener) {
+		for(FormFieldWrapper fieldWrapper : fieldWrappers) {
+			fieldWrapper.setOnFormFieldValuesEditedListener(listener);
+		}
+	}
+
+	public boolean edited() {
+		boolean edited = false;
+		for(FormFieldWrapper fieldWrapper : fieldWrappers) {
+			edited |= fieldWrapper.edited();
+		}
+		return edited;
+	}
+
+	public static FormWrapper createInLayout(Context context, LinearLayout layout, Data form) {
+		return new FormWrapper(context, layout, form);
+	}
+}

src/main/java/eu/siacs/conversations/xmpp/forms/Field.java 🔗

@@ -1,7 +1,9 @@
 package eu.siacs.conversations.xmpp.forms;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 
 import eu.siacs.conversations.xml.Element;
 
@@ -52,6 +54,19 @@ public class Field extends Element {
 		return findChildContent("value");
 	}
 
+	public List<String> getValues() {
+		List<String> values = new ArrayList<>();
+		for(Element child : getChildren()) {
+			if ("value".equals(child.getName())) {
+				String content = child.getContent();
+				if (content != null) {
+					values.add(content);
+				}
+			}
+		}
+		return values;
+	}
+
 	public String getLabel() {
 		return getAttribute("label");
 	}

src/main/res/values/strings.xml 🔗

@@ -581,4 +581,5 @@
 	<string name="disable">Disable</string>
 	<string name="selection_too_large">The selected area is too large</string>
 	<string name="no_accounts">(No activated accounts)</string>
+	<string name="this_field_is_required">This field is required</string>
 </resources>