Loading app/src/main/java/net/fabiszewski/ulogger/SettingsActivity.java +0 −25 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.util.Patterns; import android.widget.Toast; /** Loading @@ -44,32 +43,12 @@ public class SettingsActivity extends PreferenceActivity { @SuppressWarnings("deprecation") private void onCreatePreferenceActivity() { addPreferencesFromResource(R.xml.preferences); final Preference prefHost = findPreference("prefHost"); if (prefHost != null) { prefHost.setOnPreferenceChangeListener(hostChanged); } final Preference prefLiveSync = findPreference("prefLiveSync"); if (prefLiveSync != null) { prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged); } } /** * On change listener to validate server url */ private final static Preference.OnPreferenceChangeListener hostChanged = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (Patterns.WEB_URL.matcher(newValue.toString().trim()).matches()) { return true; } else { Toast.makeText(preference.getContext(), R.string.provide_valid_url, Toast.LENGTH_LONG).show(); return false; } } }; /** * On change listener to validate whether live synchronization is allowed */ Loading Loading @@ -114,10 +93,6 @@ public class SettingsActivity extends PreferenceActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); final Preference prefHost = findPreference("prefHost"); if (prefHost != null) { prefHost.setOnPreferenceChangeListener(hostChanged); } final Preference prefLiveSync = findPreference("prefLiveSync"); if (prefLiveSync != null) { prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged); Loading app/src/main/java/net/fabiszewski/ulogger/TrimmedEditTextPreference.java→app/src/main/java/net/fabiszewski/ulogger/UrlEditTextPreference.java +74 −0 Original line number Diff line number Diff line Loading @@ -10,32 +10,36 @@ package net.fabiszewski.ulogger; import android.content.Context; import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; import android.preference.EditTextPreference; import android.support.annotation.RequiresApi; import android.app.AlertDialog; import android.util.AttributeSet; import android.view.View; /** * Trimmed edit text preference * * URL edit text preference * Validates and trims URL */ class TrimmedEditTextPreference extends EditTextPreference { class UrlEditTextPreference extends EditTextPreference { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public TrimmedEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public TrimmedEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public TrimmedEditTextPreference(Context context, AttributeSet attrs) { public UrlEditTextPreference(Context context, AttributeSet attrs) { super(context, attrs); } public TrimmedEditTextPreference(Context context) { public UrlEditTextPreference(Context context) { super(context); } Loading @@ -43,4 +47,28 @@ class TrimmedEditTextPreference extends EditTextPreference { public void setText(String text) { super.setText(text.trim()); } @Override protected void showDialog(Bundle state) { super.showDialog(state); getEditText().setError(null); final AlertDialog dialog = (AlertDialog) getDialog(); View positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); positiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onPositiveButtonClicked(); } }); } private void onPositiveButtonClicked() { if (WebHelper.isValidURL(getEditText().getText().toString().trim())) { getEditText().setError(null); onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); getDialog().dismiss(); } else { getEditText().setError(getContext().getString(R.string.provide_valid_url)); } } } app/src/main/java/net/fabiszewski/ulogger/WebHelper.java +10 −0 Original line number Diff line number Diff line Loading @@ -297,4 +297,14 @@ class WebHelper { } } /** * Check whether given url is valid. * Uses relaxed pattern (@see WebPatterns#WEB_URL_RELAXED) * @param url URL * @return True if valid, false otherwise */ static boolean isValidURL(String url) { return WebPatterns.WEB_URL_RELAXED.matcher(url).matches(); } } app/src/main/java/net/fabiszewski/ulogger/WebPatterns.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * Copyright (c) 2017 Bartek Fabiszewski * http://www.fabiszewski.net * * This file is part of μlogger-android. * Licensed under GPL, either version 3, or any later. * See <http://www.gnu.org/licenses/> */ package net.fabiszewski.ulogger; import java.util.regex.Pattern; /** * This is based on java.android.util.Patterns * with WEB_URL pattern relaxed */ class WebPatterns { /** * Protocols limited to http(s). */ private static final String PROTOCOL = "(?i:https?)://"; private static final String USER_INFO = "(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,64}" + "(?::(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,25})?@"; /** * Valid UCS characters defined in RFC 3987. Excludes space characters. */ private static final String UCS_CHAR = "[" + "\u00A0-\uD7FF" + "\uF900-\uFDCF" + "\uFDF0-\uFFEF" + "\uD800\uDC00-\uD83F\uDFFD" + "\uD840\uDC00-\uD87F\uDFFD" + "\uD880\uDC00-\uD8BF\uDFFD" + "\uD8C0\uDC00-\uD8FF\uDFFD" + "\uD900\uDC00-\uD93F\uDFFD" + "\uD940\uDC00-\uD97F\uDFFD" + "\uD980\uDC00-\uD9BF\uDFFD" + "\uD9C0\uDC00-\uD9FF\uDFFD" + "\uDA00\uDC00-\uDA3F\uDFFD" + "\uDA40\uDC00-\uDA7F\uDFFD" + "\uDA80\uDC00-\uDABF\uDFFD" + "\uDAC0\uDC00-\uDAFF\uDFFD" + "\uDB00\uDC00-\uDB3F\uDFFD" + "\uDB44\uDC00-\uDB7F\uDFFD" + "&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]"; /** * Valid characters for IRI label defined in RFC 3987. */ private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR; /** * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets. */ private static final String IRI_LABEL = "[" + LABEL_CHAR + "]" + "(?:[" + LABEL_CHAR + "\\-]{0,61}[" + LABEL_CHAR + "])?"; /** * Regular expression that matches domain names without a TLD, also IP addresses */ private static final String RELAXED_DOMAIN_NAME = "(?:(?:" + IRI_LABEL + "(?:\\.(?=[" + LABEL_CHAR + "]))?)+)"; private static final String PORT_NUMBER = ":\\d{1,5}"; /** * A word boundary or end of input. This is to stop foo.sure from matching as foo.su */ private static final String WORD_BOUNDARY = "(?:\\b|$|^)"; /** * Path segment, exclude repeated slashes to rule out common error (http//example.com) */ private static final String PATH_SEGMENT = "/(?:(?:[" + LABEL_CHAR + ";:@&=~\\-.+!*'(),_])|(?:%[a-fA-F0-9]{2})|" + WORD_BOUNDARY + ")+"; /** * Regular expression pattern to match most part of RFC 3987 * Internationalized URLs, aka IRIs. * Relaxed to accept domains without a TLD. * Will not accept query part. * Only http and https protocols. */ static final Pattern WEB_URL_RELAXED = Pattern.compile( "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?" + RELAXED_DOMAIN_NAME + "(?:" + PORT_NUMBER + ")?" + "(?:" + PATH_SEGMENT + ")*" + WORD_BOUNDARY); } No newline at end of file app/src/main/res/xml/preferences.xml +2 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ --> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <net.fabiszewski.ulogger.TrimmedEditTextPreference <net.fabiszewski.ulogger.UrlEditTextPreference android:key="prefUsername" android:title="@string/pref_username_title" android:summary="@string/pref_username_summary" Loading @@ -19,7 +19,7 @@ android:title="@string/pref_pass_title" android:summary="@string/pref_pass_summary" android:inputType="textPassword" /> <net.fabiszewski.ulogger.TrimmedEditTextPreference <net.fabiszewski.ulogger.UrlEditTextPreference android:key="prefHost" android:title="@string/pref_host_title" android:summary="@string/pref_host_summary" Loading Loading
app/src/main/java/net/fabiszewski/ulogger/SettingsActivity.java +0 −25 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.util.Patterns; import android.widget.Toast; /** Loading @@ -44,32 +43,12 @@ public class SettingsActivity extends PreferenceActivity { @SuppressWarnings("deprecation") private void onCreatePreferenceActivity() { addPreferencesFromResource(R.xml.preferences); final Preference prefHost = findPreference("prefHost"); if (prefHost != null) { prefHost.setOnPreferenceChangeListener(hostChanged); } final Preference prefLiveSync = findPreference("prefLiveSync"); if (prefLiveSync != null) { prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged); } } /** * On change listener to validate server url */ private final static Preference.OnPreferenceChangeListener hostChanged = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (Patterns.WEB_URL.matcher(newValue.toString().trim()).matches()) { return true; } else { Toast.makeText(preference.getContext(), R.string.provide_valid_url, Toast.LENGTH_LONG).show(); return false; } } }; /** * On change listener to validate whether live synchronization is allowed */ Loading Loading @@ -114,10 +93,6 @@ public class SettingsActivity extends PreferenceActivity { public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); final Preference prefHost = findPreference("prefHost"); if (prefHost != null) { prefHost.setOnPreferenceChangeListener(hostChanged); } final Preference prefLiveSync = findPreference("prefLiveSync"); if (prefLiveSync != null) { prefLiveSync.setOnPreferenceChangeListener(liveSyncChanged); Loading
app/src/main/java/net/fabiszewski/ulogger/TrimmedEditTextPreference.java→app/src/main/java/net/fabiszewski/ulogger/UrlEditTextPreference.java +74 −0 Original line number Diff line number Diff line Loading @@ -10,32 +10,36 @@ package net.fabiszewski.ulogger; import android.content.Context; import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; import android.preference.EditTextPreference; import android.support.annotation.RequiresApi; import android.app.AlertDialog; import android.util.AttributeSet; import android.view.View; /** * Trimmed edit text preference * * URL edit text preference * Validates and trims URL */ class TrimmedEditTextPreference extends EditTextPreference { class UrlEditTextPreference extends EditTextPreference { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public TrimmedEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public TrimmedEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { public UrlEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public TrimmedEditTextPreference(Context context, AttributeSet attrs) { public UrlEditTextPreference(Context context, AttributeSet attrs) { super(context, attrs); } public TrimmedEditTextPreference(Context context) { public UrlEditTextPreference(Context context) { super(context); } Loading @@ -43,4 +47,28 @@ class TrimmedEditTextPreference extends EditTextPreference { public void setText(String text) { super.setText(text.trim()); } @Override protected void showDialog(Bundle state) { super.showDialog(state); getEditText().setError(null); final AlertDialog dialog = (AlertDialog) getDialog(); View positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE); positiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onPositiveButtonClicked(); } }); } private void onPositiveButtonClicked() { if (WebHelper.isValidURL(getEditText().getText().toString().trim())) { getEditText().setError(null); onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); getDialog().dismiss(); } else { getEditText().setError(getContext().getString(R.string.provide_valid_url)); } } }
app/src/main/java/net/fabiszewski/ulogger/WebHelper.java +10 −0 Original line number Diff line number Diff line Loading @@ -297,4 +297,14 @@ class WebHelper { } } /** * Check whether given url is valid. * Uses relaxed pattern (@see WebPatterns#WEB_URL_RELAXED) * @param url URL * @return True if valid, false otherwise */ static boolean isValidURL(String url) { return WebPatterns.WEB_URL_RELAXED.matcher(url).matches(); } }
app/src/main/java/net/fabiszewski/ulogger/WebPatterns.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * Copyright (c) 2017 Bartek Fabiszewski * http://www.fabiszewski.net * * This file is part of μlogger-android. * Licensed under GPL, either version 3, or any later. * See <http://www.gnu.org/licenses/> */ package net.fabiszewski.ulogger; import java.util.regex.Pattern; /** * This is based on java.android.util.Patterns * with WEB_URL pattern relaxed */ class WebPatterns { /** * Protocols limited to http(s). */ private static final String PROTOCOL = "(?i:https?)://"; private static final String USER_INFO = "(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,64}" + "(?::(?:[-a-zA-Z0-9$_.+!*'(),;?&=]|(?:%[a-fA-F0-9]{2})){1,25})?@"; /** * Valid UCS characters defined in RFC 3987. Excludes space characters. */ private static final String UCS_CHAR = "[" + "\u00A0-\uD7FF" + "\uF900-\uFDCF" + "\uFDF0-\uFFEF" + "\uD800\uDC00-\uD83F\uDFFD" + "\uD840\uDC00-\uD87F\uDFFD" + "\uD880\uDC00-\uD8BF\uDFFD" + "\uD8C0\uDC00-\uD8FF\uDFFD" + "\uD900\uDC00-\uD93F\uDFFD" + "\uD940\uDC00-\uD97F\uDFFD" + "\uD980\uDC00-\uD9BF\uDFFD" + "\uD9C0\uDC00-\uD9FF\uDFFD" + "\uDA00\uDC00-\uDA3F\uDFFD" + "\uDA40\uDC00-\uDA7F\uDFFD" + "\uDA80\uDC00-\uDABF\uDFFD" + "\uDAC0\uDC00-\uDAFF\uDFFD" + "\uDB00\uDC00-\uDB3F\uDFFD" + "\uDB44\uDC00-\uDB7F\uDFFD" + "&&[^\u00A0[\u2000-\u200A]\u2028\u2029\u202F\u3000]]"; /** * Valid characters for IRI label defined in RFC 3987. */ private static final String LABEL_CHAR = "a-zA-Z0-9" + UCS_CHAR; /** * RFC 1035 Section 2.3.4 limits the labels to a maximum 63 octets. */ private static final String IRI_LABEL = "[" + LABEL_CHAR + "]" + "(?:[" + LABEL_CHAR + "\\-]{0,61}[" + LABEL_CHAR + "])?"; /** * Regular expression that matches domain names without a TLD, also IP addresses */ private static final String RELAXED_DOMAIN_NAME = "(?:(?:" + IRI_LABEL + "(?:\\.(?=[" + LABEL_CHAR + "]))?)+)"; private static final String PORT_NUMBER = ":\\d{1,5}"; /** * A word boundary or end of input. This is to stop foo.sure from matching as foo.su */ private static final String WORD_BOUNDARY = "(?:\\b|$|^)"; /** * Path segment, exclude repeated slashes to rule out common error (http//example.com) */ private static final String PATH_SEGMENT = "/(?:(?:[" + LABEL_CHAR + ";:@&=~\\-.+!*'(),_])|(?:%[a-fA-F0-9]{2})|" + WORD_BOUNDARY + ")+"; /** * Regular expression pattern to match most part of RFC 3987 * Internationalized URLs, aka IRIs. * Relaxed to accept domains without a TLD. * Will not accept query part. * Only http and https protocols. */ static final Pattern WEB_URL_RELAXED = Pattern.compile( "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?" + RELAXED_DOMAIN_NAME + "(?:" + PORT_NUMBER + ")?" + "(?:" + PATH_SEGMENT + ")*" + WORD_BOUNDARY); } No newline at end of file
app/src/main/res/xml/preferences.xml +2 −2 Original line number Diff line number Diff line Loading @@ -9,7 +9,7 @@ --> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <net.fabiszewski.ulogger.TrimmedEditTextPreference <net.fabiszewski.ulogger.UrlEditTextPreference android:key="prefUsername" android:title="@string/pref_username_title" android:summary="@string/pref_username_summary" Loading @@ -19,7 +19,7 @@ android:title="@string/pref_pass_title" android:summary="@string/pref_pass_summary" android:inputType="textPassword" /> <net.fabiszewski.ulogger.TrimmedEditTextPreference <net.fabiszewski.ulogger.UrlEditTextPreference android:key="prefHost" android:title="@string/pref_host_title" android:summary="@string/pref_host_summary" Loading