package adonai.diary_browser; import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextWatcher; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.JavascriptInterface; import android.webkit.WebView; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.mobsandgeeks.saripaar.Rule; import com.mobsandgeeks.saripaar.ValidationError; import com.mobsandgeeks.saripaar.Validator; import com.mobsandgeeks.saripaar.annotation.ConfirmPassword; import com.mobsandgeeks.saripaar.annotation.Email; import com.mobsandgeeks.saripaar.annotation.NotEmpty; import com.mobsandgeeks.saripaar.annotation.Order; import com.mobsandgeeks.saripaar.annotation.Password; import com.mobsandgeeks.saripaar.annotation.Pattern; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Фрагмент, отвечающий за авторизацию в активности входа ({@link AuthorizationForm}) * * @author Адонай */ public class RegisterFragment extends Fragment implements View.OnClickListener, Validator.ValidationListener { private static final int VALIDATE = 0; private static final int REGISTER = 1; private static final int CAPTCHA = 2; @Order(0) @NotEmpty(messageResId = R.string.required_field) private EditText mRequestedLogin; @Order(1) @Pattern(regex = "[A-Za-z]+", messageResId = R.string.invalid_page_name) @NotEmpty(messageResId = R.string.required_field) private EditText mRequestedPage; @Order(2) @Password(messageResId = R.string.invalid_password) @NotEmpty(messageResId = R.string.required_field) private EditText mRequestedPassword; @Order(3) @ConfirmPassword(messageResId = R.string.passwords_dont_match) @NotEmpty(messageResId = R.string.required_field) private EditText mPasswordConfirm; @Order(4) @Email(messageResId = R.string.invalid_email) @NotEmpty(messageResId = R.string.required_field) private EditText mEmail; private Button mRegister; private MaterialDialog mRegisterWait, mCaptchaView; private TextWatcher mDelayedValidation = new DelayedValidation(); private Validator mValidator; private Handler mHandler; private DiaryHttpClient mHttpClient; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View reg = inflater.inflate(R.layout.authorization_form_register, container, false); mRequestedLogin = (EditText) reg.findViewById(R.id.register_desired_login); mRequestedLogin.addTextChangedListener(mDelayedValidation); mRequestedPage = (EditText) reg.findViewById(R.id.register_desired_page); mRequestedPage.addTextChangedListener(mDelayedValidation); mRequestedPassword = (EditText) reg.findViewById(R.id.register_desired_password); mRequestedPassword.addTextChangedListener(mDelayedValidation); mPasswordConfirm = (EditText) reg.findViewById(R.id.register_desired_password_again); mPasswordConfirm.addTextChangedListener(mDelayedValidation); mEmail = (EditText) reg.findViewById(R.id.register_email); mEmail.addTextChangedListener(mDelayedValidation); mRegister = (Button) reg.findViewById(R.id.register_button); mRegister.setOnClickListener(this); mRegisterWait = new MaterialDialog.Builder(getActivity()) .title(R.string.loading) .content(R.string.requesting_registration) .progress(true, 0) .build(); mValidator = new Validator(this); mValidator.setValidationMode(Validator.Mode.IMMEDIATE); mValidator.setValidationListener(this); mHttpClient = new DiaryHttpClient(); // не хочу добавлять TextWatcher на всё, раз в секунду хватит HandlerThread thread = new HandlerThread("ValidatorThread"); thread.start(); mHandler = new Handler(thread.getLooper(), new BackgroundCallback()); return reg; } @Override public void onDestroyView() { super.onDestroyView(); mRegisterWait.dismiss(); mHandler.getLooper().quit(); } @Override public void onValidationSucceeded() { mRegister.setEnabled(true); } @Override public void onValidationFailed(List<ValidationError> errors) { for(ValidationError error : errors) { EditText edit = (EditText) error.getView(); Rule firstFailed = error.getFailedRules().get(0); edit.setError(firstFailed.getMessage(getActivity())); } mRegister.setEnabled(false); } private class BackgroundCallback implements Handler.Callback { private String signature; @Override public boolean handleMessage(Message msg) { switch (msg.what) { case VALIDATE: mValidator.validate(true); return true; case REGISTER: case CAPTCHA: // необходимо для корректного продолжения //handleSignature(); List<Pair<String, String>> parameters = new ArrayList<>(16); parameters.add(Pair.create("act", "registration_post")); parameters.add(Pair.create("module", "registration")); parameters.add(Pair.create("username", mRequestedLogin.getText().toString())); parameters.add(Pair.create("shortname", mRequestedPage.getText().toString())); parameters.add(Pair.create("password", mRequestedPassword.getText().toString())); parameters.add(Pair.create("password2", mPasswordConfirm.getText().toString())); parameters.add(Pair.create("email", mEmail.getText().toString())); parameters.add(Pair.create("number", "")); parameters.add(Pair.create("month", "0")); parameters.add(Pair.create("day", "0")); parameters.add(Pair.create("year", "")); parameters.add(Pair.create("education", "1")); parameters.add(Pair.create("sfera", "")); //parameters.add(Pair.create("signature", signature)); if(msg.obj != null) { // есть ответ с капчей AdverigoCaptcha captcha = (AdverigoCaptcha) msg.obj; parameters.add(Pair.create("adverigo_captcha_answer", captcha.number)); parameters.add(Pair.create("adverigo_sid", captcha.token)); } String page = mHttpClient.postPageToString(Utils.REGISTER_PAGE, parameters); if(page != null) { Document root = Jsoup.parse(page); if (page.contains("Используйте ваши логин и пароль для входа")) { // успех handleSuccess(); } else if(root.title().equals("Проверка")) { // запрос капчи handleCaptcha(root); } else if(root.title().equals("@дневники:")) { // ошибка в данных deduceAndShowError(root); } } return true; default: return false; } } private void handleSignature() { if(signature == null) { String mainPage = mHttpClient.getPageAsString(Utils.MAIN_PAGE); Document mainDoc = Jsoup.parse(mainPage); Element sigNode = mainDoc.getElementsByAttributeValue("name", "signature").first(); if (sigNode != null) signature = sigNode.attr("value"); } } private void handleSuccess() { final MaterialDialog.Builder successShow = new MaterialDialog.Builder(getActivity()) .title(R.string.successfully_registered) .content(R.string.congratulation_registered) .positiveText(android.R.string.ok) .dismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { getActivity().onBackPressed(); } }); // показываем диалог getActivity().runOnUiThread(new Runnable() { @Override public void run() { successShow.build().show(); mRegisterWait.hide(); } }); } private class WebAppInterface { Context mContext; /** Instantiate the interface and set the context */ WebAppInterface(Context c) { mContext = c; } @JavascriptInterface public void grab(String token, String number) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { mHttpClient.syncCookiesWithClient(); mCaptchaView.dismiss(); mRegisterWait.show(); } }); AdverigoCaptcha captcha = new AdverigoCaptcha(); captcha.number = number; captcha.token = token; mHandler.sendMessage(mHandler.obtainMessage(CAPTCHA, captcha)); } } @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) // мы верим в вежливость дайри private void handleCaptcha(final Document root) { // убираем всё ненужное, показываем только враппер root.head().select("link[rel=stylesheet]").remove(); root.head().append("<link href=\"file:///android_asset/css/registration.css\" rel=\"stylesheet\" type=\"text/css\">"); root.body().select("p").remove(); root.body().append("<script type=\"text/javascript\" src=\"file:///android_asset/javascript/registration.js\"> </script>"); // показываем диалог getActivity().runOnUiThread(new Runnable() { @Override public void run() { WebView view = new WebView(getActivity()); view.getSettings().setJavaScriptEnabled(true); view.addJavascriptInterface(new WebAppInterface(getActivity()), "RegisterHandler"); mCaptchaView = new MaterialDialog.Builder(getActivity()) .title(R.string.captcha_request) .customView(view, false).build(); view.loadDataWithBaseURL(Utils.MAIN_PAGE, root.html(), null, "utf-8", null); mCaptchaView.show(); mRegisterWait.hide(); } }); } public void deduceAndShowError(Document root) { final MaterialDialog.Builder showError = new MaterialDialog.Builder(getActivity()) .title(R.string.registration_error) .positiveText(android.R.string.ok); // попытаемся вытащить ошибку Element error = root.select("td[bgcolor][align] > p > font").first(); if(error != null) { String errorText = error.ownText(); showError.content(errorText); } // из фонового потока не получится getActivity().runOnUiThread(new Runnable() { @Override public void run() { showError.build().show(); mRegisterWait.hide(); } }); } } public void onClick(View v) { switch (v.getId()) { case R.id.register_button: mHandler.sendEmptyMessage(REGISTER); mRegisterWait.show(); break; default: Toast.makeText(getActivity(), R.string.not_impl_sc, Toast.LENGTH_LONG).show(); } } private class AdverigoCaptcha { private String number; private String token; } private class DelayedValidation implements TextWatcher { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { mHandler.removeMessages(VALIDATE); mHandler.sendEmptyMessageDelayed(VALIDATE, 1000); } } }