Repository: egslava/edittext-mask Branch: master Commit: 0228a1c3791b Files: 38 Total size: 56.5 KB Directory structure: gitextract_1lvie_ry/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── MaskedEditText/ │ ├── build.gradle │ └── src/ │ ├── androidTest/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── ru/ │ │ │ └── egslava/ │ │ │ └── lib_phone/ │ │ │ ├── MainActivityTest.java │ │ │ ├── TestActivity.java │ │ │ └── actions/ │ │ │ ├── HintViewAction.java │ │ │ ├── KeepHintViewAction.java │ │ │ └── SetTextViewAction.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ └── values/ │ │ └── styles.xml │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── br/ │ │ └── com/ │ │ └── sapereaude/ │ │ └── maskedEditText/ │ │ ├── MaskedEditText.java │ │ ├── Range.java │ │ └── RawText.java │ └── res/ │ └── values/ │ └── attrs.xml ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── ru/ │ │ └── egslava/ │ │ └── edittextphonenumber/ │ │ └── ApplicationTest.java │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── ru/ │ │ └── egslava/ │ │ └── edittextphonenumber/ │ │ └── MainActivity.java │ └── res/ │ ├── layout/ │ │ └── activity_main.xml │ ├── menu/ │ │ └── main.xml │ ├── values/ │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-ru/ │ │ └── strings.xml │ └── values-w820dp/ │ └── dimens.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ ##### Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm ##### ##### (thanks to https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore) ##### /*.iml *.iml */build ## Directory-based project format: .idea/ # if you remove the above rule, at least ignore the following: # User-specific stuff: # .idea/workspace.xml # .idea/tasks.xml # .idea/dictionaries # Sensitive or high-churn files: # .idea/dataSources.ids # .idea/dataSources.xml # .idea/sqlDataSources.xml # .idea/dynamic.xml # .idea/uiDesigner.xml # Gradle: # .idea/gradle.xml # .idea/libraries # Mongo Explorer plugin: # .idea/mongoSettings.xml ## File-based project format: *.ipr *.iws ## Plugin-specific files: # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml ###### Android Studio generated ##### .gradle /local.properties /.idea/workspace.xml .DS_Store /build # Built application files *.apk *.ap_ # Files for the Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ ####### And standard github .gitignore for Android ##### # Proguard folder generated by Eclipse proguard/ # Log Files *.log ================================================ FILE: .travis.yml ================================================ language: android jdk: oraclejdk8 sudo: false android: components: - platform-tools - tools - build-tools-24.0.2 - build-tools-25.0.2 - android-22 - android-24 - android-25 - sys-img-armeabi-v7a-android-22 - sys-img-armeabi-v7a-android-25 - extra-android-m2repository # # Uncomment the lines below if you want to # # use the latest revision of Android SDK Tools # - platform-tools # - tools # # # The BuildTools version used by your project # - build-tools-25.0.2 # # # The SDK version used to compile your project # - android-25 # # # Additional components # - extra-google-google_play_services # - extra-google-m2repository # - extra-android-m2repository # - addon-google_apis-google-25 # # # Specify at least one system image, # # if you need to run emulator(s) during your tests # - sys-img-armeabi-v7a-android-22 # - sys-img-armeabi-v7a-android-16 before_script: # Create and start emulator - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a - emulator -avd test -no-skin -no-audio -no-window & - android-wait-for-emulator - adb shell input keyevent 82 & script: ./gradlew -PbintrayUser=blah -PbintrayApikey=blah connectedAndroidTest ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2014 Slava Egorenkov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MaskedEditText/build.gradle ================================================ plugins { id "com.jfrog.bintray" version "1.7.3" } apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' String projectVersion = "1.0.5" String projectGroup = "ru.egslava" version = projectVersion group = projectGroup android { compileSdkVersion 25 buildToolsVersion '25.0.2' defaultConfig { minSdkVersion 9 targetSdkVersion 25 versionCode 1 versionName projectVersion testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } } dependencies { androidTestCompile('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' testCompile '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" install { repositories.mavenInstaller { pom { project { packaging 'aar' name projectName description projectDescription url webUrl inceptionYear '2017' // HARDCODED licenses { license { name 'MIT' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' } } scm { connection gitUrl developerConnection gitUrl url webUrl } developers { developer { id 'egslava' name 'Slava Egorenkov' email 'egslava@gmail.com' } } } } } } // The end of the gradle file is for JCenter publication. // The original code was written with look at Alexander Matveychuk's code // and // https://www.virag.si/2015/01/publishing-gradle-android-library-to-jcenter/ task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } task javadoc(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } artifacts { archives javadocJar 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 } } } ================================================ FILE: MaskedEditText/src/androidTest/AndroidManifest.xml ================================================ ================================================ FILE: MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/MainActivityTest.java ================================================ package ru.egslava.lib_phone; import android.content.pm.ActivityInfo; import android.support.test.espresso.action.ViewActions; import android.support.test.filters.Suppress; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.LargeTest; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.security.InvalidParameterException; import ru.egslava.lib_phone.actions.HintViewAction; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.clearText; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard; import static android.support.test.espresso.action.ViewActions.typeText; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static ru.egslava.lib_phone.actions.KeepHintViewAction.dontKeepHints; import static ru.egslava.lib_phone.actions.KeepHintViewAction.keepHints; @LargeTest @RunWith(AndroidJUnit4.class) public class MainActivityTest { public static final int phone_input = br.com.sapereaude.maskedEditText.test.R.id.vControl; @Rule public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(TestActivity.class); @Test public void textTypingTest() { onView( allOf( withId(phone_input), isDisplayed()) ).perform(click()); // just in case, check again that it's the same view after the click onView( allOf( withId(phone_input), isDisplayed()) ).perform(typeText("9997055671")); onView( allOf( withId(phone_input), withText("+7(999)705-56-71"), isDisplayed()) ).perform(closeSoftKeyboard()); onView( allOf( withId(phone_input), isDisplayed()) ).check(matches(withText("+7(999)705-56-71"))); } @Test public void saveInstanceStateTest() { onView( allOf( withId(phone_input), isDisplayed()) ).perform(click()); // just in case, check again that it's the same view after the click onView( allOf( withId(phone_input), isDisplayed()) ).perform(typeText("9997055671")); onView( allOf( withId(phone_input), withText("+7(999)705-56-71"), isDisplayed()) ).perform(closeSoftKeyboard()); mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); onView( allOf( withId(phone_input), isDisplayed()) ).check(matches(withText("+7(999)705-56-71"))); } /** * After merging with Alexander Matveychuk, the demo app started to crash * if there's no any text and you rotate the phone */ @Test public void saveInstanceStateTest2() { mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } /** * After setKeepHint(true) a hint should appeared. * After setKeepHint(false) a hint should disappear. */ @Test public void setKeepHintTest() { onView(withId(phone_input)) .perform(new HintViewAction("9997055671")) .perform(dontKeepHints) // just to be sure // the first check. After setKeepHint(true) the hint should appear immediate .perform(ViewActions.typeText("999")) .perform(keepHints) .check(matches(withText("+7(999)705-56-71"))) // the second check. After setKeepHint(false) the hint should disappear immediate .perform(dontKeepHints) .check(matches(withText("+7(999)"))); } /** * If the text is empty changing of keepHint can lead to a crash. * It's the regression test */ @Test public void setEmptyTextTest() { // given onView(withId(phone_input)) .perform(new HintViewAction("9997055671")) // tests .perform(dontKeepHints) .check(matches(withText("+7(999)705-56-71"))) .perform(keepHints) .check(matches(withText("+7(999)705-56-71"))) .perform(dontKeepHints) // YES! Because the text is empty, user need to see a hint .check(matches(withText("+7(999)705-56-71"))); } /** * It should keep state of keepHint after activity recreation :-/ * It's the regression test */ @Test @Suppress // TODO public void keepHintAfterRotationTest() throws InterruptedException { // ====================================== // if initial state was keepHint(false) // given onView(withId(phone_input)) .perform(new HintViewAction("9997055671")) .perform(keepHints) .perform(typeText("999")) .check(matches(withText("+7(999)705-56-71"))) .perform(dontKeepHints); // rotating screen final TestActivity a1 = mActivityTestRule.getActivity(); a1.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); Thread.sleep(2500); final TestActivity a2 = mActivityTestRule.getActivity(); a2.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); Thread.sleep(2500); if (a1 != a2) { throw new InvalidParameterException("a1 != a2"); } // tests onView(withId(phone_input)) .check(matches(withText("+7(999)"))); // ====================================== // and if initial state was keepHint(true) onView(withId(phone_input)) .perform(clearText()) // after previous test .perform(new HintViewAction("1234567890")) .perform(keepHints) .perform(typeText("999")) .check(matches(withText("+7(999)456-78-90"))) ; // rotating screen mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); Thread.sleep(5000); mActivityTestRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); Thread.sleep(2500); // tests onView(withId(phone_input)) .check(matches(withText("+7(999)456-78-90"))); } } ================================================ FILE: MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/TestActivity.java ================================================ package ru.egslava.lib_phone; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; /** * Created by egslava on 04/03/2017. */ public class TestActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(br.com.sapereaude.maskedEditText.test.R.layout.activity_main); } } ================================================ FILE: MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/actions/HintViewAction.java ================================================ package ru.egslava.lib_phone.actions; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.view.View; import android.widget.TextView; import org.hamcrest.Matcher; import org.hamcrest.Matchers; public class HintViewAction implements ViewAction { String hint; public HintViewAction(String hint) { this.hint = hint; } @Override public Matcher getConstraints() { return Matchers.instanceOf(TextView.class); } @Override public String getDescription() { return "Set hints on view"; } @Override public void perform(UiController uiController, View view) { ((TextView)view).setHint(hint); } } ================================================ FILE: MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/actions/KeepHintViewAction.java ================================================ package ru.egslava.lib_phone.actions; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.view.View; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import br.com.sapereaude.maskedEditText.MaskedEditText; public enum KeepHintViewAction implements ViewAction { keepHints(true), dontKeepHints(false); boolean keepHint; KeepHintViewAction(boolean keepHint) { this.keepHint = keepHint; } @Override public Matcher getConstraints() { return Matchers.instanceOf(MaskedEditText.class); } @Override public String getDescription() { return "Set keepHint on/off"; } @Override public void perform(UiController uiController, View view) { ((MaskedEditText)view).setKeepHint(keepHint); } } ================================================ FILE: MaskedEditText/src/androidTest/java/ru/egslava/lib_phone/actions/SetTextViewAction.java ================================================ package ru.egslava.lib_phone.actions; import android.support.test.espresso.UiController; import android.support.test.espresso.ViewAction; import android.view.View; import org.hamcrest.Matcher; import br.com.sapereaude.maskedEditText.MaskedEditText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.instanceOf; public class SetTextViewAction implements ViewAction { private String value; public SetTextViewAction(String value) { this.value = value; } @SuppressWarnings("unchecked") @Override public Matcher getConstraints() { return allOf(instanceOf(MaskedEditText.class)); } @Override public void perform(UiController uiController, View view) { MaskedEditText maskedEditText = (MaskedEditText) view; maskedEditText.setText(maskedEditText.getRawText()); } @Override public String getDescription() { return "replace text"; } }; ================================================ FILE: MaskedEditText/src/androidTest/res/layout/activity_main.xml ================================================ ================================================ FILE: MaskedEditText/src/androidTest/res/values/styles.xml ================================================ ================================================ FILE: MaskedEditText/src/main/AndroidManifest.xml ================================================ ================================================ FILE: MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/MaskedEditText.java ================================================ package br.com.sapereaude.maskedEditText; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.text.TextUtilsCompat; import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.SpannableStringBuilder; import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.widget.TextView; import static android.content.ContentValues.TAG; public class MaskedEditText extends AppCompatEditText implements TextWatcher { public static final String SPACE = " "; private final OnEditorActionListener onEditorActionListener = new OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId,KeyEvent event) { switch (actionId) { // case EditorInfo.IME_ACTION_NEXT: // fixing actionNext // return false; default: return true; } } }; 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 String allowedChars; private String deniedChars; private boolean shouldKeepText; public MaskedEditText(Context context) { super(context); 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); allowedChars = attributes.getString(R.styleable.MaskedEditText_allowed_chars); deniedChars = attributes.getString(R.styleable.MaskedEditText_denied_chars); boolean enableImeAction = attributes.getBoolean(R.styleable.MaskedEditText_enable_ime_action, false); String representation = attributes.getString(R.styleable.MaskedEditText_char_representation); if(representation == null) { charRepresentation = '#'; } else { charRepresentation = representation.charAt(0); } keepHint = attributes.getBoolean(R.styleable.MaskedEditText_keep_hint, false); cleanUp(); // Ignoring enter key presses if needed if (!enableImeAction) { setOnEditorActionListener(onEditorActionListener); } else { setOnEditorActionListener(null); } 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) { // if (text == null || text.equals("")) return; 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; } private void cleanUp() { initialized = false; if(mask == null || mask.isEmpty()){ return; } generatePositionArrays(); if (!shouldKeepText || rawText == null) { rawText = new RawText(); selection = rawToMask[0]; } editingBefore = true; editingOnChanged = true; editingAfter = true; if(hasHint() && rawText.length() == 0) { this.setText(makeMaskedTextWithHint()); } 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 setShouldKeepText(boolean shouldKeepText) { this.shouldKeepText = shouldKeepText; } public boolean isKeepingText() { return shouldKeepText; } public void setMask(String mask) { this.mask = mask; cleanUp(); } public String getMask() { return this.mask; } public void setImeActionEnabled(boolean isEnabled) { if (isEnabled) setOnEditorActionListener(onEditorActionListener); else setOnEditorActionListener(null); } public String getRawText() { return this.rawText.getText(); } public void setCharRepresentation(char charRepresentation) { this.charRepresentation = charRepresentation; cleanUp(); } public char getCharRepresentation() { return this.charRepresentation; } /** * 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]; System.arraycopy(aux, 0, rawToMask, 0, charIndex); } 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 { setText(makeMaskedText()); } selectionChanged = false; setSelection(selection); editingBefore = false; editingOnChanged = false; editingAfter = false; ignore = false; } } public boolean isKeepHint() { return keepHint; } 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 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() { int maskedTextLength; if (rawText.length() < rawToMask.length) { maskedTextLength = rawToMask[rawText.length()]; } else { maskedTextLength = mask.length(); } char[] maskedText = new char[maskedTextLength]; //mask.replace(charRepresentation, ' ').toCharArray(); for (int i = 0; i < maskedText.length; i++) { int rawIndex = maskToRaw[i]; if (rawIndex == -1) { maskedText[i] = mask.charAt(i); } else { maskedText[i] = rawText.charAt(rawIndex); } } 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++) { mtrv = maskToRaw[i]; if (mtrv != -1) { if (mtrv < rawText.length()) { ssb.append(rawText.charAt(mtrv)); } else { ssb.append(getHint().charAt(maskToRaw[i])); } } else { ssb.append(mask.charAt(i)); } if ((keepHint && rawText.length() < rawToMask.length && i >= rawToMask[rawText.length()]) || (!keepHint && i >= maskFirstChunkEnd)) { ssb.setSpan(new ForegroundColorSpan(getCurrentHintTextColor()), i, i + 1, 0); } } 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), ""); } } if (allowedChars != null){ StringBuilder builder = new StringBuilder(string.length()); for(char c: string.toCharArray() ){ if (allowedChars.contains(String.valueOf(c) )){ builder.append(c); } } string = builder.toString(); } return string; } } ================================================ FILE: MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/Range.java ================================================ package br.com.sapereaude.maskedEditText; public class Range { private int start; private int end; Range() { start = -1; end = -1; } public int getStart() { return start; } public void setStart(int start) { this.start = start; } public int getEnd() { return end; } public void setEnd(int end) { this.end = end; } } ================================================ FILE: MaskedEditText/src/main/java/br/com/sapereaude/maskedEditText/RawText.java ================================================ package br.com.sapereaude.maskedEditText; /** * Raw text, another words TextWithout mask characters */ public class RawText { private String text; public RawText() { text = ""; } /** * text = 012345678, range = 123 => text = 0456789 * @param range given range */ public void subtractFromString(Range range) { String firstPart = ""; String lastPart = ""; if(range.getStart() > 0 && range.getStart() <= text.length()) { firstPart = text.substring(0, range.getStart()); } if(range.getEnd() >= 0 && range.getEnd() < text.length()) { lastPart = text.substring(range.getEnd(), text.length()); } text = firstPart.concat(lastPart); } /** * * @param newString New String to be added * @param start Position to insert newString * @param maxLength Maximum raw text length * @return Number of added characters */ public int addToString(String newString, int start, int maxLength) { String firstPart = ""; String lastPart = ""; if(newString == null || newString.equals("")) { return 0; } else if(start < 0) { throw new IllegalArgumentException("Start position must be non-negative"); } else if(start > text.length()) { throw new IllegalArgumentException("Start position must be less than the actual text length"); } int count = newString.length(); if(start > 0) { firstPart = text.substring(0, start); } if(start >= 0 && start < text.length()) { lastPart = text.substring(start, text.length()); } if(text.length() + newString.length() > maxLength) { count = maxLength - text.length(); newString = newString.substring(0, count); } text = firstPart.concat(newString).concat(lastPart); return count; } public String getText() { return text; } public int length() { return text.length(); } public char charAt(int position) { return text.charAt(position); } } ================================================ FILE: MaskedEditText/src/main/res/values/attrs.xml ================================================ ================================================ FILE: README.md ================================================ **Announcement**: [let's travel the world](https://github.com/egslava/edittext-mask/issues/65)! or let's just be guests :) # 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) ![MaskedEditText - the library for masked input of phone numbers, social security numbers and so on for Android](publish/README.gif) **Announcement**: [let's travel the world](https://github.com/egslava/edittext-mask/issues/65)! or let's just be guests :) This project derives from [toshikurauchi/MaskedEditText](https://github.com/toshikurauchi/MaskedEditText), but it's been adapted for gradle build system and has additional features: 1. filter allowed chars 2. filter denied chars 3. user can use chars from mask in his input (in original version of this library user couldn't use digit '7' in the '+7(XXX)XXX-XX-XX' pattern). 4. You can keep hints even when user started typing. So it allows you to use masks for phones, urls, etc. Enjoy! Get it on Google Play ********************************* ## en_US 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' ``` Or download project and plug it in as a library. **Announcement**: [let's travel the world](https://github.com/egslava/edittext-mask/issues/65)! or let's just be guests :) Add _xmlns:mask="http://schemas.android.com/apk/res-auto"_ to your layout xml root: 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 '#'): **Announcement**: [let's travel the world](https://github.com/egslava/edittext-mask/issues/65)! or let's just be guests :) 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()); To enable Enter softkey action (IME action): **Announcement**: [let's travel the world](https://github.com/egslava/edittext-mask/issues/65)! or let's just be guests :) Or programmatically: MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text) editText.setImeActionEnabled(true); ************************************************************************************************* ## ru_RU MaskedEditText - это всего лишь EditText, но с возможностью задавать произвольную маску. Например, нужно ввести телефон в формате +7(XXX)XXX-XX-XX. Причём можно ввести только цифры, а скобочки, дефисы и "+7" должны подставляться самостоятельно. ### Использование Вписать в `build.gradle`: ```groovy compile 'ru.egslava:MaskedEditText:1.0.5' ``` или скачать проект и подключить как библиотеку. Добавить _xmlns:mask="http://schemas.android.com/apk/res-auto"_ в корневой элемент файла разметки: _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()); Чтобы включить обработку нажатия Enter (IME action): Или программно: MaskedEditText editText = (MaskedEditText) findViewById(R.id.my_edit_text) editText.setImeActionEnabled(true); ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion '25.0.2' defaultConfig { applicationId "ru.egslava.edittextphonenumber" minSdkVersion 9 targetSdkVersion 25 versionCode 1 versionName "1.0.0" testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } 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', { exclude group: 'com.android.support', module: 'support-annotations' } } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/egslava/apps/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/ru/egslava/edittextphonenumber/ApplicationTest.java ================================================ package ru.egslava.edittextphonenumber; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/ru/egslava/edittextphonenumber/MainActivity.java ================================================ package ru.egslava.edittextphonenumber; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.text.InputFilter; import android.text.Spanned; 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 { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MaskedEditText phone = (MaskedEditText)findViewById(R.id.phone_input); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } } ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: app/src/main/res/menu/main.xml ================================================ ================================================ FILE: app/src/main/res/values/dimens.xml ================================================ 16dp 16dp ================================================ FILE: app/src/main/res/values/strings.xml ================================================ EditTextPhoneNumber Hello world! Settings Please, provide your phone number ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/main/res/values-ru/strings.xml ================================================ EditTextPhoneNumber Hello world! Settings Пожалуйста, укажите свой номер телефона ================================================ FILE: app/src/main/res/values-w820dp/dimens.xml ================================================ 64dp ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } 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 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() } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Apr 10 15:27:10 PDT 2013 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 ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Settings specified in this file will override any Gradle settings # configured through the IDE. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true org.gradle.daemon=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: settings.gradle ================================================ include ':app', ':MaskedEditText'