Skip to content

Instantly share code, notes, and snippets.

@fingerboxes
Last active July 23, 2021 20:55
Show Gist options
  • Save fingerboxes/5156252 to your computer and use it in GitHub Desktop.
Save fingerboxes/5156252 to your computer and use it in GitHub Desktop.
Android Preference widget based on EditTextPreference for inputting numeric IP Addresses.
/**
* EditTextPreference that only allows input of IP Addresses, using the Phone
* input type, and automatically inserts periods at the earliest appropriate
* interval.
*/
// Note; this probably isn't the best pattern for this - a Factory of Decorator
// pattern would have made more sense, rather than inheritance. However, this
// pattern is consistent with how other android Widgets are invoked, so I went
// with this to prevent confusion
public class IPAddressPreference extends EditTextPreference {
public IPAddressPreference(Context context) {
super(context);
getEditText().setInputType(InputType.TYPE_CLASS_PHONE);
getEditText().setFilters(new InputFilter[] { new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, android.text.Spanned dest, int dstart, int dend) {
if (end > start) {
String destTxt = dest.toString();
String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
if (!resultingTxt.matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) {
return "";
} else {
String[] splits = resultingTxt.split("\\.");
for (int i = 0; i < splits.length; i++) {
if (Integer.valueOf(splits[i]) > 255) {
return "";
}
}
}
}
return null;
}
} });
getEditText().addTextChangedListener(new TextWatcher() {
boolean deleting = false;
int lastCount = 0;
@Override
public void afterTextChanged(Editable s) {
if (!deleting) {
String working = s.toString();
String[] split = working.split("\\.");
String string = split[split.length - 1];
if (string.length() == 3 || string.equalsIgnoreCase("0")
|| (string.length() == 2 && Character.getNumericValue(string.charAt(0)) > 1)) {
s.append('.');
return;
}
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (lastCount < count) {
deleting = false;
} else {
deleting = true;
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Nothing happens here
}
});
}
}
@balage1551
Copy link

The condition in onTextChanged is wrong: adding dot after the second number when typing 200+ values.
This is a correction:

        @Override
        public void afterTextChanged(Editable s) {
            if (!deleting) {
                String working = s.toString();
                String[] split = working.split("\\.");
                String string = split[split.length - 1];
                if (string.length() == 3 || string.equalsIgnoreCase("0")
                        || (string.length() == 2 && Integer.parseInt(string) > 25)) {
                    s.append('.');
                    return;
                }
            }
        }

@weirdgyn
Copy link

weirdgyn commented Jun 6, 2019

If I try to use it I got this:

2019-06-06 11:15:39.030 13856-13856/xxxx E/AndroidRuntime: FATAL EXCEPTION: main
    Process: xxxx, PID: 13856
    android.view.InflateException: Binary XML file line #53: Error inflating class xxxx.IPAddressPreference
        at android.support.v7.preference.PreferenceInflater.createItem(PreferenceInflater.java:266)
        at android.support.v7.preference.PreferenceInflater.createItemFromTag(PreferenceInflater.java:295)
        at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:362)
        at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:364)
        at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:170)
        at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:120)
        at android.support.v7.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:138)
        at android.support.v7.preference.PreferenceFragmentCompat.setPreferencesFromResource(PreferenceFragmentCompat.java:445)
        at xxxx.SettingsFragment.onCreatePreferences(SettingsFragment.java:20)
        at android.support.v7.preference.PreferenceFragmentCompat.onCreate(PreferenceFragmentCompat.java:228)
        at android.support.v4.app.Fragment.performCreate(Fragment.java:2414)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1418)
        at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1195)
        at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:1078)
        at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:117)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2408)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
        at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
        at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
        at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391)
        at android.app.Activity.performStart(Activity.java:7348)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3132)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7045)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
     Caused by: java.lang.NoSuchMethodException: xxxx.IPAddressPreference.<init> [class android.content.Context, interface android.util.AttributeSet]
        at java.lang.Class.getConstructor0(Class.java:2328)
        at java.lang.Class.getConstructor(Class.java:1725)
        at android.support.v7.preference.PreferenceInflater.createItem(PreferenceInflater.java:252)
        at android.support.v7.preference.PreferenceInflater.createItemFromTag(PreferenceInflater.java:295) 
        at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:362) 
        at android.support.v7.preference.PreferenceInflater.rInflate(PreferenceInflater.java:364) 
        at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:170) 
        at android.support.v7.preference.PreferenceInflater.inflate(PreferenceInflater.java:120) 
        at android.support.v7.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:138) 
        at android.support.v7.preference.PreferenceFragmentCompat.setPreferencesFromResource(PreferenceFragmentCompat.java:445) 
        xxxx.SettingsFragment.onCreatePreferences(SettingsFragment.java:20) 
        at android.support.v7.preference.PreferenceFragmentCompat.onCreate(PreferenceFragmentCompat.java:228) 
        at android.support.v4.app.Fragment.performCreate(Fragment.java:2414) 
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1418) 
        at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1195) 
        at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:1078) 
        at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:117) 
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2408) 
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366) 
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273) 
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273) 
        at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229) 
        at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201) 
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620) 
        at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178) 
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391) 
        at android.app.Activity.performStart(Activity.java:7348) 
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3132) 
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180) 
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:214) 
        at android.app.ActivityThread.main(ActivityThread.java:7045) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 

@jldesignseu
Copy link

I've reworked the code to work with androidx. It's not perfect but works.

import android.content.Context;
import androidx.preference.EditTextPreference;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;

/**
 * EditTextPreference that only allows input of IP Addresses, using the Phone
 * input type, and automatically inserts periods at the earliest appropriate
 * interval.
 */

// Note; this probably isn't the best pattern for this - a Factory of Decorator
// pattern would have made more sense, rather than inheritance. However, this
// pattern is consistent with how other android Widgets are invoked, so I went
// with this to prevent confusion
public class IPAddressPreference extends EditTextPreference {

  public IPAddressPreference(Context context, AttributeSet attributeSet) {
		super(context,attributeSet);
	  setOnBindEditTextListener(editText ->
	  {
		  editText.setInputType(InputType.TYPE_CLASS_PHONE);
		  editText.setFilters(new InputFilter[]{new InputFilter() {
			  @Override
			  public CharSequence filter(CharSequence source, int start, int end, android.text.Spanned dest, int dstart, int dend) {
				  if (end > start) {
					  String destTxt = dest.toString();
					  String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
					  if (!resultingTxt.matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) {
						  return "";
					  } else {
						  String[] splits = resultingTxt.split("\\.");
						  for (int i = 0; i < splits.length; i++) {
							  if (Integer.valueOf(splits[i]) > 255) {
								  return "";
							  }
						  }
					  }
				  }
				  return null;
			  }
		  }});


		  editText.addTextChangedListener(new TextWatcher() {
			boolean deleting = false;
			int lastCount = 0;

			@Override
			public void afterTextChanged(Editable s) {
				if (!deleting) {
					String working = s.toString();
					String[] split = working.split("\\.");
					String string = split[split.length - 1];
					if (string.length() == 3 || string.equalsIgnoreCase("0")
							|| (string.length() == 2 && Integer.parseInt(string) > 25)) {
						s.append('.');
						return;
					}
				}
			}

			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				if (lastCount < count) {
					deleting = false;
				} else {
					deleting = true;
				}
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count, int after) {
				// Nothing happens here
			}
		});
	  });
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment