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
}
});
}
}
@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