protected void updateValidation(GuidedEditText editText, boolean ok, boolean visible, int resId) {
    setValidationColor(editText, ok);
    editText.setGuideVisible(visible);
    editText.setGuideText(resId);

    validatedMap.put(editText.getId(), ok);
    doValidation();
  }
  protected void updateValidation(
      GuidedEditText editText, boolean ok, boolean visible, CharSequence text) {
    setValidationColor(editText, ok);
    editText.setGuideVisible(visible);
    editText.setGuideText(text);

    validatedMap.put(editText.getId(), ok);
    doValidation();
  }
  protected synchronized void updateRetypedPasswordCorrectness() {
    CharSequence retypedPassword = editRetypeNewPassword.getText();
    // don't show mismatch message if retyped password field is empty

    if (TextUtils.isEmpty(retypedPassword)) {
      updateValidation(editRetypeNewPassword, false, false, "");
      return;
    }

    if (!editNewPassword.getText().toString().equals(retypedPassword.toString())) {
      updateValidation(editRetypeNewPassword, false, true, R.string.reg_psw_mismatch);
      return;
    }

    updateValidation(editRetypeNewPassword, true, true, R.string.ok_short);
  }
 protected void setValidationColor(GuidedEditText editText, boolean ok) {
   Resources res = this.getResources();
   editText.setGuideTextColor(
       ok
           ? res.getColorStateList(R.color.jnrain_green_dark)
           : res.getColorStateList(R.color.error_red));
 }
  public String getRealName() {
    String name = editRealName.getText().toString().trim();

    // TODO: consider foreign students who have a "(留)" suffix
    // appended to their last names WRITTEN IN UPPERCASE
    // Note this is Jiangnan University-specific.
    return checkIsEthnicMinority.isChecked() ? ('☆' + name) : name;
  }
 @Override
 public String getUID() {
   return editNewUID.getText().toString().trim();
 }
 public synchronized void setUseCurrentPhone(boolean useCurrent) {
   editPhone.setEnabled(!useCurrent);
   editPhone.setVisibility(useCurrent ? View.GONE : View.VISIBLE);
 }
 protected String getPhoneInput() {
   return editPhone.getText().toString().trim();
 }
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.kbs_register);

    mHandler =
        new Handler() {
          @Override
          public void handleMessage(Message msg) {
            loadingDlg =
                DialogHelper.showProgressDialog(
                    KBSRegisterActivity.this,
                    R.string.check_updates_dlg_title,
                    R.string.please_wait,
                    false,
                    false);
          }
        };

    lastUIDCheckTime = 0;

    initValidationMapping();

    // instance state
    if (savedInstanceState != null) {
      /*
       * editNewUID.onRestoreInstanceState(savedInstanceState
       * .getParcelable("newUID"));
       * editNewEmail.onRestoreInstanceState(savedInstanceState
       * .getParcelable("newEmail"));
       * editNewPassword.onRestoreInstanceState(savedInstanceState
       * .getParcelable("newPass"));
       * editRetypeNewPassword.onRestoreInstanceState
       * (savedInstanceState .getParcelable("repeatPass"));
       * editNewNickname.onRestoreInstanceState(savedInstanceState
       * .getParcelable("newNickname"));
       * editStudID.onRestoreInstanceState(savedInstanceState
       * .getParcelable("studID"));
       * editRealName.onRestoreInstanceState(savedInstanceState
       * .getParcelable("realname"));
       * editPhone.onRestoreInstanceState(savedInstanceState
       * .getParcelable("phone"));
       * editCaptcha.onRestoreInstanceState(savedInstanceState
       * .getParcelable("captcha"));
       */

      // captcha image
      byte[] captchaPNG = savedInstanceState.getByteArray("captchaImage");
      ByteArrayInputStream captchaStream = new ByteArrayInputStream(captchaPNG);
      try {
        Drawable captchaDrawable = BitmapDrawable.createFromStream(captchaStream, "src");
        imageRegCaptcha.setImageDrawable(captchaDrawable);
      } finally {
        try {
          captchaStream.close();
        } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
    }

    // event handlers
    // UID
    editNewUID.addTextChangedListener(
        new StrippedDownTextWatcher() {
          long lastCheckTime = 0;
          String lastUID;

          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            // FIXME: use monotonic clock...
            long curtime = System.currentTimeMillis();
            if (curtime - lastCheckTime >= KBSUIConstants.REG_CHECK_UID_INTERVAL_MILLIS) {
              String uid = s.toString();

              // don't check at the first char
              if (uid.length() > 1) {
                checkUIDAvailability(uid, curtime);
              }

              lastCheckTime = curtime;
              lastUID = uid;

              // schedule a new delayed check
              if (delayedUIDChecker != null) {
                delayedUIDChecker.cancel();
                delayedUIDChecker.purge();
              }
              delayedUIDChecker = new Timer();
              delayedUIDChecker.schedule(
                  new TimerTask() {
                    @Override
                    public void run() {
                      final String uid = getUID();

                      if (uid != lastUID) {
                        runOnUiThread(
                            new Runnable() {
                              @Override
                              public void run() {
                                checkUIDAvailability(uid, System.currentTimeMillis());
                              }
                            });

                        lastUID = uid;
                      }
                    }
                  },
                  KBSUIConstants.REG_CHECK_UID_INTERVAL_MILLIS);
            }
          }
        });

    editNewUID.setOnFocusChangeListener(
        new OnFocusChangeListener() {
          @Override
          public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus) {
              // lost focus, force check uid availability
              checkUIDAvailability(getUID(), System.currentTimeMillis());
            } else {
              // inputting, temporarily clear that notice
              updateUIDAvailability(false, 0);
            }
          }
        });

    // E-mail
    editNewEmail.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editNewEmail, false, true, R.string.reg_email_empty);
              return;
            }

            if (!EMAIL_CHECKER.matcher(s).matches()) {
              updateValidation(editNewEmail, false, true, R.string.reg_email_malformed);
              return;
            }

            updateValidation(editNewEmail, true, true, R.string.ok_short);
          }
        });

    // Password
    editNewPassword.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editNewPassword, false, true, R.string.reg_psw_empty);
              return;
            }

            if (s.length() < 6) {
              updateValidation(editNewPassword, false, true, R.string.reg_psw_too_short);
              return;
            }

            if (s.length() > 39) {
              updateValidation(editNewPassword, false, true, R.string.reg_psw_too_long);
              return;
            }

            if (getUID().equalsIgnoreCase(s.toString())) {
              updateValidation(editNewPassword, false, true, 0);
            }

            updateValidation(editNewPassword, true, true, R.string.ok_short);

            updateRetypedPasswordCorrectness();
          }
        });

    // Retype password
    editRetypeNewPassword.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            updateRetypedPasswordCorrectness();
          }
        });

    // Nickname
    editNewNickname.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editNewNickname, false, true, R.string.reg_nick_empty);
              return;
            }

            updateValidation(editNewNickname, true, true, R.string.ok_short);
          }
        });

    // Student ID
    editStudID.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editStudID, false, true, R.string.reg_stud_id_empty);
              return;
            }

            if (!IDENT_CHECKER.matcher(s).matches()) {
              updateValidation(editStudID, false, true, R.string.reg_stud_id_malformed);
              return;
            }

            updateValidation(editStudID, true, true, R.string.ok_short);
          }
        });

    // Real name
    editRealName.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editRealName, false, true, R.string.reg_realname_empty);
              return;
            }

            updateValidation(editRealName, true, true, R.string.ok_short);
          }
        });

    // Phone
    editPhone.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editPhone, false, true, R.string.reg_phone_empty);
              return;
            }

            if (!TextUtils.isDigitsOnly(s)) {
              updateValidation(editPhone, false, true, R.string.reg_phone_onlynumbers);
              return;
            }

            updateValidation(editPhone, true, true, R.string.ok_short);
          }
        });

    // Captcha
    editCaptcha.addTextChangedListener(
        new StrippedDownTextWatcher() {
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (TextUtils.isEmpty(s)) {
              updateValidation(editCaptcha, false, true, R.string.reg_captcha_empty);
              return;
            }

            updateValidation(editCaptcha, true, true, R.string.ok_short);
          }
        });

    // Use current phone
    checkUseCurrentPhone.setOnCheckedChangeListener(
        new OnCheckedChangeListener() {
          @Override
          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            setUseCurrentPhone(isChecked);
          }
        });

    // Is ethnic minority
    checkIsEthnicMinority.setOnCheckedChangeListener(
        new OnCheckedChangeListener() {
          @Override
          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            setEthnicMinority(isChecked);
          }
        });

    // Submit form!
    btnSubmitRegister.setOnClickListener(
        new OnClickListener() {
          @Override
          public void onClick(View arg0) {
            // progress dialog
            mHandler.sendMessage(new Message());

            // prevent repeated requests
            setSubmitButtonEnabled(false);

            // fire our biiiiiig request!
            // TODO: 性别默认成了妹子23333
            final String uid = getUID();
            final String psw = editNewPassword.getText().toString();
            makeSpiceRequest(
                new KBSRegisterRequest(
                    uid,
                    psw,
                    editNewNickname.getText().toString(),
                    getRealName(),
                    editStudID.getText().toString(),
                    editNewEmail.getText().toString(),
                    getPhone(),
                    editCaptcha.getText().toString(),
                    2),
                new KBSRegisterRequestListener(KBSRegisterActivity.this, uid, psw));
          }
        });

    // interface init
    // UID availability
    updateUIDAvailability(false, 0);

    // HTML-formatted register disclaimer
    FormatHelper.setHtmlText(this, textRegisterDisclaimer, R.string.register_disclaimer);
    textRegisterDisclaimer.setMovementMethod(LinkMovementMethod.getInstance());

    // is ethnic minority defaults to false
    checkIsEthnicMinority.setChecked(false);
    setEthnicMinority(false);

    // current phone number
    currentPhoneNumber = TelephonyHelper.getPhoneNumber(this);
    isCurrentPhoneNumberAvailable = currentPhoneNumber != null;

    if (isCurrentPhoneNumberAvailable) {
      // display the obtained number as hint
      FormatHelper.setHtmlText(
          this, checkUseCurrentPhone, R.string.field_use_current_phone, currentPhoneNumber);
    } else {
      // phone number unavailable, disable the choice
      checkUseCurrentPhone.setEnabled(false);
      checkUseCurrentPhone.setVisibility(View.GONE);
    }

    // default to use current phone number if available
    checkUseCurrentPhone.setChecked(isCurrentPhoneNumberAvailable);
    setUseCurrentPhone(isCurrentPhoneNumberAvailable);

    if (savedInstanceState == null) {
      // issue preflight request
      // load captcha in success callback
      this.makeSpiceRequest(new KBSRegisterRequest(), new KBSRegisterRequestListener(this));
    }
  }