Search code examples
androidregexandroid-textwatcherurl-validation

How do I validate a URL in a TextWatcher while also matching with an empty string


I have a TextWatcher that checks if a URL is valid. A URL is valid if it fulfills the URL format where "http", "https", "www", etc. are optional. It is also valid if it's an empty string. The EditText will display an error message if the URL is invalid. This is my current implementation:

private TextWatcher websiteLinkWatcher = 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) {
        if(websiteLayout.getError() != null) websiteLayout.setErrorEnabled(false);
    }

    @Override
    public void afterTextChanged(Editable s) {
        String websiteFormat = "^(|https?:\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?){0,140}$";
        if(s.toString().trim().length() > 140 || !s.toString().matches(websiteFormat)) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    websiteLayout.setErrorEnabled(true);
                    websiteLayout.setError("The provided website is not valid.");
                }
            }, 2000);
            saveEnabled.setBackgroundColor(getResources().getColor(R.color.grey200));
            saveEnabled.setClickable(false);
            // disable
        }
        else {
            saveEnabled.setBackgroundColor(getResources().getColor(R.color.blue500));
            saveEnabled.setClickable(true);
            // enable
        }
        return;
    }
};

The regex is very inconsistent. Its only advantage is it works with an empty string (i.e. no error message is displayed). Currently, http://example.com, https://example.com, an empty string are accepted. https://www.example.com is sometimes accepted or rejected. www.example.com and example.com are rejected.


Solution

  • String pattern = "(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9]\.[^\s]{2,})";
    

    Will match the following cases

    • http://www.foufos.gr
    • https://www.foufos.gr
    • http://foufos.gr
    • http://www.foufos.gr/kino
    • http://www.t.co
    • http://t.co
    • http://werer.gr
    • www.foufos.gr
    • www.mp3.com
    • www.t.co

    Will NOT match the following

    • www.foufos
    • http://www.foufos
    • http://foufos
    • www.mp3#.com
    • www.foufos-.gr
    • www.-foufos.gr

    Concerning empty string, first check if it is empty and then check for pattern:

    if(yourstring.length() == 0 ||  yourstring.matches(pattern)) {
      // do something
    }else{
       // show validation warning
    }
    

    source