package org.wordpress.android.ui.accounts;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.Html;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.ViewSwitcher;
import com.google.android.gms.auth.api.credentials.Credential;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.wordpress.android.BuildConfig;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.analytics.AnalyticsTracker;
import org.wordpress.android.analytics.AnalyticsTracker.Stat;
import org.wordpress.android.fluxc.Dispatcher;
import org.wordpress.android.fluxc.action.AccountAction;
import org.wordpress.android.fluxc.generated.AccountActionBuilder;
import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder;
import org.wordpress.android.fluxc.generated.SiteActionBuilder;
import org.wordpress.android.fluxc.model.SiteModel;
import org.wordpress.android.fluxc.network.HTTPAuthManager;
import org.wordpress.android.fluxc.network.MemorizingTrustManager;
import org.wordpress.android.fluxc.network.discovery.SelfHostedEndpointFinder.DiscoveryError;
import org.wordpress.android.fluxc.network.rest.wpcom.account.AccountRestClient.IsAvailable;
import org.wordpress.android.fluxc.store.AccountStore;
import org.wordpress.android.fluxc.store.AccountStore.AuthenticatePayload;
import org.wordpress.android.fluxc.store.AccountStore.AuthenticationErrorType;
import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged;
import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged;
import org.wordpress.android.fluxc.store.AccountStore.OnAvailabilityChecked;
import org.wordpress.android.fluxc.store.AccountStore.OnDiscoveryResponse;
import org.wordpress.android.fluxc.store.SiteStore;
import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged;
import org.wordpress.android.fluxc.store.SiteStore.RefreshSitesXMLRPCPayload;
import org.wordpress.android.fluxc.store.SiteStore.SiteErrorType;
import org.wordpress.android.ui.ActivityLauncher;
import org.wordpress.android.ui.main.WPMainActivity;
import org.wordpress.android.ui.notifications.services.NotificationsUpdateService;
import org.wordpress.android.util.AnalyticsUtils;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.EditTextUtils;
import org.wordpress.android.util.HelpshiftHelper;
import org.wordpress.android.util.HtmlUtils;
import org.wordpress.android.util.NetworkUtils;
import org.wordpress.android.util.SelfSignedSSLUtils;
import org.wordpress.android.util.SelfSignedSSLUtils.Callback;
import org.wordpress.android.util.ToastUtils;
import org.wordpress.android.util.ToastUtils.Duration;
import org.wordpress.android.util.UrlUtils;
import org.wordpress.android.util.WPActivityUtils;
import org.wordpress.android.util.WPUrlUtils;
import org.wordpress.android.widgets.ContextMenuEditText;
import org.wordpress.android.widgets.WPTextView;
import org.wordpress.emailchecker2.EmailChecker;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import static android.content.Context.CLIPBOARD_SERVICE;
public class SignInFragment extends AbstractFragment implements TextWatcher {
public static final String TAG = "sign_in_fragment_tag";
public static final int MAX_EMAIL_LENGTH = 100;
private static final String DOT_COM_BASE_URL = "https://wordpress.com";
private static final String FORGOT_PASSWORD_RELATIVE_URL = "/wp-login.php?action=lostpassword";
private static final int WPCOM_ERRONEOUS_LOGIN_THRESHOLD = 3;
private static final String KEY_IS_SELF_HOSTED = "IS_SELF_HOSTED";
private static final Pattern DOT_COM_RESERVED_NAMES =
Pattern.compile("^(?:admin|administrator|invite|main|root|web|www|[^@]*wordpress[^@]*)$");
private static final Pattern TWO_STEP_AUTH_CODE = Pattern.compile("^[0-9]{6}");
private static final Pattern WPCOM_DOMAIN = Pattern.compile("[a-z0-9]+\\.wordpress\\.com");
public static final String ENTERED_URL_KEY = "ENTERED_URL_KEY";
public static final String ENTERED_USERNAME_KEY = "ENTERED_USERNAME_KEY";
private static final String XMLRPC_BLOCKED_HELPSHIFT_FAQ_SECTION = "10";
private static final String XMLRPC_BLOCKED_HELPSHIFT_FAQ_ID = "102";
private static final String MISSING_XMLRPC_METHOD_HELPSHIFT_FAQ_SECTION = "10";
private static final String MISSING_XMLRPC_METHOD_HELPSHIFT_FAQ_ID = "11";
private static final String NO_SITE_HELPSHIFT_FAQ_SECTION = "10";
private static final String NO_SITE_HELPSHIFT_FAQ_ID = "2"; //using the same as in INVALID URL
protected EditText mUsernameEditText;
protected EditText mPasswordEditText;
protected EditText mUrlEditText;
protected ContextMenuEditText mTwoStepEditText;
protected ViewSwitcher mIconSwitcher;
protected View mWpcomLogotype;
protected LinearLayout mBottomButtonsLayout;
protected RelativeLayout mUsernameLayout;
protected RelativeLayout mPasswordLayout;
protected RelativeLayout mProgressBarSignIn;
protected RelativeLayout mUrlButtonLayout;
protected RelativeLayout mTwoStepLayout;
protected LinearLayout mTwoStepFooter;
protected boolean mSelfHosted;
protected boolean mIsSelfHostedForced;
protected boolean mEmailAutoCorrected;
protected boolean mShouldSendTwoStepSMS;
protected int mErroneousLogInCount;
protected String mUsername;
protected String mPassword;
protected String mTwoStepCode;
protected String mHttpUsername;
protected String mHttpPassword;
protected SiteModel mJetpackSite;
protected WPTextView mSignInButton;
protected WPTextView mCreateAccountButton;
protected WPTextView mAddSelfHostedButton;
protected WPTextView mProgressTextSignIn;
protected WPTextView mForgotPassword;
protected WPTextView mJetpackAuthLabel;
protected ImageView mInfoButton;
protected ImageView mInfoButtonSecondary;
protected @Inject SiteStore mSiteStore;
protected @Inject AccountStore mAccountStore;
protected @Inject Dispatcher mDispatcher;
protected @Inject HTTPAuthManager mHTTPAuthManager;
protected @Inject MemorizingTrustManager mMemorizingTrustManager;
protected boolean mSitesFetched = false;
protected boolean mAccountSettingsFetched = false;
protected boolean mAccountFetched = false;
private final Matcher mReservedNameMatcher = DOT_COM_RESERVED_NAMES.matcher("");
private final Matcher mTwoStepAuthCodeMatcher = TWO_STEP_AUTH_CODE.matcher("");
private OnMagicLinkRequestInteraction mListener;
private String mToken = "";
private boolean mSmartLockEnabled = true;
private boolean mIsActivityFinishing;
private boolean mInhibitMagicLogin; // Prevent showing magic links as that is only applicable for initial sign in
private boolean mIsMagicLinksEnabled = true;
private JetpackCallbacks mJetpackCallbacks;
public interface OnMagicLinkRequestInteraction {
void onMagicLinkRequestSuccess(String email);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((WordPress) getActivity().getApplication()).component().inject(this);
if (savedInstanceState != null) {
mSelfHosted = savedInstanceState.getBoolean(KEY_IS_SELF_HOSTED);
}
mInhibitMagicLogin = getActivity() != null
&& (getActivity().getIntent().getBooleanExtra(SignInActivity.EXTRA_INHIBIT_MAGIC_LOGIN, false)
|| !WPActivityUtils.isEmailClientAvailable(getActivity()));
AnalyticsTracker.track(Stat.LOGIN_ACCESSED);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.signin_fragment, container, false);
mIconSwitcher = (ViewSwitcher) rootView.findViewById(R.id.icon_switcher);
mWpcomLogotype = rootView.findViewById(R.id.nux_wordpress_logotype);
mUrlButtonLayout = (RelativeLayout) rootView.findViewById(R.id.url_button_layout);
mTwoStepLayout = (RelativeLayout) rootView.findViewById(R.id.two_factor_layout);
mTwoStepFooter = (LinearLayout) rootView.findViewById(R.id.two_step_footer);
mUsernameLayout = (RelativeLayout) rootView.findViewById(R.id.nux_username_layout);
mUsernameLayout.setOnClickListener(mOnLoginFormClickListener);
mPasswordLayout = (RelativeLayout) rootView.findViewById(R.id.nux_password_layout);
mPasswordLayout.setOnClickListener(mOnLoginFormClickListener);
mUsernameEditText = (EditText) rootView.findViewById(R.id.nux_username);
mUsernameEditText.addTextChangedListener(this);
mUsernameEditText.setOnClickListener(mOnLoginFormClickListener);
mPasswordEditText = (EditText) rootView.findViewById(R.id.nux_password);
mPasswordEditText.addTextChangedListener(this);
mPasswordEditText.setOnClickListener(mOnLoginFormClickListener);
mJetpackAuthLabel = (WPTextView) rootView.findViewById(R.id.nux_jetpack_auth_label);
mUrlEditText = (EditText) rootView.findViewById(R.id.nux_url);
mSignInButton = (WPTextView) rootView.findViewById(R.id.nux_sign_in_button);
mSignInButton.setOnClickListener(mSignInClickListener);
mProgressBarSignIn = (RelativeLayout) rootView.findViewById(R.id.nux_sign_in_progress_bar);
mProgressTextSignIn = (WPTextView) rootView.findViewById(R.id.nux_sign_in_progress_text);
mCreateAccountButton = (WPTextView) rootView.findViewById(R.id.nux_create_account_button);
mCreateAccountButton.setOnClickListener(mCreateAccountListener);
mAddSelfHostedButton = (WPTextView) rootView.findViewById(R.id.nux_add_selfhosted_button);
mAddSelfHostedButton.setText(getString(R.string.nux_add_selfhosted_blog));
mAddSelfHostedButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
toggleSignInMode();
}
});
mForgotPassword = (WPTextView) rootView.findViewById(R.id.forgot_password);
mForgotPassword.setOnClickListener(mForgotPasswordListener);
mUsernameEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
autocorrectUsername();
// Show the self-hosted sign-in form if the user entered a username that can't be for WordPress.com
if (!mSelfHosted) {
mReservedNameMatcher.reset(mUsernameEditText.getText().toString());
if (mReservedNameMatcher.matches()) {
showSelfHostedSignInForm();
}
}
}
}
});
mPasswordEditText.setOnEditorActionListener(mEditorAction);
mUrlEditText.setOnEditorActionListener(mEditorAction);
mTwoStepEditText = (ContextMenuEditText) rootView.findViewById(R.id.nux_two_step);
mTwoStepEditText.addTextChangedListener(this);
mTwoStepEditText.setOnKeyListener(
new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (keyCode == EditorInfo.IME_ACTION_DONE)) {
if (fieldsFilled()) {
signIn();
}
}
return false;
}
}
);
mTwoStepEditText.setOnContextMenuListener(
new ContextMenuEditText.OnContextMenuListener() {
@Override
public void onCut() {
}
@Override
public void onCopy() {
}
@Override
public void onPaste() {
mTwoStepEditText.setText(getAuthCodeFromClipboard());
if (TextUtils.isEmpty(mTwoStepEditText.getText().toString())) {
showTwoStepCodeError(R.string.invalid_verification_code_format);
}
}
}
);
WPTextView twoStepFooterButton = (WPTextView) rootView.findViewById(R.id.two_step_footer_button);
twoStepFooterButton.setText(Html.fromHtml("<u>" + getString(R.string.two_step_footer_button) + "</u>"));
twoStepFooterButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
requestSMSTwoStepCode();
}
});
mBottomButtonsLayout = (LinearLayout) rootView.findViewById(R.id.nux_bottom_buttons);
initPasswordVisibilityButton(rootView, mPasswordEditText);
initInfoButtons(rootView);
moveBottomButtons();
if (mSelfHosted) {
showSelfHostedSignInForm();
}
autofillFromBuildConfig();
if (savedInstanceState == null) {
configureMagicLinkUI();
}
mUsernameEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((didPressNextKey(actionId, event) || didPressEnterKey(actionId, event)) && mIsMagicLinksEnabled) {
signIn();
return true;
} else {
return false;
}
}
});
return rootView;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SignInFragment.OnMagicLinkRequestInteraction) {
mListener = (SignInFragment.OnMagicLinkRequestInteraction) context;
} else {
throw new RuntimeException(context.toString() + " must implement OnMagicLinkRequestInteraction");
}
if (context instanceof JetpackCallbacks) {
mJetpackCallbacks = (JetpackCallbacks) context;
} else {
throw new RuntimeException(context.toString() + " must implement JetpackCallbacks");
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onResume() {
super.onResume();
// Ensure two-step form is shown if needed
if (!TextUtils.isEmpty(mTwoStepEditText.getText()) && mTwoStepLayout.getVisibility() == View.GONE) {
setTwoStepAuthVisibility(true);
// Insert authentication code if copied to clipboard
} else if (TextUtils.isEmpty(mTwoStepEditText.getText()) && mTwoStepLayout.getVisibility() == View.VISIBLE) {
mTwoStepEditText.setText(getAuthCodeFromClipboard());
}
if (!mToken.isEmpty() && !mInhibitMagicLogin) {
mSmartLockEnabled = false;
attemptLoginWithMagicLink();
} else {
mSmartLockEnabled = true;
}
if (!mIsMagicLinksEnabled) {
showPasswordFieldAndFocus();
} else if (mInhibitMagicLogin) {
showPasswordField(false);
}
}
/**
* Hide toggle button "add self hosted / log in with WordPress.com" and show self hosted URL
* edit box
*/
public void forceSelfHostedMode(@NonNull String prefillUrl) {
mUrlButtonLayout.setVisibility(View.VISIBLE);
mPasswordLayout.setVisibility(View.VISIBLE);
mAddSelfHostedButton.setVisibility(View.GONE);
mCreateAccountButton.setVisibility(View.GONE);
switchToDotOrgIcon(true);
switchBackgroundToDotOrg(true, true);
if (!prefillUrl.isEmpty()) {
mUrlEditText.setText(prefillUrl);
}
mSelfHosted = true;
mIsSelfHostedForced = true;
}
private void showPasswordFieldAndFocus() {
showPasswordField(true);
}
private void showPasswordField(boolean doFocus) {
if (isAdded()) {
endProgress();
showPasswordField();
if (doFocus) {
mPasswordEditText.requestFocus();
}
mSignInButton.setText(getString(R.string.sign_in));
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.showSoftInput(mPasswordEditText, InputMethodManager.SHOW_IMPLICIT);
}
}
private void showPasswordField() {
if (isAdded()) {
mIsMagicLinksEnabled = false;
mPasswordLayout.setVisibility(View.VISIBLE);
mForgotPassword.setVisibility(View.VISIBLE);
if (!mSelfHosted) {
mPasswordEditText.setImeOptions(EditorInfo.IME_ACTION_DONE);
showWPComLogoType(true);
}
mSignInButton.setText(R.string.sign_in);
}
}
protected void toggleSignInMode(){
if (mUrlButtonLayout.getVisibility() == View.VISIBLE) {
if (mInhibitMagicLogin) {
showDotComSignInForm();
} else {
mIsMagicLinksEnabled = true;
configureMagicLinkUI();
}
} else {
showSelfHostedSignInForm();
}
if (fieldsFilled()) {
mSignInButton.setEnabled(true);
} else {
mSignInButton.setEnabled(false);
}
}
protected void showDotComSignInForm(){
mUrlButtonLayout.setVisibility(View.GONE);
mAddSelfHostedButton.setText(getString(R.string.nux_add_selfhosted_blog));
switchToDotOrgIcon(false);
switchBackgroundToDotOrg(false, false);
showWPComLogoType(false);
}
protected void showSelfHostedSignInForm(){
endProgress();
mSelfHosted = true;
mUrlButtonLayout.setVisibility(View.VISIBLE);
mAddSelfHostedButton.setText(getString(R.string.nux_oops_not_selfhosted_blog));
showPasswordField();
switchToDotOrgIcon(true);
switchBackgroundToDotOrg(true, false);
}
private void switchToDotOrgIcon(boolean showDotOrg) {
if (mIconSwitcher.getDisplayedChild() == 0) {
if (showDotOrg) {
mIconSwitcher.showNext();
}
showWPComLogoType(false);
} else {
if (!showDotOrg) {
mIconSwitcher.showPrevious();
}
}
}
private void showWPComLogoType(boolean show) {
int visibility = show ? View.VISIBLE : View.GONE;
mWpcomLogotype.setVisibility(visibility);
}
private void switchBackgroundToDotOrg(boolean useDotOrg, boolean noFading) {
if (getView() == null) {
return;
}
TransitionDrawable transition = (TransitionDrawable) getView().getBackground();
if (useDotOrg) {
transition.startTransition(noFading ? 0 : 500);
} else {
transition.reverseTransition(noFading ? 0 : 500);
}
}
public void setToken(String token) {
mToken = token;
}
public void setIsMagicLinkEnabled(boolean isMagicLinksEnabled) {
mIsMagicLinksEnabled = isMagicLinksEnabled;
}
private void initInfoButtons(View rootView) {
OnClickListener infoButtonListener = new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), HelpActivity.class);
// Used to pass data to an eventual support service
intent.putExtra(ENTERED_URL_KEY, EditTextUtils.getText(mUrlEditText));
intent.putExtra(ENTERED_USERNAME_KEY, EditTextUtils.getText(mUsernameEditText));
intent.putExtra(HelpshiftHelper.ORIGIN_KEY, HelpshiftHelper.chooseHelpshiftLoginTag
(mJetpackCallbacks.isJetpackAuth(), isWPComLogin() && !mSelfHosted));
startActivity(intent);
}
};
mInfoButton = (ImageView) rootView.findViewById(R.id.info_button);
mInfoButtonSecondary = (ImageView) rootView.findViewById(R.id.info_button_secondary);
mInfoButton.setOnClickListener(infoButtonListener);
mInfoButtonSecondary.setOnClickListener(infoButtonListener);
}
/*
* autofill the username and password from BuildConfig/gradle.properties (developer feature,
* only enabled for DEBUG releases)
*/
private void autofillFromBuildConfig() {
if (!BuildConfig.DEBUG) return;
String userName = (String) WordPress.getBuildConfigValue(getActivity().getApplication(),
"DEBUG_DOTCOM_LOGIN_USERNAME");
String password = (String) WordPress.getBuildConfigValue(getActivity().getApplication(),
"DEBUG_DOTCOM_LOGIN_PASSWORD");
if (!TextUtils.isEmpty(userName)) {
mUsernameEditText.setText(userName);
AppLog.d(T.NUX, "Autofilled username from build config");
}
if (!TextUtils.isEmpty(password)) {
mPasswordEditText.setText(password);
AppLog.d(T.NUX, "Autofilled password from build config");
}
}
public boolean canAutofillUsernameAndPassword() {
return EditTextUtils.getText(mUsernameEditText).isEmpty()
&& EditTextUtils.getText(mPasswordEditText).isEmpty()
&& mUsernameEditText != null
&& mPasswordEditText != null
&& mSmartLockEnabled
&& !mSelfHosted;
}
public void onCredentialRetrieved(Credential credential) {
AppLog.d(T.NUX, "Retrieved username from SmartLock: " + credential.getId());
if (isAdded() && canAutofillUsernameAndPassword()) {
AnalyticsTracker.track(Stat.LOGIN_AUTOFILL_CREDENTIALS_FILLED);
mUsernameEditText.setText(credential.getId());
mPasswordEditText.setText(credential.getPassword());
}
showPasswordField();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
moveBottomButtons();
}
private void setSecondaryButtonVisible(boolean visible) {
mInfoButtonSecondary.setVisibility(visible ? View.VISIBLE : View.GONE);
mInfoButton.setVisibility(visible ? View.GONE : View.VISIBLE);
}
private void moveBottomButtons() {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mBottomButtonsLayout.setOrientation(LinearLayout.HORIZONTAL);
if (getResources().getInteger(R.integer.isSW600DP) == 0) {
setSecondaryButtonVisible(true);
} else {
setSecondaryButtonVisible(false);
}
// make the top padding match the bottom padding of the logo so the logo doesn't touch the screen top
mIconSwitcher.setPadding(mIconSwitcher.getPaddingLeft(), mIconSwitcher.getPaddingBottom(), mIconSwitcher
.getPaddingRight(), mIconSwitcher.getPaddingBottom());
} else {
mBottomButtonsLayout.setOrientation(LinearLayout.VERTICAL);
setSecondaryButtonVisible(false);
// revert the top padding to zero when in portrait
mIconSwitcher.setPadding(mIconSwitcher.getPaddingLeft(), 0, mIconSwitcher.getPaddingRight(), mIconSwitcher
.getPaddingBottom());
}
}
private final OnClickListener mOnLoginFormClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
// Don't change layout if we are performing a network operation
if (mProgressBarSignIn.getVisibility() == View.VISIBLE) return;
if (mTwoStepLayout.getVisibility() == View.VISIBLE) {
setTwoStepAuthVisibility(false);
}
}
};
private void autocorrectUsername() {
if (mEmailAutoCorrected) {
return;
}
final String email = EditTextUtils.getText(mUsernameEditText).trim();
// Check if the username looks like an email address
final Pattern emailRegExPattern = Patterns.EMAIL_ADDRESS;
Matcher matcher = emailRegExPattern.matcher(email);
if (!matcher.find()) {
return;
}
// It looks like an email address, then try to correct it
String suggest = EmailChecker.suggestDomainCorrection(email);
if (suggest.compareTo(email) != 0) {
mEmailAutoCorrected = true;
mUsernameEditText.setText(suggest);
mUsernameEditText.setSelection(suggest.length());
}
}
private boolean isWPComLogin() {
String selfHostedUrl = EditTextUtils.getText(mUrlEditText).trim();
return !mSelfHosted || TextUtils.isEmpty(selfHostedUrl) ||
WPUrlUtils.isWordPressCom(UrlUtils.addUrlSchemeIfNeeded(selfHostedUrl, false));
}
private void configureMagicLinkUI() {
showDotComSignInForm();
mSelfHosted = false;
mPasswordLayout.setVisibility(View.GONE);
mForgotPassword.setVisibility(View.GONE);
mSignInButton.setText(getString(R.string.button_next));
}
private boolean isJetpackAuth() {
return mJetpackSite != null;
}
// Set blog for Jetpack auth
public void setBlogAndCustomMessageForJetpackAuth(SiteModel site, String customAuthMessage) {
mJetpackSite = site;
if (customAuthMessage != null && mJetpackAuthLabel != null) {
mJetpackAuthLabel.setText(customAuthMessage);
}
if (mAddSelfHostedButton != null) {
mJetpackAuthLabel.setVisibility(View.VISIBLE);
mAddSelfHostedButton.setVisibility(View.GONE);
mCreateAccountButton.setVisibility(View.GONE);
}
}
private final View.OnClickListener mCreateAccountListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
createUserFragment();
}
};
private void createUserFragment() {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
NewUserFragment newUserFragment = NewUserFragment.newInstance();
newUserFragment.setTargetFragment(this, NewUserFragment.NEW_USER);
transaction.setCustomAnimations(R.anim.activity_slide_in_from_right, R.anim.activity_slide_out_to_left,
R.anim.activity_slide_in_from_left, R.anim.activity_slide_out_to_right);
transaction.replace(R.id.fragment_container, newUserFragment);
transaction.addToBackStack(null);
transaction.commit();
AnalyticsTracker.track(Stat.CREATE_ACCOUNT_INITIATED);
}
private String getForgotPasswordURL() {
String baseUrl = DOT_COM_BASE_URL;
if (!isWPComLogin()) {
baseUrl = EditTextUtils.getText(mUrlEditText).trim();
String lowerCaseBaseUrl = baseUrl.toLowerCase(Locale.getDefault());
if (!lowerCaseBaseUrl.startsWith("https://") && !lowerCaseBaseUrl.startsWith("http://")) {
baseUrl = "http://" + baseUrl;
}
}
return baseUrl + FORGOT_PASSWORD_RELATIVE_URL;
}
private final View.OnClickListener mForgotPasswordListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
String forgotPasswordUrl = getForgotPasswordURL();
AppLog.i(T.NUX, "User tapped forgot password link: " + forgotPasswordUrl);
ActivityLauncher.openUrlExternal(getContext(), forgotPasswordUrl);
}
};
protected void onDoneAction() {
signIn();
}
private final TextView.OnEditorActionListener mEditorAction = new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (mPasswordEditText == v) {
if (mSelfHosted) {
mUrlEditText.requestFocus();
return true;
} else {
return onDoneEvent(actionId, event);
}
}
return onDoneEvent(actionId, event);
}
};
private void trackAnalyticsSignIn() {
AnalyticsUtils.refreshMetadata(mAccountStore, mSiteStore);
Map<String, Boolean> properties = new HashMap<>();
properties.put("dotcom_user", isWPComLogin());
AnalyticsTracker.track(Stat.SIGNED_IN, properties);
if (!isWPComLogin()) {
AnalyticsTracker.track(Stat.ADDED_SELF_HOSTED_SITE);
}
}
private void saveCredentialsInSmartLock() {
SmartLockHelper smartLockHelper = getSmartLockHelper();
// mUsername and mPassword are null when the user log in with a magic link
if (smartLockHelper != null && mUsername != null && mPassword != null) {
smartLockHelper.saveCredentialsInSmartLock(mUsername, mPassword,
HtmlUtils.fastUnescapeHtml(mAccountStore.getAccount().getDisplayName()),
Uri.parse(mAccountStore.getAccount().getAvatarUrl()));
}
}
private void finishCurrentActivity() {
if (mIsActivityFinishing) {
return;
}
// Clear persisted text from in the URL field
mUrlEditText.setText("");
mIsActivityFinishing = true;
saveCredentialsInSmartLock();
if (getActivity() == null) {
return;
}
if (mInhibitMagicLogin) {
// just finish the login activity and return to the its "caller"
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
} else {
// move on the the main activity
Intent intent = new Intent(getActivity(), WPMainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(SignInActivity.MAGIC_LOGIN, mIsMagicLinksEnabled);
getActivity().startActivity(intent);
}
}
public void attemptLoginWithMagicLink() {
// Save Token to the AccountStore, this will a onAuthenticationChanged (that will pull account setting and
// sites)
AccountStore.UpdateTokenPayload payload = new AccountStore.UpdateTokenPayload(mToken);
mDispatcher.dispatch(AccountActionBuilder.newUpdateAccessTokenAction(payload));
}
private SmartLockHelper getSmartLockHelper() {
if (getActivity() != null && getActivity() instanceof SignInActivity) {
return ((SignInActivity) getActivity()).getSmartLockHelper();
}
return null;
}
public void showAuthErrorMessage() {
if (mJetpackAuthLabel != null) {
mJetpackAuthLabel.setVisibility(View.VISIBLE);
mJetpackAuthLabel.setText(getResources().getString(R.string.auth_required));
}
}
private String getAuthCodeFromClipboard() {
ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(CLIPBOARD_SERVICE);
if (clipboard.getPrimaryClip() != null && clipboard.getPrimaryClip().getItemAt(0) != null
&& clipboard.getPrimaryClip().getItemAt(0).getText() != null) {
String code = clipboard.getPrimaryClip().getItemAt(0).getText().toString();
mTwoStepAuthCodeMatcher.reset(code);
if (!code.isEmpty() && mTwoStepAuthCodeMatcher.matches()) {
return code;
}
}
return "";
}
private void setTwoStepAuthVisibility(boolean isVisible) {
mTwoStepLayout.setVisibility(isVisible ? View.VISIBLE : View.GONE);
mTwoStepFooter.setVisibility(isVisible ? View.VISIBLE : View.GONE);
mSignInButton.setText(isVisible ? getString(R.string.verify) : getString(R.string.sign_in));
mForgotPassword.setVisibility(isVisible ? View.GONE : View.VISIBLE);
mBottomButtonsLayout.setVisibility(isVisible ? View.GONE : View.VISIBLE);
mUsernameEditText.setFocusableInTouchMode(!isVisible);
mUsernameLayout.setAlpha(isVisible ? 0.6f : 1.0f);
mPasswordEditText.setFocusableInTouchMode(!isVisible);
mPasswordLayout.setAlpha(isVisible ? 0.6f : 1.0f);
if (isVisible) {
mTwoStepEditText.requestFocus();
mTwoStepEditText.setText("");
showSoftKeyboard();
} else {
mTwoStepEditText.setText("");
mTwoStepEditText.clearFocus();
}
}
private void showSoftKeyboard() {
if (isAdded() && !hasHardwareKeyboard()) {
InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_NOT_ALWAYS);
}
}
private boolean hasHardwareKeyboard() {
return (getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS);
}
private void signInAndFetchBlogListWPCom() {
startProgress(getString(R.string.connecting_wpcom));
AuthenticatePayload payload = new AuthenticatePayload(mUsername, mPassword);
payload.twoStepCode = mTwoStepCode;
payload.shouldSendTwoStepSms = mShouldSendTwoStepSMS;
mDispatcher.dispatch(AuthenticationActionBuilder.newAuthenticateAction(payload));
}
private void signInAndFetchBlogListWPOrg() {
startProgress(getString(R.string.signing_in));
String url = EditTextUtils.getText(mUrlEditText).trim();
url = url.replaceAll("\r|\n", "");
// Self Hosted don't have any "Authentication" request, try to list sites with user/password
mDispatcher.dispatch(AuthenticationActionBuilder.newDiscoverEndpointAction(url));
}
private boolean checkNetworkConnectivity() {
if (!NetworkUtils.isNetworkAvailable(getActivity())) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
SignInDialogFragment nuxAlert;
nuxAlert = SignInDialogFragment.newInstance(getString(R.string.no_network_title),
getString(R.string.no_network_message),
R.drawable.ic_notice_white_64dp,
getString(R.string.cancel));
ft.add(nuxAlert, "alert");
ft.commitAllowingStateLoss();
return false;
}
return true;
}
private boolean checkIfUserIsAlreadyLoggedIn() {
if (mAccountStore.hasAccessToken()) {
String currentUsername = mAccountStore.getAccount().getUserName();
AppLog.e(T.NUX, "User is already logged in WordPress.com: " + currentUsername
+ " - but tries to sign in again: " + mUsername);
if (getActivity() != null) {
if (currentUsername.equals(mUsername)) {
ToastUtils.showToast(getActivity(), R.string.already_logged_in_wpcom_same_username, Duration.LONG);
} else {
ToastUtils.showToast(getActivity(), R.string.already_logged_in_wpcom, Duration.LONG);
}
}
return true;
}
return false;
}
protected void signIn() {
if (mSelfHosted || !mIsMagicLinksEnabled) {
if (!isUserDataValid()) {
return;
}
if (!checkNetworkConnectivity()) {
return;
}
mUsername = EditTextUtils.getText(mUsernameEditText).trim().toLowerCase();
mPassword = EditTextUtils.getText(mPasswordEditText).trim();
mTwoStepCode = EditTextUtils.getText(mTwoStepEditText).trim();
if (isWPComLogin()) {
// If the user is already logged in a wordpress.com account, bail out
if (checkIfUserIsAlreadyLoggedIn()) {
return;
}
AppLog.i(T.NUX, "User tries to sign in on WordPress.com with username: " + mUsername);
signInAndFetchBlogListWPCom();
} else {
String selfHostedUrl = EditTextUtils.getText(mUrlEditText).trim();
AppLog.i(T.NUX, "User tries to sign in on Self Hosted: " + selfHostedUrl
+ " with username: " + mUsername);
signInAndFetchBlogListWPOrg();
}
} else {
if (isUsernameEmail()) {
startProgress(getActivity().getString(R.string.checking_email));
mDispatcher.dispatch(AccountActionBuilder.newIsAvailableEmailAction(mUsername));
} else if (isWPComDomain(mUsername)) {
// If a wpcom domain was entered, check if the subdomain matches an existing username.
String maybeUsername = UrlUtils.extractSubDomain(mUsername);
if (maybeUsername.length() > 0) {
// See if the username exists.
startProgress(getActivity().getString(R.string.checking_username));
mDispatcher.dispatch(AccountActionBuilder.newIsAvailableUsernameAction(maybeUsername));
} else {
// The text that was entered was .wordpress.com or the like.
// Its invalid so just show an error.
showUsernameError(R.string.username_invalid);
}
} else {
showPasswordFieldAndFocus();
}
}
}
/**
* Tests the specified string to see if it contains a wpcom subdomain.
*
* @param string The string to check
* @return True if the string contains a wpcom subdomain, else false.
*/
private boolean isWPComDomain(String string) {
Matcher matcher = WPCOM_DOMAIN.matcher(string);
return matcher.find();
}
private boolean isUsernameEmail() {
mUsername = EditTextUtils.getText(mUsernameEditText).trim();
Pattern emailRegExPattern = Patterns.EMAIL_ADDRESS;
Matcher matcher = emailRegExPattern.matcher(mUsername);
return matcher.find() && mUsername.length() <= MAX_EMAIL_LENGTH;
}
private void requestSMSTwoStepCode() {
if (!isAdded()) return;
ToastUtils.showToast(getActivity(), R.string.two_step_sms_sent);
mTwoStepEditText.setText("");
mShouldSendTwoStepSMS = true;
signIn();
}
private final OnClickListener mSignInClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
signIn();
}
};
@Override
public void afterTextChanged(Editable s) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (fieldsFilled()) {
mSignInButton.setEnabled(true);
} else {
mSignInButton.setEnabled(false);
}
mPasswordEditText.setError(null);
mUsernameEditText.setError(null);
mTwoStepEditText.setError(null);
}
private boolean fieldsFilled() {
return EditTextUtils.getText(mUsernameEditText).trim().length() > 0
&& (mPasswordLayout.getVisibility() == View.GONE || EditTextUtils.getText(mPasswordEditText).trim().length() > 0)
&& (mTwoStepLayout.getVisibility() == View.GONE || EditTextUtils.getText(mTwoStepEditText).trim().length() > 0);
}
protected boolean isUserDataValid() {
final String username = EditTextUtils.getText(mUsernameEditText).trim();
final String password = EditTextUtils.getText(mPasswordEditText).trim();
final String url = EditTextUtils.getText(mUrlEditText).trim();
boolean retValue = true;
if (TextUtils.isEmpty(password)) {
mPasswordEditText.setError(getString(R.string.required_field));
mPasswordEditText.requestFocus();
retValue = false;
}
if (TextUtils.isEmpty(username)) {
mUsernameEditText.setError(getString(R.string.required_field));
mUsernameEditText.requestFocus();
retValue = false;
}
if (mIsSelfHostedForced && TextUtils.isEmpty(url)) {
mUrlEditText.setError(getString(R.string.required_field));
mUrlEditText.requestFocus();
retValue = false;
}
return retValue;
}
private void showPasswordError(int messageId) {
mPasswordEditText.setError(getString(messageId));
mPasswordEditText.requestFocus();
}
private void showUsernameError(int messageId) {
mUsernameEditText.setError(getString(messageId));
mUsernameEditText.requestFocus();
}
private void showUrlError(int messageId) {
mUrlEditText.setError(getString(messageId));
mUrlEditText.requestFocus();
}
private void showTwoStepCodeError(int messageId) {
mTwoStepEditText.setError(getString(messageId));
mTwoStepEditText.requestFocus();
}
protected boolean specificShowError(int messageId) {
switch (getErrorType(messageId)) {
case USERNAME:
case PASSWORD:
showPasswordError(messageId);
showUsernameError(messageId);
return true;
default:
return false;
}
}
protected void startProgress(String message) {
mProgressBarSignIn.setVisibility(View.VISIBLE);
mProgressTextSignIn.setVisibility(View.VISIBLE);
mSignInButton.setVisibility(View.GONE);
mProgressBarSignIn.setEnabled(false);
mProgressTextSignIn.setText(message);
mUsernameEditText.setEnabled(false);
mPasswordEditText.setEnabled(false);
mTwoStepEditText.setEnabled(false);
mUrlEditText.setEnabled(false);
mAddSelfHostedButton.setEnabled(false);
mCreateAccountButton.setEnabled(false);
mForgotPassword.setEnabled(false);
}
protected void endProgress() {
mProgressBarSignIn.setVisibility(View.GONE);
mProgressTextSignIn.setVisibility(View.GONE);
mSignInButton.setVisibility(View.VISIBLE);
mUsernameEditText.setEnabled(true);
mPasswordEditText.setEnabled(true);
mTwoStepEditText.setEnabled(true);
mUrlEditText.setEnabled(true);
mAddSelfHostedButton.setEnabled(true);
mCreateAccountButton.setEnabled(true);
mForgotPassword.setEnabled(true);
}
private void askForHttpAuthCredentials(@NonNull final String url) {
// Prompt for http credentials
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
alert.setTitle(R.string.http_authorization_required);
View httpAuth = getActivity().getLayoutInflater().inflate(R.layout.alert_http_auth, null);
final EditText usernameEditText = (EditText) httpAuth.findViewById(R.id.http_username);
final EditText passwordEditText = (EditText) httpAuth.findViewById(R.id.http_password);
alert.setView(httpAuth);
alert.setPositiveButton(R.string.sign_in, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
mHttpUsername = EditTextUtils.getText(usernameEditText);
mHttpPassword = EditTextUtils.getText(passwordEditText);
mHTTPAuthManager.addHTTPAuthCredentials(mHttpUsername, mHttpPassword, url, null);
signInAndFetchBlogListWPOrg();
}
});
alert.show();
endProgress();
}
protected void showInvalidUsernameOrPasswordDialog() {
// Show a dialog
FragmentTransaction ft = getFragmentManager().beginTransaction();
SignInDialogFragment nuxAlert;
// create a 3 buttons dialog ("Contact us", "Forget your password?" and "Cancel")
nuxAlert = SignInDialogFragment.newInstance(getString(org.wordpress.android.R.string.nux_cannot_log_in),
getString(org.wordpress.android.R.string.username_or_password_incorrect),
org.wordpress.android.R.drawable.ic_notice_white_64dp, 3, getString(
org.wordpress.android.R.string.cancel), getString(
org.wordpress.android.R.string.forgot_password), getString(
org.wordpress.android.R.string.contact_us), SignInDialogFragment.ACTION_OPEN_URL,
SignInDialogFragment.ACTION_OPEN_SUPPORT_CHAT);
// Put entered url and entered username args, that could help our support team
Bundle bundle = nuxAlert.getArguments();
bundle.putString(SignInDialogFragment.ARG_OPEN_URL_PARAM, getForgotPasswordURL());
bundle.putString(ENTERED_URL_KEY, EditTextUtils.getText(mUrlEditText));
bundle.putString(ENTERED_USERNAME_KEY, EditTextUtils.getText(mUsernameEditText));
bundle.putSerializable(HelpshiftHelper.ORIGIN_KEY, HelpshiftHelper.chooseHelpshiftLoginTag
(mJetpackCallbacks.isJetpackAuth(), isWPComLogin() && !mSelfHosted));
nuxAlert.setArguments(bundle);
ft.add(nuxAlert, "alert");
ft.commitAllowingStateLoss();
}
protected void handleInvalidUsernameOrPassword(int messageId) {
mErroneousLogInCount += 1;
if (mErroneousLogInCount >= WPCOM_ERRONEOUS_LOGIN_THRESHOLD) {
// Clear previous errors
mPasswordEditText.setError(null);
mUsernameEditText.setError(null);
showInvalidUsernameOrPasswordDialog();
} else {
showPasswordError(messageId);
showUsernameError(messageId);
}
endProgress();
}
private void showGenericErrorDialog(String errorMessage) {
showGenericErrorDialog(errorMessage, null, null);
}
private void showGenericErrorDialog(String errorMessage, String faqId, String faqSection) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
SignInDialogFragment nuxAlert;
int faqAction = SignInDialogFragment.ACTION_OPEN_SUPPORT_CHAT;
String thirdButtonLabel = getString(R.string.contact_us);
if (!TextUtils.isEmpty(faqId) || !TextUtils.isEmpty(faqSection)) {
faqAction = SignInDialogFragment.ACTION_OPEN_FAQ_PAGE;
thirdButtonLabel = getString(R.string.tell_me_more);
}
nuxAlert = SignInDialogFragment.newInstance(getString(org.wordpress.android.R.string.nux_cannot_log_in),
errorMessage, R.drawable.ic_notice_white_64dp, 3,
getString(R.string.cancel), getString(R.string.reader_title_applog), thirdButtonLabel,
SignInDialogFragment.ACTION_OPEN_APPLICATION_LOG,
faqAction, faqId, faqSection);
Bundle bundle = nuxAlert.getArguments();
bundle.putSerializable(HelpshiftHelper.ORIGIN_KEY, HelpshiftHelper.chooseHelpshiftLoginTag
(mJetpackCallbacks.isJetpackAuth(), isWPComLogin() && !mSelfHosted));
nuxAlert.setArguments(bundle);
ft.add(nuxAlert, "alert");
ft.commitAllowingStateLoss();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_IS_SELF_HOSTED, mSelfHosted);
}
@Override
public void onStart() {
super.onStart();
// Autofill username / password if string fields are set (only useful after an error in sign up).
// This can't be done in onCreateView
if (mUsername != null) {
mUsernameEditText.setText(mUsername);
}
if (mPassword != null) {
mPasswordEditText.setText(mPassword);
}
mDispatcher.register(this);
}
@Override
public void onStop() {
super.onStop();
mDispatcher.unregister(this);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == NewUserFragment.NEW_USER && resultCode == Activity.RESULT_OK) {
if (data != null) {
// Text views will be populated by username/password if these fields are set
mUsername = data.getStringExtra("username");
mPassword = data.getStringExtra("password");
}
}
}
// OnChanged events
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAccountChanged(OnAccountChanged event) {
if (event.isError()) {
AppLog.e(T.API, "onAccountChanged has error: " + event.error.type + " - " + event.error.message);
showAccountError(event.error.type, event.error.message);
endProgress();
return;
}
AppLog.i(T.NUX, "onAccountChanged: " + event.toString());
// Success
mAccountSettingsFetched |= event.causeOfChange == AccountAction.FETCH_SETTINGS;
mAccountFetched |= event.causeOfChange == AccountAction.FETCH_ACCOUNT;
// Finish activity if sites have been fetched
if (mSitesFetched && mAccountSettingsFetched && mAccountFetched) {
finishCurrentActivity();
}
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAuthenticationChanged(OnAuthenticationChanged event) {
if (event.isError()) {
AppLog.e(T.API, "onAuthenticationChanged has error: " + event.error.type + " - " + event.error.message);
AnalyticsTracker.track(Stat.LOGIN_FAILED, event.getClass().getSimpleName(), event.error.type.toString(), event.error.message);
showAuthError(event.error.type, event.error.message);
endProgress();
return;
}
AppLog.i(T.NUX, "onAuthenticationChanged: " + event.toString());
fetchAccountSettingsAndSites();
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onSiteChanged(OnSiteChanged event) {
if (event.isError()) {
AppLog.e(T.API, "onSiteChanged has error: " + event.error.type + " - " + event.error.toString());
endProgress();
if (!isAdded()) {
return;
}
if (event.error.type == SiteErrorType.DUPLICATE_SITE) {
if (event.rowsAffected == 0) {
// If there is a duplicate site and not any site has been added, show an error and
// stop the sign in process
ToastUtils.showToast(getContext(), R.string.cannot_add_duplicate_site);
return;
} else {
// If there is a duplicate site, notify the user something could be wrong,
// but continue the sign in process
ToastUtils.showToast(getContext(), R.string.duplicate_site_detected);
}
} else {
return;
}
}
// Login Successful
trackAnalyticsSignIn();
mSitesFetched = true;
// Finish activity if account settings have been fetched or if it's a wporg site
if ((mAccountSettingsFetched && mAccountFetched) || !isWPComLogin()) {
finishCurrentActivity();
}
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDiscoverySucceeded(OnDiscoveryResponse event) {
if (event.isError()) {
AppLog.e(T.API, "onDiscoveryResponse has error: " + event.error.name() + " - " + event.error.toString());
handleDiscoveryError(event.error, event.failedEndpoint);
AnalyticsTracker.track(Stat.LOGIN_FAILED, event.getClass().getSimpleName(), event.error.name(), event.error.toString());
return;
}
AppLog.i(T.NUX, "Discovery succeeded, endpoint: " + event.xmlRpcEndpoint);
RefreshSitesXMLRPCPayload selfhostedPayload = new RefreshSitesXMLRPCPayload();
selfhostedPayload.username = mUsername;
selfhostedPayload.password = mPassword;
selfhostedPayload.url = event.xmlRpcEndpoint;
mDispatcher.dispatch(SiteActionBuilder.newFetchSitesXmlRpcAction(selfhostedPayload));
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onAvailabilityChecked(OnAvailabilityChecked event) {
if (event.isError()) {
AppLog.e(T.API, "OnAvailabilityChecked has error: " + event.error.type + " - " + event.error.message);
}
switch(event.type) {
case EMAIL:
handleEmailAvailabilityEvent(event);
break;
case USERNAME:
handleUsernameAvailabilityEvent(event);
break;
default:
// Failsafe
showSelfHostedSignInForm();
}
}
/**
* Handler for an email availability event. If a user enters an email address for their
* username an API checks to see if it belongs to a wpcom account. If it exists the magic links
* flow is followed. Otherwise the self-hosted sign in form is shown.
* @param event
*/
private void handleEmailAvailabilityEvent(OnAvailabilityChecked event) {
if (event.type != IsAvailable.EMAIL) {
return;
}
if (!event.isAvailable) {
// Email address exists in WordPress.com
if (mListener != null) {
mListener.onMagicLinkRequestSuccess(mUsername);
}
} else {
// Email address doesn't exist in WordPress.com, show the self hosted sign in form
showSelfHostedSignInForm();
}
}
/**
* Handler for a username availability event. If a user enters a wpcom domain as their username
* an API call is made to check if the subdomain is a valid username. This method handles
* the result of that API call, showing an error if the subdomain was not a valid username,
* or showing the password field if it was a valid username.
*
* @param event
*/
private void handleUsernameAvailabilityEvent(OnAvailabilityChecked event) {
endProgress();
if (event.type != IsAvailable.USERNAME ) {
return;
}
if (event.isAvailable) {
// Username doesn't exist in WordPress.com, show just show an error.
showUsernameError(R.string.username_invalid);
return;
}
// Username exists in WordPress.com. Update the form and show the password field.
mUsername = event.value;
mUsernameEditText.setText(event.value);
showPasswordFieldAndFocus();
}
public void handleDiscoveryError(DiscoveryError error, final String failedEndpoint) {
AppLog.e(T.API, "Discover error: " + error);
endProgress();
if (!isAdded()) {
return;
}
switch (error) {
case ERRONEOUS_SSL_CERTIFICATE:
SelfSignedSSLUtils.showSSLWarningDialog(getActivity(), mMemorizingTrustManager,
new Callback() {
@Override
public void certificateTrusted() {
if (failedEndpoint == null) {
return;
}
// retry login with the same parameters
startProgress(getString(R.string.signing_in));
mDispatcher.dispatch(
AuthenticationActionBuilder.newDiscoverEndpointAction(failedEndpoint));
}
});
break;
case HTTP_AUTH_REQUIRED:
askForHttpAuthCredentials(failedEndpoint);
break;
case WORDPRESS_COM_SITE:
signInAndFetchBlogListWPCom();
break;
case NO_SITE_ERROR:
showGenericErrorDialog(getResources().getString(R.string.no_site_error),
NO_SITE_HELPSHIFT_FAQ_ID,
NO_SITE_HELPSHIFT_FAQ_SECTION);
break;
case INVALID_URL:
showUrlError(R.string.invalid_site_url_message);
AnalyticsTracker.track(Stat.LOGIN_INSERTED_INVALID_URL);
break;
case MISSING_XMLRPC_METHOD:
showGenericErrorDialog(getResources().getString(R.string.xmlrpc_missing_method_error),
MISSING_XMLRPC_METHOD_HELPSHIFT_FAQ_ID,
MISSING_XMLRPC_METHOD_HELPSHIFT_FAQ_SECTION);
break;
case XMLRPC_BLOCKED:
// use this to help the user a bit: pass the Helpshift page ID or section ID
// on the rest of the error cases in this switch
showGenericErrorDialog(getResources().getString(R.string.xmlrpc_post_blocked_error),
XMLRPC_BLOCKED_HELPSHIFT_FAQ_ID,
XMLRPC_BLOCKED_HELPSHIFT_FAQ_SECTION);
break;
case XMLRPC_FORBIDDEN:
showGenericErrorDialog(getResources().getString(R.string.xmlrpc_endpoint_forbidden_error),
XMLRPC_BLOCKED_HELPSHIFT_FAQ_ID,
XMLRPC_BLOCKED_HELPSHIFT_FAQ_SECTION);
break;
case GENERIC_ERROR:
default:
showGenericErrorDialog(getResources().getString(R.string.nux_cannot_log_in));
break;
}
}
private void showAccountError(AccountStore.AccountErrorType error, String errorMessage) {
switch (error) {
case ACCOUNT_FETCH_ERROR:
showError(R.string.error_fetch_my_profile);
break;
case SETTINGS_FETCH_ERROR:
showError(R.string.error_fetch_account_settings);
break;
case SETTINGS_POST_ERROR:
showError(R.string.error_post_account_settings);
break;
case GENERIC_ERROR:
default:
showError(errorMessage);
break;
}
}
private void showAuthError(AuthenticationErrorType error, String errorMessage) {
switch (error) {
case INCORRECT_USERNAME_OR_PASSWORD:
case NOT_AUTHENTICATED: // NOT_AUTHENTICATED is the generic error from XMLRPC response on first call.
handleInvalidUsernameOrPassword(R.string.username_or_password_incorrect);
break;
case INVALID_OTP:
showTwoStepCodeError(R.string.invalid_verification_code);
break;
case NEEDS_2FA:
setTwoStepAuthVisibility(true);
mTwoStepEditText.setText(getAuthCodeFromClipboard());
break;
case INVALID_REQUEST:
// TODO: FluxC: could be specific?
default:
// For all other kind of error, show a dialog with API Response error message
AppLog.e(T.NUX, "Server response: " + errorMessage);
showGenericErrorDialog(errorMessage);
break;
}
}
private void fetchAccountSettingsAndSites() {
if (mAccountStore.hasAccessToken()) {
// Fetch user infos
mDispatcher.dispatch(AccountActionBuilder.newFetchAccountAction());
mDispatcher.dispatch(AccountActionBuilder.newFetchSettingsAction());
// Fetch sites
mDispatcher.dispatch(SiteActionBuilder.newFetchSitesAction());
// Start Notification service
NotificationsUpdateService.startService(getActivity().getApplicationContext());
}
}
}