Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom validation with new validateCustom tag #21

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Latest Version: [![Latest version](https://jitpack.io/v/Ilhasoft/data-binding-va

* Minimum/Maximum length validation for text fields;
* Validate inputs based on field type (email, credit card, URL, CPF and so on);
* Custom validation by calling a public static method on a string returning a boolean.
* Pre-defined error messages translated into English, Portuguese and Spanish;
* Custom error messages by field;
* Supports [`TextInputLayout`](https://developer.android.com/reference/android/support/design/widget/TextInputLayout.html) and EditText;
Expand Down Expand Up @@ -127,6 +128,27 @@ You can even validate input by date, for example Email, URL, Username, CreditCar
<EditText app:validateType='@{"cpf"}' />
```

#### Validate Custom ####

Adding `validateCustom`, you can set a public static function do validation, for example:

```
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:validateCustom='@{"br.com.ilhasoft.support.validation.sample.MainActivity.validatePassword"}'
app:validateCustomMessage="@{@string/custom_error_password_description}"
/>

// in MainActivity:
public static boolean validatePassword(String password){
return password.matches(".*[a-z].*") &&
password.matches(".*[A-Z].*") &&
password.matches(".*[0-9].*");
}
```

### Applying Validation ###

It will be necessary to instantiate `Validator` passing as argument your `ViewDataBinding` instance got from your layout binding. After that you can call `validate()` that will return if your data is valid or not. Example:
Expand Down Expand Up @@ -224,6 +246,20 @@ By default, the library prompts error messages and doens't dismiss the error aut
app:validateDateAutoDismiss="@{true}" />
```

### Validate each character ###

To validate as the user types, use addTextChangedListener. For example:

```
binding.password.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) { }
@Override public void afterTextChanged(Editable s) {
validator.validate(binding.password);
}
});
```

## License ##

Copyright 2017-present Ilhasoft
Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ buildscript {
repositories {
maven { url 'https://maven.google.com' }
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
}
}
Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Wed Sep 20 21:27:17 BRT 2017
#Thu Apr 05 09:32:46 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2017-present Ilhasoft.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package br.com.ilhasoft.support.validation.binding;

import android.databinding.BindingAdapter;
import android.widget.TextView;

import br.com.ilhasoft.support.validation.R;
import br.com.ilhasoft.support.validation.rule.CustomRule;
import br.com.ilhasoft.support.validation.util.EditTextHandler;
import br.com.ilhasoft.support.validation.util.ErrorMessageHelper;
import br.com.ilhasoft.support.validation.util.ViewTagHelper;

/**
* Created by John Lombardo on april 5 2018
*/
public class CustomBindings {

@BindingAdapter(value = {"validateCustom", "validateCustomMessage", "validateCustomAutoDismiss"}, requireAll = false)
public static void bindingCustom(TextView view, String staticFunction, String errorMessage, boolean autoDismiss) {
if (autoDismiss) {
EditTextHandler.disableErrorOnChanged(view);
}

String handledErrorMessage = ErrorMessageHelper.getStringOrDefault(view,
errorMessage, R.string.error_message_custom_validation);
ViewTagHelper.appendValue(R.id.validator_rule, view, new CustomRule(view, staticFunction, handledErrorMessage));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2017-present Ilhasoft.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package br.com.ilhasoft.support.validation.rule;

import android.widget.TextView;

import br.com.ilhasoft.support.validation.util.EditTextHandler;
import br.com.ilhasoft.support.validation.util.CustomRuleCaller;

/**
* Created by John Lombardo on april 5 2018
*/
public class CustomRule extends Rule<TextView, String> {

public CustomRule(TextView view, String value, String errorMessage) {
super(view, value, errorMessage);
}

@Override
public boolean isValid(TextView view) {
return CustomRuleCaller.isString(value, view.getText().toString());
}

@Override
public void onValidationSucceeded(TextView view) {
EditTextHandler.removeError(view);
}

@Override
public void onValidationFailed(TextView view) {
EditTextHandler.setError(view, errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package br.com.ilhasoft.support.validation.util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class CustomRuleCaller {
public static boolean isString(String func, String arg){
final String clazzName = func.substring(0,func.lastIndexOf("."));
final String funcName = func.substring(func.lastIndexOf(".")+1);
try {
final Class c = Class.forName(clazzName);
Method m = c.getDeclaredMethod(funcName, String.class);
Object o = m.invoke(null, arg);
return (boolean) o;
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
}
}
}
1 change: 1 addition & 0 deletions library/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<string name="error_message_max_length">Campo no válido. El número máximo de caracteres %1$d</string>
<string name="error_message_date_validation">Formato de datos no válido</string>
<string name="error_message_regex_validation">Formato no válido</string>
<string name="error_message_custom_validation">Formato no válido</string>

<string name="error_message_email_validation">Formato de correo electrónico no válido</string>
<string name="error_message_cpf_validation">Formato de CPF no válido</string>
Expand Down
1 change: 1 addition & 0 deletions library/src/main/res/values-pt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<string name="error_message_max_length">Campo inválido. O número máximo de caracteres é %1$d</string>
<string name="error_message_date_validation">Formato de data inválido</string>
<string name="error_message_regex_validation">Formato inválido</string>
<string name="error_message_custom_validation">Formato inválido</string>

<string name="error_message_email_validation">Formato do e-mail inválido</string>
<string name="error_message_cpf_validation">Formato do CPF inválido</string>
Expand Down
1 change: 1 addition & 0 deletions library/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<string name="error_message_max_length">Invalid field. The maximum number of characters %1$d</string>
<string name="error_message_date_validation">Invalid data format</string>
<string name="error_message_regex_validation">Invalid format</string>
<string name="error_message_custom_validation">Invalid format</string>

<string name="error_message_email_validation">Invalid email format</string>
<string name="error_message_username_validation">Invalid username format</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package br.com.ilhasoft.support.validation;

import org.junit.Test;

import br.com.ilhasoft.support.validation.util.CustomRuleCaller;

import static org.junit.Assert.assertEquals;

public class CustomRuleCallerTest {
@Test
public void it_is_foo() throws Exception {
assertEquals(true, CustomRuleCaller.isString("br.com.ilhasoft.support.validation.CustomRuleCallerTest.isItFoo", "Foo"));
}

@Test
public void it_is_not_foo() throws Exception {
assertEquals(false, CustomRuleCaller.isString("br.com.ilhasoft.support.validation.CustomRuleCallerTest.isItFoo", "Bar"));
}

public static boolean isItFoo(String arg){
return "foo".equals(arg.toLowerCase());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
Expand Down Expand Up @@ -39,6 +41,14 @@ protected void onCreate(Bundle savedInstanceState) {
binding.validate.setOnClickListener(onValidateAllClickListener);
binding.toValidate.setOnClickListener(onValidateAllWithListenerClickListener);

binding.password.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) { }
@Override public void afterTextChanged(Editable s) {
validator.validate(binding.password);
}
});

validator = new Validator(binding);
validator.setValidationListener(this);
validator.enableFormValidationMode();
Expand Down Expand Up @@ -80,4 +90,18 @@ private void saveToDatabase() {
Log.i(TAG, "Salvar os dados no banco de dados");
}

/**
* Validate the password has a number an upper case character and a lower case character.
*
* Note: This is called through the following tag in activity_main:
* app:validateCustom='@{"br.com.ilhasoft.support.validation.sample.MainActivity.validatePassword"}'
* @param password the string under test
* @return true if it's valid
*/
public static boolean validatePassword(String password){
return password.matches(".*[a-z].*") &&
password.matches(".*[A-Z].*") &&
password.matches(".*[0-9].*");
}

}
4 changes: 3 additions & 1 deletion sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"
android:inputType="textPassword" />
android:inputType="textPassword"
app:validateCustom='@{"br.com.ilhasoft.support.validation.sample.MainActivity.validatePassword"}'
app:validateCustomMessage="@{@string/custom_error_password_description}"/>

</android.support.design.widget.TextInputLayout>

Expand Down
1 change: 1 addition & 0 deletions sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
<string name="custom_error_date">Custom message: It\'s not a date!</string>
<string name="custom_error_min_length">Custom message: This message is too small!</string>
<string name="custom_error_max_length">Custom message: This message is too big!</string>
<string name="custom_error_password_description">Custom message: Password has a lower, an upper and a digit!</string>
</resources>