diff --git a/MaskedEditText/build.gradle b/MaskedEditText/build.gradle index e57534e..e52a469 100644 --- a/MaskedEditText/build.gradle +++ b/MaskedEditText/build.gradle @@ -5,20 +5,20 @@ plugins { apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' -String projectVersion = "1.0.5" +String projectVersion = "1.0.6" String projectGroup = "ru.egslava" version = projectVersion group = projectGroup android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' + compileSdkVersion 26 + buildToolsVersion '26.0.1' defaultConfig { - minSdkVersion 9 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 26 versionCode 1 versionName projectVersion testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -26,22 +26,22 @@ android { } dependencies { - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.2.0' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:26.0.1' - testCompile 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' } // bintray deploy .... String projectName = "edittext-mask" String projectDescription = GFileUtils.readFile(new File("README.md")) -String webUrl = "https://github.com/egslava/edittext-mask" -String gitUrl = "https://github.com/egslava/edittext-mask.git" +String webUrl = "https://github.com/edwardstock/edittext-mask" +String gitUrl = "https://github.com/edwardstock/edittext-mask.git" install { @@ -105,25 +105,26 @@ artifacts { archives sourcesJar } -bintray { - user = bintrayUser - key = bintrayApikey - - configurations = ['archives'] - pkg { - repo = "maven" - name = projectName - userOrg = user -// projectUrl = webUrl - vcsUrl = gitUrl - licenses = ["MIT"] - group = projectGroup - publish = true - - version { - name = projectVersion -// desc = projectDescription - vcsTag = projectVersion +if(hasProperty("bintrayUser") && hasProperty("bintrayApikey")) { + bintray { + user = bintrayUser + key = bintrayApikey + + configurations = ['archives'] + pkg { + repo = "maven" + name = projectName + userOrg = user + vcsUrl = gitUrl + licenses = ["MIT"] + group = projectGroup + publish = true + + version { + name = projectVersion + vcsTag = projectVersion + } } } -} \ No newline at end of file +} + diff --git a/MaskedEditText/src/androidTest/AndroidManifest.xml b/MaskedEditText/src/androidTest/AndroidManifest.xml index bd876b8..10c1e25 100755 --- a/MaskedEditText/src/androidTest/AndroidManifest.xml +++ b/MaskedEditText/src/androidTest/AndroidManifest.xml @@ -5,12 +5,12 @@ android:versionName="1.0" > + android:minSdkVersion="14" + android:targetSdkVersion="26" /> + android:theme="@style/Theme.AppCompat" /> \ No newline at end of file diff --git a/MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/MaskedEditText.java b/MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/MaskedEditText.java index 71776c7..981eb78 100755 --- a/MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/MaskedEditText.java +++ b/MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/MaskedEditText.java @@ -4,7 +4,7 @@ import android.content.res.TypedArray; import android.os.Bundle; import android.os.Parcelable; -import android.support.v4.text.TextUtilsCompat; +import android.support.annotation.NonNull; import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.SpannableStringBuilder; @@ -21,381 +21,402 @@ public class MaskedEditText extends AppCompatEditText implements TextWatcher { public static final String SPACE = " "; + protected int maxRawLength; private String mask; - private char charRepresentation; - private boolean keepHint; - private int[] rawToMask; - private RawText rawText; - private boolean editingBefore; - private boolean editingOnChanged; - private boolean editingAfter; - private int[] maskToRaw; - private int selection; - private boolean initialized; - private boolean ignore; - protected int maxRawLength; - private int lastValidMaskPosition; - private boolean selectionChanged; - private OnFocusChangeListener focusChangeListener; + private char charRepresentation; + private boolean keepHint; + private int[] rawToMask; + private RawText rawText; + private boolean editingBefore; + private boolean editingOnChanged; + private boolean editingAfter; + private int[] maskToRaw; + private int selection; + private boolean initialized; + private boolean ignore; + private int lastValidMaskPosition; + private boolean selectionChanged; + private OnFocusChangeListener focusChangeListener; private String allowedChars; private String deniedChars; public MaskedEditText(Context context) { - super(context); - init(); - } + super(context); + init(); + } - public MaskedEditText(Context context, AttributeSet attrs) { - super(context, attrs); - init(); + public MaskedEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); - TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.MaskedEditText); - mask = attributes.getString(R.styleable.MaskedEditText_mask); + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.MaskedEditText); + mask = attributes.getString(R.styleable.MaskedEditText_mask); + if (mask == null) mask = ""; allowedChars = attributes.getString(R.styleable.MaskedEditText_allowed_chars); deniedChars = attributes.getString(R.styleable.MaskedEditText_denied_chars); - String representation = attributes.getString(R.styleable.MaskedEditText_char_representation); + String representation = attributes.getString(R.styleable.MaskedEditText_char_representation); - if(representation == null) { - charRepresentation = '#'; - } else { - charRepresentation = representation.charAt(0); - } + if (representation == null) { + charRepresentation = '#'; + } else { + charRepresentation = representation.charAt(0); + } - keepHint = attributes.getBoolean(R.styleable.MaskedEditText_keep_hint, false); + keepHint = attributes.getBoolean(R.styleable.MaskedEditText_keep_hint, false); - cleanUp(); + cleanUp(); - // Ignoring enter key presses - setOnEditorActionListener(new OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId,KeyEvent event) { - switch (actionId) { + // Ignoring enter key presses + setOnEditorActionListener(new OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + switch (actionId) { // case EditorInfo.IME_ACTION_NEXT: - // fixing actionNext + // fixing actionNext // return false; - default: - return true; - } - } - }); - attributes.recycle(); - } - - @Override - public Parcelable onSaveInstanceState() { - final Parcelable superParcellable = super.onSaveInstanceState(); - final Bundle state = new Bundle(); - state.putParcelable("super", superParcellable); - state.putString("text", getRawText()); - state.putBoolean("keepHint", isKeepHint()); - return state; - } - - @Override - public void onRestoreInstanceState(Parcelable state) { - Bundle bundle = (Bundle) state; - keepHint = bundle.getBoolean("keepHint", false); - super.onRestoreInstanceState(((Bundle) state).getParcelable("super")); - final String text = bundle.getString("text"); - - setText(text); - Log.d(TAG, "onRestoreInstanceState: " + text); - } - - @Override - public void setText(CharSequence text, BufferType type) { + default: + return true; + } + } + }); + attributes.recycle(); + } + + public MaskedEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + @Override + public Parcelable onSaveInstanceState() { + final Parcelable superParcellable = super.onSaveInstanceState(); + final Bundle state = new Bundle(); + state.putParcelable("super", superParcellable); + state.putString("text", getRawText()); + state.putBoolean("keepHint", isKeepHint()); + return state; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + Bundle bundle = (Bundle) state; + keepHint = bundle.getBoolean("keepHint", false); + super.onRestoreInstanceState(((Bundle) state).getParcelable("super")); + final String text = bundle.getString("text"); + + setText(text); + Log.d(TAG, "onRestoreInstanceState: " + text); + } + + @Override + public void setText(CharSequence text, BufferType type) { // if (text == null || text.equals("")) return; - super.setText(text, type); - } + super.setText(text, type); + } - /** @param listener - its onFocusChange() method will be called before performing MaskedEditText operations, - * related to this event. */ - @Override - public void setOnFocusChangeListener(OnFocusChangeListener listener) { - focusChangeListener = listener; - } + /** + * @param listener - its onFocusChange() method will be called before performing MaskedEditText operations, + * related to this event. + */ + @Override + public void setOnFocusChangeListener(OnFocusChangeListener listener) { + focusChangeListener = listener; + } - private void cleanUp() { - initialized = false; + private void cleanUp() { + initialized = false; - generatePositionArrays(); + generatePositionArrays(); - rawText = new RawText(); - selection = rawToMask[0]; + if (rawToMask.length == 0) return; + rawText = new RawText(); + selection = rawToMask[0]; - editingBefore = true; - editingOnChanged = true; - editingAfter = true; - if(hasHint() && rawText.length() == 0) { + editingBefore = true; + editingOnChanged = true; + editingAfter = true; + if (hasHint() && rawText.length() == 0) { this.setText(makeMaskedTextWithHint()); - } else { + } else { this.setText(makeMaskedText()); - } - editingBefore = false; - editingOnChanged = false; - editingAfter = false; - - maxRawLength = maskToRaw[previousValidPosition(mask.length() - 1)] + 1; - lastValidMaskPosition = findLastValidMaskPosition(); - initialized = true; - - super.setOnFocusChangeListener(new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (focusChangeListener != null) { - focusChangeListener.onFocusChange(v, hasFocus); - } - - if (hasFocus()) { - selectionChanged = false; - MaskedEditText.this.setSelection(lastValidPosition()); - } - } - }); - } - - private int findLastValidMaskPosition() { - for(int i = maskToRaw.length - 1; i >= 0; i--) { - if(maskToRaw[i] != -1) return i; - } - throw new RuntimeException("Mask must contain at least one representation char"); - } - - private boolean hasHint() { - return getHint() != null; - } - - public MaskedEditText(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - public void setMask(String mask) { - this.mask = mask; - cleanUp(); - } - - public String getMask() { - return this.mask; - } - - public String getRawText() { - return this.rawText.getText(); - } - - public void setCharRepresentation(char charRepresentation) { - this.charRepresentation = charRepresentation; - cleanUp(); - } - - public char getCharRepresentation() { - return this.charRepresentation; - } + } + editingBefore = false; + editingOnChanged = false; + editingAfter = false; + + maxRawLength = maskToRaw[previousValidPosition(mask.length() - 1)] + 1; + lastValidMaskPosition = findLastValidMaskPosition(); + initialized = true; + + super.setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (focusChangeListener != null) { + focusChangeListener.onFocusChange(v, hasFocus); + } + + if (hasFocus()) { + selectionChanged = false; + MaskedEditText.this.setSelection(lastValidPosition()); + } + } + }); + } + + private int findLastValidMaskPosition() { + for (int i = maskToRaw.length - 1; i >= 0; i--) { + if (maskToRaw[i] != -1) return i; + } + throw new RuntimeException("Mask must contain at least one representation char"); + } + + private boolean hasHint() { + return getHint() != null; + } + + public String getAllowedChars() { + return this.allowedChars; + } + + public void setAllowedChars(String allowedChars) { + this.allowedChars = allowedChars; + } + + public String getDeniedChars() { + return this.deniedChars; + } + + public void setDeniedChars(String deniedChars) { + this.deniedChars = deniedChars; + } + + public String getMask() { + return this.mask; + } + + public void setMask(@NonNull String mask) { + //noinspection ConstantConditions + if (mask == null) throw new NullPointerException("Mask can't be null"); + this.mask = mask; + cleanUp(); + } + + public String getRawText() { + return this.rawText.getText(); + } + + public char getCharRepresentation() { + return this.charRepresentation; + } + + public void setCharRepresentation(char charRepresentation) { + this.charRepresentation = charRepresentation; + cleanUp(); + } /** - * Generates positions for values characters. For instance: - * Input data: mask = "+7(###)###-##-## - * After method execution: - * rawToMask = [3, 4, 5, 6, 8, 9, 11, 12, 14, 15] - * maskToRaw = [-1, -1, -1, 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, -1, 8, 9] - * charsInMask = "+7()- " (and space, yes) + * Generates positions for values characters. For instance: + * Input data: mask = "+7(###)###-##-## + * After method execution: + * rawToMask = [3, 4, 5, 6, 8, 9, 11, 12, 14, 15] + * maskToRaw = [-1, -1, -1, 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, -1, 8, 9] + * charsInMask = "+7()- " (and space, yes) */ - private void generatePositionArrays() { - int[] aux = new int[mask.length()]; - maskToRaw = new int[mask.length()]; - String charsInMaskAux = ""; - - int charIndex = 0; - for(int i = 0; i < mask.length(); i++) { - char currentChar = mask.charAt(i); - if(currentChar == charRepresentation) { - aux[charIndex] = i; - maskToRaw[i] = charIndex++; - } - else { - String charAsString = Character.toString(currentChar); - if(!charsInMaskAux.contains(charAsString)) { - charsInMaskAux = charsInMaskAux.concat(charAsString); - } - maskToRaw[i] = -1; - } - } - if(charsInMaskAux.indexOf(' ') < 0) { - charsInMaskAux = charsInMaskAux + SPACE; - } - - char[] charsInMask = charsInMaskAux.toCharArray(); - - rawToMask = new int[charIndex]; - for (int i = 0; i < charIndex; i++) { - rawToMask[i] = aux[i]; - } - } - - private void init() { - addTextChangedListener(this); - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, - int after) { - if(!editingBefore) { - editingBefore = true; - if(start > lastValidMaskPosition) { - ignore = true; - } - int rangeStart = start; - if(after == 0) { - rangeStart = erasingStart(start); - } - Range range = calculateRange(rangeStart, start + count); - if(range.getStart() != -1) { - rawText.subtractFromString(range); - } - if(count > 0) { - selection = previousValidPosition(start); - } - } - } - - private int erasingStart(int start) { - while(start > 0 && maskToRaw[start] == -1) { - start--; - } - return start; - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - if(!editingOnChanged && editingBefore) { - editingOnChanged = true; - if(ignore) { - return; - } - if(count > 0) { - int startingPosition = maskToRaw[nextValidPosition(start)]; - String addedString = s.subSequence(start, start + count).toString(); - count = rawText.addToString(clear(addedString), startingPosition, maxRawLength); - if(initialized) { - int currentPosition; - if(startingPosition + count < rawToMask.length) - currentPosition = rawToMask[startingPosition + count]; - else - currentPosition = lastValidMaskPosition + 1; - selection = nextValidPosition(currentPosition); - } - } - } - } - - @Override - public void afterTextChanged(Editable s) { - if(!editingAfter && editingBefore && editingOnChanged) { - editingAfter = true; + private void generatePositionArrays() { + int[] aux = new int[mask.length()]; + maskToRaw = new int[mask.length()]; + String charsInMaskAux = ""; + + int charIndex = 0; + for (int i = 0; i < mask.length(); i++) { + char currentChar = mask.charAt(i); + if (currentChar == charRepresentation) { + aux[charIndex] = i; + maskToRaw[i] = charIndex++; + } else { + String charAsString = Character.toString(currentChar); + if (!charsInMaskAux.contains(charAsString)) { + charsInMaskAux = charsInMaskAux.concat(charAsString); + } + maskToRaw[i] = -1; + } + } + if (charsInMaskAux.indexOf(' ') < 0) { + charsInMaskAux = charsInMaskAux + SPACE; + } + + char[] charsInMask = charsInMaskAux.toCharArray(); + + rawToMask = new int[charIndex]; + for (int i = 0; i < charIndex; i++) { + rawToMask[i] = aux[i]; + } + } + + private void init() { + addTextChangedListener(this); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + if (!editingBefore) { + editingBefore = true; + if (start > lastValidMaskPosition) { + ignore = true; + } + int rangeStart = start; + if (after == 0) { + rangeStart = erasingStart(start); + } + Range range = calculateRange(rangeStart, start + count); + if (range.getStart() != -1) { + rawText.subtractFromString(range); + } + if (count > 0) { + selection = previousValidPosition(start); + } + } + } + + private int erasingStart(int start) { + while (start > 0 && maskToRaw[start] == -1) { + start--; + } + return start; + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (!editingOnChanged && editingBefore) { + editingOnChanged = true; + if (ignore) { + return; + } + if (count > 0) { + int startingPosition = maskToRaw[nextValidPosition(start)]; + String addedString = s.subSequence(start, start + count).toString(); + count = rawText.addToString(clear(addedString), startingPosition, maxRawLength); + if (initialized) { + int currentPosition; + if (startingPosition + count < rawToMask.length) + currentPosition = rawToMask[startingPosition + count]; + else + currentPosition = lastValidMaskPosition + 1; + selection = nextValidPosition(currentPosition); + } + } + } + } + + @Override + public void afterTextChanged(Editable s) { + if (!editingAfter && editingBefore && editingOnChanged) { + editingAfter = true; if (hasHint() && (keepHint || rawText.length() == 0)) { setText(makeMaskedTextWithHint()); - } else { + } else { setText(makeMaskedText()); } - selectionChanged = false; - setSelection(selection); + selectionChanged = false; + setSelection(selection); - editingBefore = false; - editingOnChanged = false; - editingAfter = false; - ignore = false; - } - } + editingBefore = false; + editingOnChanged = false; + editingAfter = false; + ignore = false; + } + } - public boolean isKeepHint() { - return keepHint; - } + public boolean isKeepHint() { + return keepHint; + } - public void setKeepHint(boolean keepHint) { - this.keepHint = keepHint; - setText(getRawText()); - } + public void setKeepHint(boolean keepHint) { + this.keepHint = keepHint; + setText(getRawText()); + } - @Override - protected void onSelectionChanged(int selStart, int selEnd) { - // On Android 4+ this method is being called more than 1 time if there is a hint in the EditText, what moves the cursor to left - // Using the boolean var selectionChanged to limit to one execution + @Override + protected void onSelectionChanged(int selStart, int selEnd) { + // On Android 4+ this method is being called more than 1 time if there is a hint in the EditText, what moves the cursor to left + // Using the boolean var selectionChanged to limit to one execution - if(initialized ){ - if(!selectionChanged) { + if (initialized) { + if (!selectionChanged) { selStart = fixSelection(selStart); selEnd = fixSelection(selEnd); - // exactly in this order. If getText.length() == 0 then selStart will be -1 - if (selStart > getText().length()) selStart = getText().length(); - if (selStart < 0) selStart = 0; - - // exactly in this order. If getText.length() == 0 then selEnd will be -1 - if (selEnd > getText().length()) selEnd = getText().length(); - if (selEnd < 0) selEnd = 0; - - setSelection(selStart, selEnd); - selectionChanged = true; - } else{ - //check to see if the current selection is outside the already entered text - if(selStart > rawText.length() - 1){ - final int start = fixSelection(selStart); - final int end = fixSelection(selEnd); - if (start >= 0 && end < getText().length()){ - setSelection(start, end); - } - } - } - } - super.onSelectionChanged(selStart, selEnd); - } - - private int fixSelection(int selection) { - if(selection > lastValidPosition()) { - return lastValidPosition(); - } else { - return nextValidPosition(selection); - } - } - - private int nextValidPosition(int currentPosition) { - while(currentPosition < lastValidMaskPosition && maskToRaw[currentPosition] == -1) { - currentPosition++; - } - if(currentPosition > lastValidMaskPosition) return lastValidMaskPosition + 1; - return currentPosition; - } - - private int previousValidPosition(int currentPosition) { - while(currentPosition >= 0 && maskToRaw[currentPosition] == -1) { - currentPosition--; - if(currentPosition < 0) { - return nextValidPosition(0); - } - } - return currentPosition; - } - - private int lastValidPosition() { - if(rawText.length() == maxRawLength) { - return rawToMask[rawText.length() - 1] + 1; - } - return nextValidPosition(rawToMask[rawText.length()]); - } - - - private String makeMaskedText() { + // exactly in this order. If getText.length() == 0 then selStart will be -1 + if (selStart > getText().length()) selStart = getText().length(); + if (selStart < 0) selStart = 0; + + // exactly in this order. If getText.length() == 0 then selEnd will be -1 + if (selEnd > getText().length()) selEnd = getText().length(); + if (selEnd < 0) selEnd = 0; + + setSelection(selStart, selEnd); + selectionChanged = true; + } else { + //check to see if the current selection is outside the already entered text + if (selStart > rawText.length() - 1) { + final int start = fixSelection(selStart); + final int end = fixSelection(selEnd); + if (start >= 0 && end < getText().length()) { + setSelection(start, end); + } + } + } + } + super.onSelectionChanged(selStart, selEnd); + } + + private int fixSelection(int selection) { + if (selection > lastValidPosition()) { + return lastValidPosition(); + } else { + return nextValidPosition(selection); + } + } + + private int nextValidPosition(int currentPosition) { + while (currentPosition < lastValidMaskPosition && maskToRaw[currentPosition] == -1) { + currentPosition++; + } + if (currentPosition > lastValidMaskPosition) return lastValidMaskPosition + 1; + return currentPosition; + } + + private int previousValidPosition(int currentPosition) { + while (currentPosition >= 0 && maskToRaw[currentPosition] == -1) { + currentPosition--; + if (currentPosition < 0) { + return nextValidPosition(0); + } + } + return currentPosition; + } + + private int lastValidPosition() { + if (rawText.length() == maxRawLength) { + return rawToMask[rawText.length() - 1] + 1; + } + return nextValidPosition(rawToMask[rawText.length()]); + } + + + private String makeMaskedText() { int maskedTextLength; if (rawText.length() < rawToMask.length) { maskedTextLength = rawToMask[rawText.length()]; } else { maskedTextLength = mask.length(); } - char[] maskedText = new char[maskedTextLength]; //mask.replace(charRepresentation, ' ').toCharArray(); + char[] maskedText = new char[maskedTextLength]; //mask.replace(charRepresentation, ' ').toCharArray(); for (int i = 0; i < maskedText.length; i++) { int rawIndex = maskToRaw[i]; if (rawIndex == -1) { @@ -404,14 +425,14 @@ private String makeMaskedText() { maskedText[i] = rawText.charAt(rawIndex); } } - return new String(maskedText); - } + return new String(maskedText); + } private CharSequence makeMaskedTextWithHint() { SpannableStringBuilder ssb = new SpannableStringBuilder(); int mtrv; int maskFirstChunkEnd = rawToMask[0]; - for(int i = 0; i < mask.length(); i++) { + for (int i = 0; i < mask.length(); i++) { mtrv = maskToRaw[i]; if (mtrv != -1) { if (mtrv < rawText.length()) { @@ -430,40 +451,40 @@ private CharSequence makeMaskedTextWithHint() { return ssb; } - private Range calculateRange(int start, int end) { - Range range = new Range(); - for(int i = start; i <= end && i < mask.length(); i++) { - if(maskToRaw[i] != -1) { - if(range.getStart() == -1) { - range.setStart(maskToRaw[i]); - } - range.setEnd(maskToRaw[i]); - } - } - if(end == mask.length()) { - range.setEnd(rawText.length()); - } - if(range.getStart() == range.getEnd() && start < end) { - int newStart = previousValidPosition(range.getStart() - 1); - if(newStart < range.getStart()) { - range.setStart(newStart); - } - } - return range; - } - - private String clear(String string) { - if (deniedChars != null){ - for(char c: deniedChars.toCharArray()){ - string = string.replace(Character.toString(c), ""); + private Range calculateRange(int start, int end) { + Range range = new Range(); + for (int i = start; i <= end && i < mask.length(); i++) { + if (maskToRaw[i] != -1) { + if (range.getStart() == -1) { + range.setStart(maskToRaw[i]); + } + range.setEnd(maskToRaw[i]); + } + } + if (end == mask.length()) { + range.setEnd(rawText.length()); + } + if (range.getStart() == range.getEnd() && start < end) { + int newStart = previousValidPosition(range.getStart() - 1); + if (newStart < range.getStart()) { + range.setStart(newStart); + } + } + return range; + } + + private String clear(String string) { + if (deniedChars != null) { + for (char c : deniedChars.toCharArray()) { + string = string.replace(Character.toString(c), ""); } } - if (allowedChars != null){ + if (allowedChars != null) { StringBuilder builder = new StringBuilder(string.length()); - for(char c: string.toCharArray() ){ - if (allowedChars.contains(String.valueOf(c) )){ + for (char c : string.toCharArray()) { + if (allowedChars.contains(String.valueOf(c))) { builder.append(c); } } @@ -471,6 +492,6 @@ private String clear(String string) { string = builder.toString(); } - return string; - } + return string; + } } diff --git a/README.md b/README.md index 25f8c30..a702a74 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # MaskedEditText -[![Download](https://api.bintray.com/packages/egorenkov/maven/edittext-mask/images/download.svg) ](https://bintray.com/egorenkov/maven/edittext-mask/_latestVersion) [![Build Status](https://travis-ci.org/egslava/edittext-mask.svg?branch=master)](https://travis-ci.org/egslava/edittext-mask) +[![](https://jitpack.io/v/edwardstock/edittext-mask.svg)](https://jitpack.io/#edwardstock/edittext-mask) ![MaskedEditText - the library for masked input of phone numbers, social security numbers and so on for Android](publish/README.gif) @@ -23,17 +23,22 @@ MaskedEditText is a simple Android EditText with customizable input mask support For instance, you need user specified his phone in format +7(XXX)XXX-XX-XX. You also know user should have the only possibility to write digits but minuses, brackets and "+7" should appear automatically. ### Usage - Add this to your `build.gradle` : ```groovy -compile 'ru.egslava:MaskedEditText:1.0.5' +compile 'com.github.edwardstock:edittext-mask:1.0.7' ``` + +Or for android gradle plugin >= 3.0 (beta or preview) +```groovy +implementation 'com.github.edwardstock:edittext-mask:1.0.7' +``` + Or download project and plug it in as a library. Add _xmlns:mask="http://schemas.android.com/apk/res-auto"_ to your layout xml root: - - + mask:keep_hint="true" + /> +``` + Where _mask_ is the input mask you want and '#' is an editable position (will be replaced by a whitespace on screen). You can optionally set the representation character (in case you don't want to use '#'): - - - +``` You can also change the mask and the representation character programatically: - - MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text) - // Setting the representation character to '$' - editText.setCharRepresentation('$'); - // Logging the representation character - Log.i("Representation character", editText.getCharRepresentation()); - // Setting the mask - editText.setMask("##/##/####"); - // Logging the mask - Log.i("Mask", editText.getMask()); - +```java +import br.com.sapereaude.maskedEditText.MakedEditText; + +class MyActivity extends Activity { + + void onCreate(Bundle savedInstance) { + + MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text); + // Setting the representation character to '$' + editText.setCharRepresentation('$'); + // Logging the representation character + Log.i("Representation character", editText.getCharRepresentation()); + // Setting the mask + editText.setMask("##/##/####"); + // Settings allowed chars + editText.setAllowedChars("0123456789"); + // Settings denied chars + editText.setDeniedChars("_ "); // underscore, whitespace + // Logging the mask + Log.i("Mask", editText.getMask()); + + } +} +``` ************************************************************************************************* ## ru_RU @@ -78,7 +98,7 @@ MaskedEditText - это всего лишь EditText, но с возможнос Вписать в `build.gradle`: ```groovy -compile 'ru.egslava:MaskedEditText:1.0.5' +compile 'com.github.edwardstock:edittext-mask:1.0.7' ``` или скачать проект и подключить как библиотеку. @@ -93,7 +113,7 @@ compile 'ru.egslava:MaskedEditText:1.0.5' mask:allowed_chars="1234567890" mask:mask="+7(###)###-##-##" android:hint="1234567890" - app:keep_hint="true" + mask:keep_hint="true" /> _mask_ задаёт требуемую маску, символ '#' задаёт редактируемую позицию (и будет заменён на пробел на экране). @@ -108,13 +128,28 @@ _mask_ задаёт требуемую маску, символ '#' задаёт /> Кроме того, всё тоже самое можно сделать и программно: - - MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text) - // Setting the representation character to '$' - editText.setCharRepresentation('$'); - // Logging the representation character - Log.i("Representation character", editText.getCharRepresentation()); - // Setting the mask - editText.setMask("##/##/####"); - // Logging the mask - Log.i("Mask", editText.getMask()); +```java +import br.com.sapereaude.maskedEditText.MakedEditText; + +class MyActivity extends Activity { + + void onCreate(Bundle savedInstance) { + + MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text); + // Setting the representation character to '$' + editText.setCharRepresentation('$'); + // Logging the representation character + Log.i("Representation character", editText.getCharRepresentation()); + // Setting the mask + editText.setMask("##/##/####"); + // Settings allowed chars + editText.setAllowedChars("0123456789"); + // Settings denied chars + editText.setDeniedChars("_ "); // underscore, whitespace + // Logging the mask + Log.i("Mask", editText.getMask()); + + } +} +``` + diff --git a/app/build.gradle b/app/build.gradle index 0a82ff0..9971e73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,13 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion '25.0.2' + compileSdkVersion 26 + buildToolsVersion '26.0.1' defaultConfig { applicationId "ru.egslava.edittextphonenumber" - minSdkVersion 9 - targetSdkVersion 25 + minSdkVersion 14 + targetSdkVersion 26 versionCode 1 versionName "1.0.0" testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' @@ -21,10 +21,10 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.2.0' - compile project(':MaskedEditText') - androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:26.0.1' + implementation project(':MaskedEditText') + androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' } } diff --git a/app/src/main/java/ru/egslava/edittextphonenumber/MainActivity.java b/app/src/main/java/ru/egslava/edittextphonenumber/MainActivity.java index 81443b0..350d7b5 100644 --- a/app/src/main/java/ru/egslava/edittextphonenumber/MainActivity.java +++ b/app/src/main/java/ru/egslava/edittextphonenumber/MainActivity.java @@ -1,24 +1,19 @@ package ru.egslava.edittextphonenumber; -import android.support.v7.app.ActionBarActivity; import android.os.Bundle; -import android.text.InputFilter; -import android.text.Spanned; +import android.support.v7.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; -import android.view.View; -import android.widget.EditText; - import br.com.sapereaude.maskedEditText.MaskedEditText; -public class MainActivity extends ActionBarActivity { +public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - MaskedEditText phone = (MaskedEditText)findViewById(R.id.phone_input); + MaskedEditText phone = findViewById(R.id.phone_input); } @Override diff --git a/build.gradle b/build.gradle index 0b9dbcf..b9d34aa 100644 --- a/build.gradle +++ b/build.gradle @@ -3,19 +3,16 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' -// classpath 'com.github.dcendents:android-maven-plugin:1.2' - // https://mvnrepository.com/artifact/com.github.dcendents/android-maven-gradle-plugin + classpath 'com.android.tools.build:gradle:3.0.0-beta2' classpath group: 'com.github.dcendents', name: 'android-maven-gradle-plugin', version: '1.5' -// classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } allprojects { repositories { jcenter() + google() } } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fab72e1..198fa73 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Apr 10 15:27:10 PDT 2013 +#Wed Aug 16 12:33:31 MSK 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-rc-1-all.zip