Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings;
     18 
     19 import com.android.internal.widget.LockPatternUtils;
     20 import com.android.internal.widget.PasswordEntryKeyboardHelper;
     21 import com.android.internal.widget.PasswordEntryKeyboardView;
     22 import com.android.settings.notification.RedactionInterstitial;
     23 
     24 import android.app.Activity;
     25 import android.app.Fragment;
     26 import android.app.admin.DevicePolicyManager;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.content.Intent;
     30 import android.inputmethodservice.KeyboardView;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.provider.Settings;
     35 import android.text.Editable;
     36 import android.text.InputType;
     37 import android.text.Selection;
     38 import android.text.Spannable;
     39 import android.text.TextUtils;
     40 import android.text.TextWatcher;
     41 import android.view.KeyEvent;
     42 import android.view.LayoutInflater;
     43 import android.view.View;
     44 import android.view.ViewGroup;
     45 import android.view.View.OnClickListener;
     46 import android.view.inputmethod.EditorInfo;
     47 import android.widget.Button;
     48 import android.widget.TextView;
     49 import android.widget.TextView.OnEditorActionListener;
     50 
     51 public class ChooseLockPassword extends SettingsActivity {
     52     public static final String PASSWORD_MIN_KEY = "lockscreen.password_min";
     53     public static final String PASSWORD_MAX_KEY = "lockscreen.password_max";
     54     public static final String PASSWORD_MIN_LETTERS_KEY = "lockscreen.password_min_letters";
     55     public static final String PASSWORD_MIN_LOWERCASE_KEY = "lockscreen.password_min_lowercase";
     56     public static final String PASSWORD_MIN_UPPERCASE_KEY = "lockscreen.password_min_uppercase";
     57     public static final String PASSWORD_MIN_NUMERIC_KEY = "lockscreen.password_min_numeric";
     58     public static final String PASSWORD_MIN_SYMBOLS_KEY = "lockscreen.password_min_symbols";
     59     public static final String PASSWORD_MIN_NONLETTER_KEY = "lockscreen.password_min_nonletter";
     60 
     61     @Override
     62     public Intent getIntent() {
     63         Intent modIntent = new Intent(super.getIntent());
     64         modIntent.putExtra(EXTRA_SHOW_FRAGMENT, ChooseLockPasswordFragment.class.getName());
     65         return modIntent;
     66     }
     67 
     68     public static Intent createIntent(Context context, int quality, final boolean isFallback,
     69             int minLength, final int maxLength, boolean requirePasswordToDecrypt,
     70             boolean confirmCredentials) {
     71         Intent intent = new Intent().setClass(context, ChooseLockPassword.class);
     72         intent.putExtra(LockPatternUtils.PASSWORD_TYPE_KEY, quality);
     73         intent.putExtra(PASSWORD_MIN_KEY, minLength);
     74         intent.putExtra(PASSWORD_MAX_KEY, maxLength);
     75         intent.putExtra(ChooseLockGeneric.CONFIRM_CREDENTIALS, confirmCredentials);
     76         intent.putExtra(LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, isFallback);
     77         intent.putExtra(EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, requirePasswordToDecrypt);
     78         return intent;
     79     }
     80 
     81     @Override
     82     protected boolean isValidFragment(String fragmentName) {
     83         if (ChooseLockPasswordFragment.class.getName().equals(fragmentName)) return true;
     84         return false;
     85     }
     86 
     87     @Override
     88     public void onCreate(Bundle savedInstanceState) {
     89         // TODO: Fix on phones
     90         // Disable IME on our window since we provide our own keyboard
     91         //getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
     92                 //WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
     93         super.onCreate(savedInstanceState);
     94         CharSequence msg = getText(R.string.lockpassword_choose_your_password_header);
     95         setTitle(msg);
     96     }
     97 
     98     public static class ChooseLockPasswordFragment extends Fragment
     99             implements OnClickListener, OnEditorActionListener,  TextWatcher {
    100         private static final String KEY_FIRST_PIN = "first_pin";
    101         private static final String KEY_UI_STAGE = "ui_stage";
    102         private TextView mPasswordEntry;
    103         private int mPasswordMinLength = 4;
    104         private int mPasswordMaxLength = 16;
    105         private int mPasswordMinLetters = 0;
    106         private int mPasswordMinUpperCase = 0;
    107         private int mPasswordMinLowerCase = 0;
    108         private int mPasswordMinSymbols = 0;
    109         private int mPasswordMinNumeric = 0;
    110         private int mPasswordMinNonLetter = 0;
    111         private LockPatternUtils mLockPatternUtils;
    112         private int mRequestedQuality = DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
    113         private ChooseLockSettingsHelper mChooseLockSettingsHelper;
    114         private Stage mUiStage = Stage.Introduction;
    115         private boolean mDone = false;
    116         private TextView mHeaderText;
    117         private String mFirstPin;
    118         private KeyboardView mKeyboardView;
    119         private PasswordEntryKeyboardHelper mKeyboardHelper;
    120         private boolean mIsAlphaMode;
    121         private Button mCancelButton;
    122         private Button mNextButton;
    123         private static final int CONFIRM_EXISTING_REQUEST = 58;
    124         static final int RESULT_FINISHED = RESULT_FIRST_USER;
    125         private static final long ERROR_MESSAGE_TIMEOUT = 3000;
    126         private static final int MSG_SHOW_ERROR = 1;
    127 
    128         private Handler mHandler = new Handler() {
    129             @Override
    130             public void handleMessage(Message msg) {
    131                 if (msg.what == MSG_SHOW_ERROR) {
    132                     updateStage((Stage) msg.obj);
    133                 }
    134             }
    135         };
    136 
    137         /**
    138          * Keep track internally of where the user is in choosing a pattern.
    139          */
    140         protected enum Stage {
    141 
    142             Introduction(R.string.lockpassword_choose_your_password_header,
    143                     R.string.lockpassword_choose_your_pin_header,
    144                     R.string.lockpassword_continue_label),
    145 
    146             NeedToConfirm(R.string.lockpassword_confirm_your_password_header,
    147                     R.string.lockpassword_confirm_your_pin_header,
    148                     R.string.lockpassword_ok_label),
    149 
    150             ConfirmWrong(R.string.lockpassword_confirm_passwords_dont_match,
    151                     R.string.lockpassword_confirm_pins_dont_match,
    152                     R.string.lockpassword_continue_label);
    153 
    154             Stage(int hintInAlpha, int hintInNumeric, int nextButtonText) {
    155                 this.alphaHint = hintInAlpha;
    156                 this.numericHint = hintInNumeric;
    157                 this.buttonText = nextButtonText;
    158             }
    159 
    160             public final int alphaHint;
    161             public final int numericHint;
    162             public final int buttonText;
    163         }
    164 
    165         // required constructor for fragments
    166         public ChooseLockPasswordFragment() {
    167 
    168         }
    169 
    170         @Override
    171         public void onCreate(Bundle savedInstanceState) {
    172             super.onCreate(savedInstanceState);
    173             mLockPatternUtils = new LockPatternUtils(getActivity());
    174             Intent intent = getActivity().getIntent();
    175             if (!(getActivity() instanceof ChooseLockPassword)) {
    176                 throw new SecurityException("Fragment contained in wrong activity");
    177             }
    178             mRequestedQuality = Math.max(intent.getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY,
    179                     mRequestedQuality), mLockPatternUtils.getRequestedPasswordQuality());
    180             mPasswordMinLength = Math.max(
    181                     intent.getIntExtra(PASSWORD_MIN_KEY, mPasswordMinLength), mLockPatternUtils
    182                             .getRequestedMinimumPasswordLength());
    183             mPasswordMaxLength = intent.getIntExtra(PASSWORD_MAX_KEY, mPasswordMaxLength);
    184             mPasswordMinLetters = Math.max(intent.getIntExtra(PASSWORD_MIN_LETTERS_KEY,
    185                     mPasswordMinLetters), mLockPatternUtils.getRequestedPasswordMinimumLetters());
    186             mPasswordMinUpperCase = Math.max(intent.getIntExtra(PASSWORD_MIN_UPPERCASE_KEY,
    187                     mPasswordMinUpperCase), mLockPatternUtils.getRequestedPasswordMinimumUpperCase());
    188             mPasswordMinLowerCase = Math.max(intent.getIntExtra(PASSWORD_MIN_LOWERCASE_KEY,
    189                     mPasswordMinLowerCase), mLockPatternUtils.getRequestedPasswordMinimumLowerCase());
    190             mPasswordMinNumeric = Math.max(intent.getIntExtra(PASSWORD_MIN_NUMERIC_KEY,
    191                     mPasswordMinNumeric), mLockPatternUtils.getRequestedPasswordMinimumNumeric());
    192             mPasswordMinSymbols = Math.max(intent.getIntExtra(PASSWORD_MIN_SYMBOLS_KEY,
    193                     mPasswordMinSymbols), mLockPatternUtils.getRequestedPasswordMinimumSymbols());
    194             mPasswordMinNonLetter = Math.max(intent.getIntExtra(PASSWORD_MIN_NONLETTER_KEY,
    195                     mPasswordMinNonLetter), mLockPatternUtils.getRequestedPasswordMinimumNonLetter());
    196 
    197             mChooseLockSettingsHelper = new ChooseLockSettingsHelper(getActivity());
    198         }
    199 
    200         @Override
    201         public View onCreateView(LayoutInflater inflater, ViewGroup container,
    202                 Bundle savedInstanceState) {
    203 
    204             View view = inflater.inflate(R.layout.choose_lock_password, null);
    205 
    206             mCancelButton = (Button) view.findViewById(R.id.cancel_button);
    207             mCancelButton.setOnClickListener(this);
    208             mNextButton = (Button) view.findViewById(R.id.next_button);
    209             mNextButton.setOnClickListener(this);
    210 
    211             mIsAlphaMode = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC == mRequestedQuality
    212                     || DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC == mRequestedQuality
    213                     || DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality;
    214             mKeyboardView = (PasswordEntryKeyboardView) view.findViewById(R.id.keyboard);
    215             mPasswordEntry = (TextView) view.findViewById(R.id.password_entry);
    216             mPasswordEntry.setOnEditorActionListener(this);
    217             mPasswordEntry.addTextChangedListener(this);
    218 
    219             final Activity activity = getActivity();
    220             mKeyboardHelper = new PasswordEntryKeyboardHelper(activity,
    221                     mKeyboardView, mPasswordEntry);
    222             mKeyboardHelper.setKeyboardMode(mIsAlphaMode ?
    223                     PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA
    224                     : PasswordEntryKeyboardHelper.KEYBOARD_MODE_NUMERIC);
    225 
    226             mHeaderText = (TextView) view.findViewById(R.id.headerText);
    227             mKeyboardView.requestFocus();
    228 
    229             int currentType = mPasswordEntry.getInputType();
    230             mPasswordEntry.setInputType(mIsAlphaMode ? currentType
    231                     : (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD));
    232 
    233             Intent intent = getActivity().getIntent();
    234             final boolean confirmCredentials = intent.getBooleanExtra("confirm_credentials", true);
    235             if (savedInstanceState == null) {
    236                 updateStage(Stage.Introduction);
    237                 if (confirmCredentials) {
    238                     mChooseLockSettingsHelper.launchConfirmationActivity(CONFIRM_EXISTING_REQUEST,
    239                             null, null);
    240                 }
    241             } else {
    242                 mFirstPin = savedInstanceState.getString(KEY_FIRST_PIN);
    243                 final String state = savedInstanceState.getString(KEY_UI_STAGE);
    244                 if (state != null) {
    245                     mUiStage = Stage.valueOf(state);
    246                     updateStage(mUiStage);
    247                 }
    248             }
    249             mDone = false;
    250             if (activity instanceof SettingsActivity) {
    251                 final SettingsActivity sa = (SettingsActivity) activity;
    252                 int id = mIsAlphaMode ? R.string.lockpassword_choose_your_password_header
    253                         : R.string.lockpassword_choose_your_pin_header;
    254                 CharSequence title = getText(id);
    255                 sa.setTitle(title);
    256             }
    257 
    258             return view;
    259         }
    260 
    261         @Override
    262         public void onResume() {
    263             super.onResume();
    264             updateStage(mUiStage);
    265             mKeyboardView.requestFocus();
    266         }
    267 
    268         @Override
    269         public void onPause() {
    270             mHandler.removeMessages(MSG_SHOW_ERROR);
    271 
    272             super.onPause();
    273         }
    274 
    275         @Override
    276         public void onSaveInstanceState(Bundle outState) {
    277             super.onSaveInstanceState(outState);
    278             outState.putString(KEY_UI_STAGE, mUiStage.name());
    279             outState.putString(KEY_FIRST_PIN, mFirstPin);
    280         }
    281 
    282         @Override
    283         public void onActivityResult(int requestCode, int resultCode,
    284                 Intent data) {
    285             super.onActivityResult(requestCode, resultCode, data);
    286             switch (requestCode) {
    287                 case CONFIRM_EXISTING_REQUEST:
    288                     if (resultCode != Activity.RESULT_OK) {
    289                         getActivity().setResult(RESULT_FINISHED);
    290                         getActivity().finish();
    291                     }
    292                     break;
    293             }
    294         }
    295 
    296         protected void updateStage(Stage stage) {
    297             final Stage previousStage = mUiStage;
    298             mUiStage = stage;
    299             updateUi();
    300 
    301             // If the stage changed, announce the header for accessibility. This
    302             // is a no-op when accessibility is disabled.
    303             if (previousStage != stage) {
    304                 mHeaderText.announceForAccessibility(mHeaderText.getText());
    305             }
    306         }
    307 
    308         /**
    309          * Validates PIN and returns a message to display if PIN fails test.
    310          * @param password the raw password the user typed in
    311          * @return error message to show to user or null if password is OK
    312          */
    313         private String validatePassword(String password) {
    314             if (password.length() < mPasswordMinLength) {
    315                 return getString(mIsAlphaMode ?
    316                         R.string.lockpassword_password_too_short
    317                         : R.string.lockpassword_pin_too_short, mPasswordMinLength);
    318             }
    319             if (password.length() > mPasswordMaxLength) {
    320                 return getString(mIsAlphaMode ?
    321                         R.string.lockpassword_password_too_long
    322                         : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1);
    323             }
    324             int letters = 0;
    325             int numbers = 0;
    326             int lowercase = 0;
    327             int symbols = 0;
    328             int uppercase = 0;
    329             int nonletter = 0;
    330             for (int i = 0; i < password.length(); i++) {
    331                 char c = password.charAt(i);
    332                 // allow non control Latin-1 characters only
    333                 if (c < 32 || c > 127) {
    334                     return getString(R.string.lockpassword_illegal_character);
    335                 }
    336                 if (c >= '0' && c <= '9') {
    337                     numbers++;
    338                     nonletter++;
    339                 } else if (c >= 'A' && c <= 'Z') {
    340                     letters++;
    341                     uppercase++;
    342                 } else if (c >= 'a' && c <= 'z') {
    343                     letters++;
    344                     lowercase++;
    345                 } else {
    346                     symbols++;
    347                     nonletter++;
    348                 }
    349             }
    350             if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC == mRequestedQuality
    351                     || DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality) {
    352                 if (letters > 0 || symbols > 0) {
    353                     // This shouldn't be possible unless user finds some way to bring up
    354                     // soft keyboard
    355                     return getString(R.string.lockpassword_pin_contains_non_digits);
    356                 }
    357                 // Check for repeated characters or sequences (e.g. '1234', '0000', '2468')
    358                 final int sequence = LockPatternUtils.maxLengthSequence(password);
    359                 if (DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX == mRequestedQuality
    360                         && sequence > LockPatternUtils.MAX_ALLOWED_SEQUENCE) {
    361                     return getString(R.string.lockpassword_pin_no_sequential_digits);
    362                 }
    363             } else if (DevicePolicyManager.PASSWORD_QUALITY_COMPLEX == mRequestedQuality) {
    364                 if (letters < mPasswordMinLetters) {
    365                     return String.format(getResources().getQuantityString(
    366                             R.plurals.lockpassword_password_requires_letters, mPasswordMinLetters),
    367                             mPasswordMinLetters);
    368                 } else if (numbers < mPasswordMinNumeric) {
    369                     return String.format(getResources().getQuantityString(
    370                             R.plurals.lockpassword_password_requires_numeric, mPasswordMinNumeric),
    371                             mPasswordMinNumeric);
    372                 } else if (lowercase < mPasswordMinLowerCase) {
    373                     return String.format(getResources().getQuantityString(
    374                             R.plurals.lockpassword_password_requires_lowercase, mPasswordMinLowerCase),
    375                             mPasswordMinLowerCase);
    376                 } else if (uppercase < mPasswordMinUpperCase) {
    377                     return String.format(getResources().getQuantityString(
    378                             R.plurals.lockpassword_password_requires_uppercase, mPasswordMinUpperCase),
    379                             mPasswordMinUpperCase);
    380                 } else if (symbols < mPasswordMinSymbols) {
    381                     return String.format(getResources().getQuantityString(
    382                             R.plurals.lockpassword_password_requires_symbols, mPasswordMinSymbols),
    383                             mPasswordMinSymbols);
    384                 } else if (nonletter < mPasswordMinNonLetter) {
    385                     return String.format(getResources().getQuantityString(
    386                             R.plurals.lockpassword_password_requires_nonletter, mPasswordMinNonLetter),
    387                             mPasswordMinNonLetter);
    388                 }
    389             } else {
    390                 final boolean alphabetic = DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
    391                         == mRequestedQuality;
    392                 final boolean alphanumeric = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
    393                         == mRequestedQuality;
    394                 if ((alphabetic || alphanumeric) && letters == 0) {
    395                     return getString(R.string.lockpassword_password_requires_alpha);
    396                 }
    397                 if (alphanumeric && numbers == 0) {
    398                     return getString(R.string.lockpassword_password_requires_digit);
    399                 }
    400             }
    401             if(mLockPatternUtils.checkPasswordHistory(password)) {
    402                 return getString(mIsAlphaMode ? R.string.lockpassword_password_recently_used
    403                         : R.string.lockpassword_pin_recently_used);
    404             }
    405 
    406             return null;
    407         }
    408 
    409         private void handleNext() {
    410             if (mDone) return;
    411 
    412             final String pin = mPasswordEntry.getText().toString();
    413             if (TextUtils.isEmpty(pin)) {
    414                 return;
    415             }
    416             String errorMsg = null;
    417             if (mUiStage == Stage.Introduction) {
    418                 errorMsg = validatePassword(pin);
    419                 if (errorMsg == null) {
    420                     mFirstPin = pin;
    421                     mPasswordEntry.setText("");
    422                     updateStage(Stage.NeedToConfirm);
    423                 }
    424             } else if (mUiStage == Stage.NeedToConfirm) {
    425                 if (mFirstPin.equals(pin)) {
    426                     final boolean isFallback = getActivity().getIntent().getBooleanExtra(
    427                             LockPatternUtils.LOCKSCREEN_BIOMETRIC_WEAK_FALLBACK, false);
    428                     mLockPatternUtils.clearLock(isFallback);
    429                     final boolean required = getActivity().getIntent().getBooleanExtra(
    430                             EncryptionInterstitial.EXTRA_REQUIRE_PASSWORD, true);
    431                     mLockPatternUtils.setCredentialRequiredToDecrypt(required);
    432                     mLockPatternUtils.saveLockPassword(pin, mRequestedQuality, isFallback);
    433                     getActivity().setResult(RESULT_FINISHED);
    434                     getActivity().finish();
    435                     mDone = true;
    436                     startActivity(RedactionInterstitial.createStartIntent(getActivity()));
    437                 } else {
    438                     CharSequence tmp = mPasswordEntry.getText();
    439                     if (tmp != null) {
    440                         Selection.setSelection((Spannable) tmp, 0, tmp.length());
    441                     }
    442                     updateStage(Stage.ConfirmWrong);
    443                 }
    444             }
    445             if (errorMsg != null) {
    446                 showError(errorMsg, mUiStage);
    447             }
    448         }
    449 
    450         public void onClick(View v) {
    451             switch (v.getId()) {
    452                 case R.id.next_button:
    453                     handleNext();
    454                     break;
    455 
    456                 case R.id.cancel_button:
    457                     getActivity().finish();
    458                     break;
    459             }
    460         }
    461 
    462         private void showError(String msg, final Stage next) {
    463             mHeaderText.setText(msg);
    464             mHeaderText.announceForAccessibility(mHeaderText.getText());
    465             Message mesg = mHandler.obtainMessage(MSG_SHOW_ERROR, next);
    466             mHandler.removeMessages(MSG_SHOW_ERROR);
    467             mHandler.sendMessageDelayed(mesg, ERROR_MESSAGE_TIMEOUT);
    468         }
    469 
    470         public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    471             // Check if this was the result of hitting the enter or "done" key
    472             if (actionId == EditorInfo.IME_NULL
    473                     || actionId == EditorInfo.IME_ACTION_DONE
    474                     || actionId == EditorInfo.IME_ACTION_NEXT) {
    475                 handleNext();
    476                 return true;
    477             }
    478             return false;
    479         }
    480 
    481         /**
    482          * Update the hint based on current Stage and length of password entry
    483          */
    484         private void updateUi() {
    485             String password = mPasswordEntry.getText().toString();
    486             final int length = password.length();
    487             if (mUiStage == Stage.Introduction && length > 0) {
    488                 if (length < mPasswordMinLength) {
    489                     String msg = getString(mIsAlphaMode ? R.string.lockpassword_password_too_short
    490                             : R.string.lockpassword_pin_too_short, mPasswordMinLength);
    491                     mHeaderText.setText(msg);
    492                     mNextButton.setEnabled(false);
    493                 } else {
    494                     String error = validatePassword(password);
    495                     if (error != null) {
    496                         mHeaderText.setText(error);
    497                         mNextButton.setEnabled(false);
    498                     } else {
    499                         mHeaderText.setText(R.string.lockpassword_press_continue);
    500                         mNextButton.setEnabled(true);
    501                     }
    502                 }
    503             } else {
    504                 mHeaderText.setText(mIsAlphaMode ? mUiStage.alphaHint : mUiStage.numericHint);
    505                 mNextButton.setEnabled(length > 0);
    506             }
    507             mNextButton.setText(mUiStage.buttonText);
    508         }
    509 
    510         public void afterTextChanged(Editable s) {
    511             // Changing the text while error displayed resets to NeedToConfirm state
    512             if (mUiStage == Stage.ConfirmWrong) {
    513                 mUiStage = Stage.NeedToConfirm;
    514             }
    515             updateUi();
    516         }
    517 
    518         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    519 
    520         }
    521 
    522         public void onTextChanged(CharSequence s, int start, int before, int count) {
    523 
    524         }
    525     }
    526 }
    527