From fe3f2f713091f5808df416e29e6c61341a09e69d Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:17:00 -0700 Subject: [PATCH 01/71] Remove LoginEmailPasswordFragment --- .../org/wordpress/android/e2e/LoginTests.kt | 30 +- .../wordpress/android/e2e/flows/LoginFlow.kt | 15 - .../wordpress/android/support/BaseTest.java | 2 +- .../android/ui/accounts/LoginActivity.java | 57 +-- .../login/LoginEmailPasswordFragment.java | 424 ------------------ .../android/login/LoginGoogleFragment.java | 4 +- .../android/login/LoginListener.java | 10 +- .../login/LoginMagicLinkRequestFragment.java | 26 +- .../login/LoginMagicLinkSentFragment.java | 21 - .../android/login/SignupGoogleFragment.java | 16 +- .../android/login/di/LoginFragmentModule.java | 4 - .../layout/login_email_password_screen.xml | 69 --- .../login_magic_link_request_screen.xml | 9 - .../layout/login_magic_link_sent_screen.xml | 9 - libs/login/src/main/res/values/strings.xml | 1 + 15 files changed, 22 insertions(+), 675 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginEmailPasswordFragment.java delete mode 100644 libs/login/src/main/res/layout/login_email_password_screen.xml diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt index dd9380378e54..8a53986021e9 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt @@ -17,10 +17,10 @@ class LoginTests : BaseTest() { } @Test - fun e2eLoginWithEmailPassword() { + fun e2eLoginWithMagicLink() { LoginFlow().chooseContinueWithWpCom(super.mComposeTestRule) .enterEmailAddress(E2ECredentials.WP_COM_USER_EMAIL) - .enterPassword(E2ECredentials.WP_COM_USER_PASSWORD) + .openMagicLink() .confirmLogin() ComposeEspressoLink().unregister() @@ -36,32 +36,6 @@ class LoginTests : BaseTest() { ComposeEspressoLink().unregister() } - @Test - fun e2eLoginWithSiteAddress() { - LoginFlow().chooseEnterYourSiteAddress(super.mComposeTestRule) - .enterSiteAddress(E2ECredentials.WP_COM_USER_SITE_ADDRESS) - .enterEmailAddress(E2ECredentials.WP_COM_USER_EMAIL) - .enterPassword(E2ECredentials.WP_COM_USER_PASSWORD) - .confirmLogin() - - ComposeEspressoLink().unregister() - } - - @Test - fun e2eLoginWithMagicLink() { - try { - LoginFlow().chooseContinueWithWpCom(super.mComposeTestRule) - .enterEmailAddress(E2ECredentials.WP_COM_USER_EMAIL) - .chooseMagicLink() - .openMagicLink() - .confirmLogin() - - ComposeEspressoLink().unregister() - } finally { - logoutIfNecessary() - } - } - @Test fun e2eLoginWithSelfHostedAccount() { LoginFlow().chooseEnterYourSiteAddress(super.mComposeTestRule) diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt index a8dcf13ecd86..4e7e0228c79c 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt @@ -35,21 +35,6 @@ class LoginFlow { return this } - fun enterPassword(password: String?): LoginFlow { - // Password Screen – Fill it in and click "Continue" - // See LoginEmailPasswordFragment - WPSupportUtils.populateTextField(R.id.input, password) - WPSupportUtils.clickOn(R.id.bottom_button) - return this - } - - fun chooseMagicLink(): LoginFlow { - // Password Screen – Choose "Get a login link by email" - // See LoginEmailPasswordFragment - WPSupportUtils.clickOn(LoginR.id.login_get_email_link) - return this - } - fun openMagicLink(): LoginFlow { // Magic Link Sent Screen – Should see "Check email" button // See LoginMagicLinkSentFragment diff --git a/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java b/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java index b36dfd6fba4d..3cd170b9e087 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java +++ b/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java @@ -99,7 +99,7 @@ protected void wpLogin() { logoutIfNecessary(); new LoginFlow().chooseContinueWithWpCom(mComposeTestRule) .enterEmailAddress(WP_COM_USER_EMAIL) - .enterPassword(WP_COM_USER_PASSWORD) + .openMagicLink() .confirmLogin(); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 642838e2e651..3e745cc4c601 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -44,7 +44,6 @@ import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginAnalyticsListener; import org.wordpress.android.login.LoginEmailFragment; -import org.wordpress.android.login.LoginEmailPasswordFragment; import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMagicLinkRequestFragment; @@ -610,36 +609,17 @@ public void onTermsOfServiceClicked() { @Override public void gotWpcomEmail(String email, boolean verifyEmail, @Nullable AuthOptions authOptions) { initSmartLockIfNotFinished(false); - boolean isMagicLinkEnabled = - getLoginMode() != LoginMode.WPCOM_LOGIN_DEEPLINK && getLoginMode() != LoginMode.SHARE_INTENT; - if (authOptions != null) { - if (authOptions.isPasswordless()) { - showMagicLinkRequestScreen(email, verifyEmail, false, true); - } else { - showEmailPasswordScreen(email, verifyEmail, isMagicLinkEnabled); - } - } else { - if (isMagicLinkEnabled) { - showMagicLinkRequestScreen(email, verifyEmail, true, false); - } else { - showEmailPasswordScreen(email, verifyEmail, false); - } - } - } - - private void showEmailPasswordScreen(String email, boolean verifyEmail, boolean allowMagicLink) { - LoginEmailPasswordFragment loginEmailPasswordFragment = LoginEmailPasswordFragment - .newInstance(email, null, null, null, false, allowMagicLink, verifyEmail); - slideInFragment(loginEmailPasswordFragment, true, LoginEmailPasswordFragment.TAG); + boolean forceRequestAtStart = authOptions != null && authOptions.isPasswordless(); + showMagicLinkRequestScreen(email, verifyEmail, forceRequestAtStart); } - private void showMagicLinkRequestScreen(String email, boolean verifyEmail, boolean allowPassword, + private void showMagicLinkRequestScreen(String email, boolean verifyEmail, boolean forceRequestAtStart) { AuthEmailPayloadScheme scheme = mViewModel.getMagicLinkScheme(); String jetpackConnectionSource = mJetpackConnectSource != null ? mJetpackConnectSource.toString() : null; LoginMagicLinkRequestFragment loginMagicLinkRequestFragment = LoginMagicLinkRequestFragment - .newInstance(email, scheme, mIsJetpackConnect, jetpackConnectionSource, verifyEmail, allowPassword, + .newInstance(email, scheme, mIsJetpackConnect, jetpackConnectionSource, verifyEmail, forceRequestAtStart); slideInFragment(loginMagicLinkRequestFragment, true, LoginMagicLinkRequestFragment.TAG); } @@ -662,13 +642,6 @@ public void loginViaSiteAddress() { slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); } - @Override - public void loginViaSocialAccount(String email, String idToken, String service, boolean isPasswordRequired) { - LoginEmailPasswordFragment loginEmailPasswordFragment = - LoginEmailPasswordFragment.newInstance(email, null, idToken, service, isPasswordRequired); - slideInFragment(loginEmailPasswordFragment, true, LoginEmailPasswordFragment.TAG); - } - @Override public void loggedInViaSocialAccount(ArrayList oldSitesIds, boolean doLoginUpdate) { mLoginAnalyticsListener.trackLoginSocialSuccess(); @@ -681,9 +654,9 @@ public void loginViaWpcomUsernameInstead() { } @Override - public void showMagicLinkSentScreen(String email, boolean allowPassword) { + public void showMagicLinkSentScreen(String email) { LoginMagicLinkSentFragment loginMagicLinkSentFragment = - LoginMagicLinkSentFragment.newInstance(email, allowPassword); + LoginMagicLinkSentFragment.newInstance(email); slideInFragment(loginMagicLinkSentFragment, true, LoginMagicLinkSentFragment.TAG); } @@ -721,25 +694,12 @@ public void openEmailClient(boolean isLogin) { } } - @Override - public void usePasswordInstead(String email) { - mLoginAnalyticsListener.trackLoginMagicLinkExited(); - LoginEmailPasswordFragment loginEmailPasswordFragment = - LoginEmailPasswordFragment.newInstance(email, null, null, null, false); - slideInFragment(loginEmailPasswordFragment, true, LoginEmailPasswordFragment.TAG); - } - @Override public void forgotPassword(String url) { mLoginAnalyticsListener.trackLoginForgotPasswordClicked(); ActivityLauncher.openUrlExternal(this, url + FORGOT_PASSWORD_URL_SUFFIX); } - @Override - public void useMagicLinkInstead(String email, boolean verifyEmail) { - showMagicLinkRequestScreen(email, verifyEmail, false, true); - } - @Override public void needs2fa(String email, String password) { Login2FaFragment login2FaFragment = Login2FaFragment.newInstance(email, password); @@ -883,11 +843,6 @@ public void helpMagicLinkSent(String email) { viewHelp(Origin.LOGIN_MAGIC_LINK); } - @Override - public void helpEmailPasswordScreen(String email) { - viewHelp(Origin.LOGIN_EMAIL_PASSWORD); - } - @Override public void help2FaScreen(String email) { viewHelp(Origin.LOGIN_2FA); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailPasswordFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailPasswordFragment.java deleted file mode 100644 index 62cfc53e7503..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailPasswordFragment.java +++ /dev/null @@ -1,424 +0,0 @@ -package org.wordpress.android.login; - -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.login.LoginWpcomService.LoginState; -import org.wordpress.android.login.LoginWpcomService.OnCredentialsOK; -import org.wordpress.android.login.LoginWpcomService.TwoFactorRequested; -import org.wordpress.android.login.util.AvatarHelper; -import org.wordpress.android.login.util.AvatarHelper.AvatarRequestListener; -import org.wordpress.android.login.util.SiteUtils; -import org.wordpress.android.login.widgets.WPLoginInputRow; -import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.AutoForeground; -import org.wordpress.android.util.NetworkUtils; -import org.wordpress.android.util.ToastUtils; -import org.wordpress.android.util.ToastUtils.Duration; - -import java.util.ArrayList; - -import dagger.android.support.AndroidSupportInjection; - -public class LoginEmailPasswordFragment extends LoginBaseFormFragment implements TextWatcher, - OnEditorCommitListener { - private static final String KEY_REQUESTED_PASSWORD = "KEY_REQUESTED_PASSWORD"; - private static final String KEY_OLD_SITES_IDS = "KEY_OLD_SITES_IDS"; - - protected static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - protected static final String ARG_PASSWORD = "ARG_PASSWORD"; - protected static final String ARG_SOCIAL_ID_TOKEN = "ARG_SOCIAL_ID_TOKEN"; - protected static final String ARG_SOCIAL_LOGIN = "ARG_SOCIAL_LOGIN"; - protected static final String ARG_SOCIAL_SERVICE = "ARG_SOCIAL_SERVICE"; - protected static final String ARG_ALLOW_MAGIC_LINK = "ARG_ALLOW_MAGIC_LINK"; - protected static final String ARG_VERIFY_MAGIC_LINK_EMAIL = "ARG_VERIFY_MAGIC_LINK_EMAIL"; - - private static final String FORGOT_PASSWORD_URL_WPCOM = "https://wordpress.com/"; - - public static final String TAG = "login_email_password_fragment_tag"; - - private WPLoginInputRow mPasswordInput; - - private String mRequestedPassword; - ArrayList mOldSitesIDs; - - private String mEmailAddress; - private String mIdToken; - private String mPassword; - private String mService; - private boolean mIsSocialLogin; - private boolean mAllowMagicLink; - private boolean mVerifyMagicLinkEmail; - - private AutoForeground.ServiceEventConnection mServiceEventConnection; - - public static LoginEmailPasswordFragment newInstance(String emailAddress, String password, - String idToken, String service, - boolean isSocialLogin) { - return newInstance(emailAddress, password, idToken, service, isSocialLogin, false, false); - } - - public static LoginEmailPasswordFragment newInstance(String emailAddress, String password, String idToken, - String service, boolean isSocialLogin, boolean allowMagicLink, - boolean verifyMagicLinkEmail) { - LoginEmailPasswordFragment fragment = new LoginEmailPasswordFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, emailAddress); - args.putString(ARG_PASSWORD, password); - args.putString(ARG_SOCIAL_ID_TOKEN, idToken); - args.putString(ARG_SOCIAL_SERVICE, service); - args.putBoolean(ARG_SOCIAL_LOGIN, isSocialLogin); - args.putBoolean(ARG_ALLOW_MAGIC_LINK, allowMagicLink); - args.putBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL, verifyMagicLinkEmail); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getArguments() != null) { - mEmailAddress = getArguments().getString(ARG_EMAIL_ADDRESS); - mPassword = getArguments().getString(ARG_PASSWORD); - mIdToken = getArguments().getString(ARG_SOCIAL_ID_TOKEN); - mService = getArguments().getString(ARG_SOCIAL_SERVICE); - mIsSocialLogin = getArguments().getBoolean(ARG_SOCIAL_LOGIN); - mAllowMagicLink = getArguments().getBoolean(ARG_ALLOW_MAGIC_LINK); - mVerifyMagicLinkEmail = getArguments().getBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL); - } - - if (savedInstanceState == null) { - // cleanup the service state on first appearance - LoginWpcomService.clearLoginServiceState(); - } else { - mRequestedPassword = savedInstanceState.getString(KEY_REQUESTED_PASSWORD); - } - } - - @Override - public void onResume() { - super.onResume(); - mAnalyticsListener.emailPasswordFormScreenResumed(); - updatePrimaryButtonEnabledStatus(); - - // connect to the Service. We'll receive updates via EventBus. - mServiceEventConnection = new AutoForeground.ServiceEventConnection(getContext(), - LoginWpcomService.class, this); - - // install the change listener as late as possible so the UI can be setup (updated from the Service state) - // before triggering the state cleanup happening in the change listener. - mPasswordInput.addTextChangedListener(this); - } - - @Override - public void onPause() { - super.onPause(); - - // disconnect from the Service - mServiceEventConnection.disconnect(getContext(), this); - } - - private void updatePrimaryButtonEnabledStatus() { - String currentPassword = mPasswordInput.getEditText().getText().toString(); - getBottomButton().setEnabled(!currentPassword.trim().isEmpty()); - } - - @Override - protected boolean listenForLogin() { - return false; - } - - @Override - protected @LayoutRes int getContentLayout() { - return R.layout.login_email_password_screen; - } - - @Override - protected @LayoutRes int getProgressBarText() { - return R.string.logging_in; - } - - @Override - protected void setupLabel(@NonNull TextView label) { - label.setText(mIsSocialLogin ? R.string.enter_wpcom_password_google : R.string.enter_wpcom_password); - } - - @Override - protected void setupContent(ViewGroup rootView) { - // important for accessibility - talkback - getActivity().setTitle(R.string.selfhosted_site_login_title); - - mPasswordInput = rootView.findViewById(R.id.login_password_row); - mPasswordInput.setOnEditorCommitListener(this); - - rootView.findViewById(R.id.login_reset_password).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mLoginListener != null) { - mLoginListener.forgotPassword(FORGOT_PASSWORD_URL_WPCOM); - } - } - }); - - final View divider = rootView.findViewById(R.id.login_button_divider); - divider.setVisibility(mAllowMagicLink ? View.VISIBLE : View.GONE); - - final Button magicLinkButton = rootView.findViewById(R.id.login_get_email_link); - magicLinkButton.setVisibility(mAllowMagicLink ? View.VISIBLE : View.GONE); - magicLinkButton.setOnClickListener(new OnClickListener() { - @Override public void onClick(View v) { - if (mLoginListener != null) { - mAnalyticsListener.trackRequestMagicLinkClick(); - mLoginListener.useMagicLinkInstead(mEmailAddress, mVerifyMagicLinkEmail); - } - } - }); - - final ProgressBar avatarProgressBar = rootView.findViewById(R.id.avatar_progress); - final ImageView avatarView = rootView.findViewById(R.id.gravatar); - final TextView emailView = rootView.findViewById(R.id.email); - - emailView.setText(mEmailAddress); - - AvatarHelper.loadAvatarFromEmail(this, mEmailAddress, avatarView, new AvatarRequestListener() { - @Override public void onRequestFinished() { - avatarProgressBar.setVisibility(View.GONE); - } - }); - } - - @Override - protected void buildToolbar(Toolbar toolbar, ActionBar actionBar) { - actionBar.setTitle(R.string.log_in); - } - - @Override - protected void setupBottomButton(Button button) { - button.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - next(); - } - }); - } - - @Override - protected EditText getEditTextToFocusOnStart() { - return mPasswordInput.getEditText(); - } - - @Override - protected void onHelp() { - if (mLoginListener != null) { - mLoginListener.helpEmailPasswordScreen(mEmailAddress); - } - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (savedInstanceState == null) { - mAnalyticsListener.trackPasswordFormViewed(mIsSocialLogin); - - if (!TextUtils.isEmpty(mPassword)) { - mPasswordInput.setText(mPassword); - } else { - if (BuildConfig.DEBUG) { - mPasswordInput.getEditText().setText(BuildConfig.DEBUG_WPCOM_LOGIN_PASSWORD); - } - } - } else { - mOldSitesIDs = savedInstanceState.getIntegerArrayList(KEY_OLD_SITES_IDS); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putString(KEY_REQUESTED_PASSWORD, mRequestedPassword); - outState.putIntegerArrayList(KEY_OLD_SITES_IDS, mOldSitesIDs); - } - - protected void next() { - mAnalyticsListener.trackSubmitClicked(); - - if (!NetworkUtils.checkConnection(getActivity())) { - return; - } - - startProgress(false); - - mRequestedPassword = mPasswordInput.getEditText().getText().toString(); - - LoginWpcomService.loginWithEmailAndPassword( - getContext(), - mEmailAddress, - mRequestedPassword, - mIdToken, - mService, - mIsSocialLogin, - mLoginListener.getLoginMode() == LoginMode.JETPACK_LOGIN_ONLY, - mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE - ); - mOldSitesIDs = SiteUtils.getCurrentSiteIds(mSiteStore, false); - } - - @Override - public void onEditorCommit() { - mPasswordInput.setError(null); - next(); - } - - @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) { - mPasswordInput.setError(null); - - LoginWpcomService.clearLoginServiceState(); - updatePrimaryButtonEnabledStatus(); - } - - private void showPasswordError() { - String message = getString(R.string.password_incorrect); - mAnalyticsListener.trackFailure(message); - mPasswordInput.setError(message); - } - - private void showError(String error) { - mAnalyticsListener.trackFailure(error); - mPasswordInput.setError(error); - } - - @Override - protected void onLoginFinished() { - mAnalyticsListener.trackAnalyticsSignIn(true); - mLoginListener.startPostLoginServices(); - - if (mIsSocialLogin) { - mLoginListener.loggedInViaSocialAccount(mOldSitesIDs, false); - } else { - mLoginListener.loggedInViaPassword(mOldSitesIDs); - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onCredentialsOK(OnCredentialsOK event) { - saveCredentialsInSmartLock(mLoginListener, mEmailAddress, mRequestedPassword); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onTwoFactorAuthStarted(TwoFactorRequested event) { - onLoginFinished(false); - mLoginListener.needs2fa(mEmailAddress, mRequestedPassword, event.userId, - event.webauthnNonce, event.authenticatorNonce, event.backupNonce, - event.pushNonce, event.supportedAuthTypes); - LoginWpcomService.clearLoginServiceState(); - } - - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) - public void onLoginStateUpdated(LoginState loginState) { - AppLog.i(T.NUX, "Received state: " + loginState.getStepName()); - - switch (loginState.getStep()) { - case IDLE: - // nothing special to do, we'll start the service on next() - break; - case AUTHENTICATING: - case SOCIAL_LOGIN: - case FETCHING_ACCOUNT: - case FETCHING_SETTINGS: - case FETCHING_SITES: - if (!isInProgress()) { - startProgress(); - } - break; - case FAILURE_EMAIL_WRONG_PASSWORD: - onLoginFinished(false); - showPasswordError(); - break; - case FAILURE_2FA: - onLoginFinished(false); - mLoginListener.needs2fa(mEmailAddress, mRequestedPassword); - - // consume the state so we don't relauch the 2FA dialog if user backs up - LoginWpcomService.clearLoginServiceState(); - break; - case FAILURE_SOCIAL_2FA: - onLoginFinished(false); - mLoginListener.needs2faSocialConnect(mEmailAddress, mRequestedPassword, mIdToken, mService); - - // consume the state so we don't relauch the 2FA dialog if user backs up - LoginWpcomService.clearLoginServiceState(); - break; - case SECURITY_KEY_NEEDED: - onLoginFinished(false); - // consume the state so we don't relauch the 2FA dialog if user backs up - LoginWpcomService.clearLoginServiceState(); - break; - case FAILURE_FETCHING_ACCOUNT: - onLoginFinished(false); - showError(getString(R.string.error_fetch_my_profile)); - break; - case FAILURE_CANNOT_ADD_DUPLICATE_SITE: - onLoginFinished(false); - showError(getString(R.string.cannot_add_duplicate_site)); - break; - case FAILURE_USE_WPCOM_USERNAME_INSTEAD_OF_EMAIL: - onLoginFinished(false); - mLoginListener.loginViaWpcomUsernameInstead(); - ToastUtils.showToast(getContext(), R.string.error_user_username_instead_of_email, Duration.LONG); - - mAnalyticsListener.trackFailure(loginState.getStep().name()); - // consume the state so we don't re-redirect to username login if user backs up - LoginWpcomService.clearLoginServiceState(); - break; - case FAILURE: - onLoginFinished(false); - showError(getString(R.string.error_generic)); - break; - case SUCCESS: - onLoginFinished(true); - break; - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java index 2bf5224932af..a8da875c28f9 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java @@ -207,9 +207,9 @@ public void onSocialChanged(OnSocialChanged event) { switch (event.error.type) { // WordPress account exists with input email address, but not connected. case USER_EXISTS: - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - wordpress acount exists but not connected"); + AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - wordpress account exists but not connected"); mAnalyticsListener.trackSocialAccountsNeedConnecting(); - mLoginListener.loginViaSocialAccount(mGoogleEmail, mIdToken, SERVICE_TYPE_GOOGLE, true); + showError(getString(R.string.login_error_email_not_connected_to_google)); break; // WordPress account does not exist with input email address. case UNKNOWN_USER: diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 25e85e121ec4..8cd20b148a39 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -26,7 +26,6 @@ interface SelfSignedSSLCallback { void gotUnregisteredSocialAccount(String email, String displayName, String idToken, String photoUrl, String service); void loginViaSiteAddress(); - void loginViaSocialAccount(String email, String idToken, String service, boolean isPasswordRequired); void loggedInViaSocialAccount(ArrayList oldSiteIds, boolean doLoginUpdate); void loginViaWpcomUsernameInstead(); void loginViaSiteCredentials(String inputSiteAddress); @@ -37,17 +36,14 @@ void gotUnregisteredSocialAccount(String email, String displayName, String idTok void onTermsOfServiceClicked(); // Login Request Magic Link callbacks - void showMagicLinkSentScreen(String email, boolean allowPassword); - void usePasswordInstead(String email); + void showMagicLinkSentScreen(String email); void helpMagicLinkRequest(String email); // Login Magic Link Sent callbacks void openEmailClient(boolean isLogin); void helpMagicLinkSent(String email); - // Login email password callbacks - void forgotPassword(String url); - void useMagicLinkInstead(String email, boolean verifyEmail); + // Login 2FA callbacks void needs2fa(String email, String password); void needs2fa(String email, String password, String userId, String webauthnNonce, String nonceAuthenticator, String nonceBackup, String noncePush, @@ -56,7 +52,6 @@ void needs2faSocial(String email, String userId, String nonceAuthenticator, Stri String nonceSms, String nonceWebauthn, List supportedAuthTypes); void needs2faSocialConnect(String email, String password, String idToken, String service); void loggedInViaPassword(ArrayList oldSitesIds); - void helpEmailPasswordScreen(String email); // Login Site Address input callbacks void alreadyLoggedInWpcom(ArrayList oldSitesIds); @@ -69,6 +64,7 @@ void needs2faSocial(String email, String userId, String nonceAuthenticator, Stri void handleSiteAddressError(ConnectSiteInfoPayload siteInfo); // Login username password callbacks + void forgotPassword(String url); void saveCredentialsInSmartLock(@Nullable String username, @Nullable String password, @NonNull String displayName, @Nullable Uri profilePicture); void loggedInViaUsernamePassword(ArrayList oldSitesIds); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java index 31ff1ebc1988..509ff7341662 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java @@ -52,7 +52,6 @@ public class LoginMagicLinkRequestFragment extends Fragment { private static final String ARG_IS_JETPACK_CONNECT = "ARG_IS_JETPACK_CONNECT"; private static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; private static final String ARG_VERIFY_MAGIC_LINK_EMAIL = "ARG_VERIFY_MAGIC_LINK_EMAIL"; - private static final String ARG_ALLOW_PASSWORD = "ARG_ALLOW_PASSWORD"; private static final String ARG_FORCE_REQUEST_AT_START = "ARG_FORCE_REQUEST_AT_START"; private static final String ERROR_KEY = "error"; @@ -70,7 +69,6 @@ public class LoginMagicLinkRequestFragment extends Fragment { private boolean mInProgress; private boolean mIsJetpackConnect; private boolean mVerifyMagicLinkEmail; - private boolean mAllowPassword; private boolean mForceRequestAtStart; @Inject protected Dispatcher mDispatcher; @@ -79,13 +77,7 @@ public class LoginMagicLinkRequestFragment extends Fragment { public static LoginMagicLinkRequestFragment newInstance(String email, AuthEmailPayloadScheme scheme, boolean isJetpackConnect, String jetpackConnectSource, - boolean verifyEmail) { - return newInstance(email, scheme, isJetpackConnect, jetpackConnectSource, verifyEmail, true, false); - } - - public static LoginMagicLinkRequestFragment newInstance(String email, AuthEmailPayloadScheme scheme, - boolean isJetpackConnect, String jetpackConnectSource, - boolean verifyEmail, boolean allowPassword, + boolean verifyEmail, boolean forceRequestAtStart) { LoginMagicLinkRequestFragment fragment = new LoginMagicLinkRequestFragment(); Bundle args = new Bundle(); @@ -94,7 +86,6 @@ public static LoginMagicLinkRequestFragment newInstance(String email, AuthEmailP args.putBoolean(ARG_IS_JETPACK_CONNECT, isJetpackConnect); args.putString(ARG_JETPACK_CONNECT_SOURCE, jetpackConnectSource); args.putBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL, verifyEmail); - args.putBoolean(ARG_ALLOW_PASSWORD, allowPassword); args.putBoolean(ARG_FORCE_REQUEST_AT_START, forceRequestAtStart); fragment.setArguments(args); return fragment; @@ -121,7 +112,6 @@ public void onCreate(Bundle savedInstanceState) { mIsJetpackConnect = getArguments().getBoolean(ARG_IS_JETPACK_CONNECT); mJetpackConnectSource = getArguments().getString(ARG_JETPACK_CONNECT_SOURCE); mVerifyMagicLinkEmail = getArguments().getBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL); - mAllowPassword = getArguments().getBoolean(ARG_ALLOW_PASSWORD); mForceRequestAtStart = getArguments().getBoolean(ARG_FORCE_REQUEST_AT_START); } @@ -140,18 +130,6 @@ public void onClick(View v) { } }); - final Button passwordButton = view.findViewById(R.id.login_enter_password); - passwordButton.setVisibility(mAllowPassword ? View.VISIBLE : View.GONE); - passwordButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAnalyticsListener.trackLoginWithPasswordClick(); - if (mLoginListener != null) { - mLoginListener.usePasswordInstead(mEmail); - } - } - }); - mAvatarProgressBar = view.findViewById(R.id.avatar_progress); ImageView avatarView = view.findViewById(R.id.gravatar); @@ -365,7 +343,7 @@ public void onAuthEmailSent(OnAuthEmailSent event) { fragmentManager.popBackStack(); } } - mLoginListener.showMagicLinkSentScreen(mEmail, mAllowPassword); + mLoginListener.showMagicLinkSentScreen(mEmail); } } } diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java index 54fb001e851b..bdbc006706c0 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java @@ -8,7 +8,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -29,24 +28,17 @@ public class LoginMagicLinkSentFragment extends Fragment { public static final String TAG = "login_magic_link_sent_fragment_tag"; private static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - private static final String ARG_ALLOW_PASSWORD = "ARG_ALLOW_PASSWORD"; private LoginListener mLoginListener; private String mEmail; - private boolean mAllowPassword; @Inject protected LoginAnalyticsListener mAnalyticsListener; public static LoginMagicLinkSentFragment newInstance(String email) { - return newInstance(email, true); - } - - public static LoginMagicLinkSentFragment newInstance(String email, boolean allowPassword) { LoginMagicLinkSentFragment fragment = new LoginMagicLinkSentFragment(); Bundle args = new Bundle(); args.putString(ARG_EMAIL_ADDRESS, email); - args.putBoolean(ARG_ALLOW_PASSWORD, allowPassword); fragment.setArguments(args); return fragment; } @@ -56,7 +48,6 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mEmail = getArguments().getString(ARG_EMAIL_ADDRESS); - mAllowPassword = getArguments().getBoolean(ARG_ALLOW_PASSWORD); } setHasOptionsMenu(true); @@ -75,18 +66,6 @@ public void onClick(View v) { } }); - final Button passwordButton = view.findViewById(R.id.login_enter_password); - passwordButton.setVisibility(mAllowPassword ? View.VISIBLE : View.GONE); - passwordButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAnalyticsListener.trackLoginWithPasswordClick(); - if (mLoginListener != null) { - mLoginListener.usePasswordInstead(mEmail); - } - } - }); - final View avatarProgressBar = view.findViewById(R.id.avatar_progress); ImageView avatarView = view.findViewById(R.id.gravatar); diff --git a/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java b/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java index f0e82e500eb9..55d75b378b12 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java @@ -252,7 +252,8 @@ public void onSocialChanged(OnSocialChanged event) { // WordPress account exists with input email address, but not connected. case USER_EXISTS: AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - error - user already exists"); - loginViaSocialAccount(); + mAnalyticsListener.trackSignupSocialAccountsNeedConnecting(); + showError(getString(R.string.login_error_email_not_connected_to_google)); break; // Too many attempts on sending SMS verification code. The user has to wait before they try again case SMS_CODE_THROTTLED: @@ -272,16 +273,9 @@ public void onSocialChanged(OnSocialChanged event) { event.nonceSms, event.nonceWebauthn, event.twoStepTypes); finishFlow(); } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - google login success"); - loginViaSocialAccount(); + AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - account exists but not connected"); + mAnalyticsListener.trackSignupSocialAccountsNeedConnecting(); + showError(getString(R.string.login_error_email_not_connected_to_google)); } } - - private void loginViaSocialAccount() { - mAnalyticsListener.trackSignupSocialAccountsNeedConnecting(); - mAnalyticsListener.trackSignupSocialToLogin(); - mLoginListener.showSignupToLoginMessage(); - mLoginListener.loginViaSocialAccount(mGoogleEmail, mIdToken, SERVICE_TYPE_GOOGLE, true); - finishFlow(); - } } diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index bc4519328222..0c2cbe7c4cc4 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -2,7 +2,6 @@ import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginEmailFragment; -import org.wordpress.android.login.LoginEmailPasswordFragment; import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginMagicLinkRequestFragment; import org.wordpress.android.login.LoginMagicLinkSentFragment; @@ -23,9 +22,6 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract LoginEmailFragment loginEmailFragment(); - @ContributesAndroidInjector - abstract LoginEmailPasswordFragment loginEmailPasswordFragment(); - @ContributesAndroidInjector abstract LoginGoogleFragment loginGoogleFragment(); diff --git a/libs/login/src/main/res/layout/login_email_password_screen.xml b/libs/login/src/main/res/layout/login_email_password_screen.xml deleted file mode 100644 index fee59cd210b4..000000000000 --- a/libs/login/src/main/res/layout/login_email_password_screen.xml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/layout/login_magic_link_request_screen.xml b/libs/login/src/main/res/layout/login_magic_link_request_screen.xml index 8b25988d2a9e..1eaa2eb1b24c 100644 --- a/libs/login/src/main/res/layout/login_magic_link_request_screen.xml +++ b/libs/login/src/main/res/layout/login_magic_link_request_screen.xml @@ -32,15 +32,6 @@ android:layout_marginTop="@dimen/margin_extra_medium_large" android:text="@string/login_magic_links_label" /> - - diff --git a/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml b/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml index f9192b18a9eb..5205985939b8 100644 --- a/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml +++ b/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml @@ -39,15 +39,6 @@ android:layout_marginTop="@dimen/margin_extra_medium_large" android:text="@string/magic_link_not_seeing_email_message" /> - - diff --git a/libs/login/src/main/res/values/strings.xml b/libs/login/src/main/res/values/strings.xml index dcabee6810ba..c43ee8333cb4 100644 --- a/libs/login/src/main/res/values/strings.xml +++ b/libs/login/src/main/res/values/strings.xml @@ -62,6 +62,7 @@ or Close There\'s no WordPress.com account matching this Google account. + This email has a WordPress.com account but it\'s not connected to Google. Please log in using an email magic link instead. There was some trouble connecting with the Google account. We\'ve made too many attempts to send an SMS verification code — take a break, and request a new one in a minute. Google login could not be started. From 43c9ed4e5ef76d972bd08e48cf9fb7889b1dc65a Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:27:12 -0700 Subject: [PATCH 02/71] Remove Login Magic Link Fragments --- .../org/wordpress/android/e2e/LoginTests.kt | 20 - .../wordpress/android/e2e/flows/LoginFlow.kt | 22 -- .../wordpress/android/support/BaseTest.java | 10 +- .../android/ui/accounts/LoginActivity.java | 33 +- .../android/login/LoginListener.java | 7 +- .../login/LoginMagicLinkRequestFragment.java | 349 ------------------ .../login/LoginMagicLinkSentFragment.java | 147 -------- .../android/login/di/LoginFragmentModule.java | 8 - .../login_magic_link_request_screen.xml | 50 --- .../layout/login_magic_link_sent_screen.xml | 57 --- 10 files changed, 7 insertions(+), 696 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java delete mode 100644 libs/login/src/main/res/layout/login_magic_link_request_screen.xml delete mode 100644 libs/login/src/main/res/layout/login_magic_link_sent_screen.xml diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt index 8a53986021e9..b21707c91685 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt @@ -16,26 +16,6 @@ class LoginTests : BaseTest() { logoutIfNecessary() } - @Test - fun e2eLoginWithMagicLink() { - LoginFlow().chooseContinueWithWpCom(super.mComposeTestRule) - .enterEmailAddress(E2ECredentials.WP_COM_USER_EMAIL) - .openMagicLink() - .confirmLogin() - - ComposeEspressoLink().unregister() - } - - @Test - fun e2eLoginWithPasswordlessAccount() { - LoginFlow().chooseContinueWithWpCom(super.mComposeTestRule) - .enterEmailAddress(E2ECredentials.WP_COM_PASSWORDLESS_USER_EMAIL) - .openMagicLink() - .confirmLogin() - - ComposeEspressoLink().unregister() - } - @Test fun e2eLoginWithSelfHostedAccount() { LoginFlow().chooseEnterYourSiteAddress(super.mComposeTestRule) diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt index 4e7e0228c79c..bb09f5481e2e 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt @@ -1,18 +1,11 @@ package org.wordpress.android.e2e.flows -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri import android.widget.EditText import androidx.compose.ui.test.junit4.ComposeTestRule -import androidx.test.core.app.ActivityScenario -import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso import androidx.test.espresso.matcher.ViewMatchers import org.hamcrest.CoreMatchers import org.hamcrest.Matchers -import org.wordpress.android.BuildConfig import org.wordpress.android.R import org.wordpress.android.e2e.pages.HelpScreen import org.wordpress.android.e2e.pages.LandingPage.tapContinueWithWpCom @@ -35,21 +28,6 @@ class LoginFlow { return this } - fun openMagicLink(): LoginFlow { - // Magic Link Sent Screen – Should see "Check email" button - // See LoginMagicLinkSentFragment - WPSupportUtils.waitForElementToBeDisplayed(LoginR.id.login_open_email_client) - - // Follow the magic link to continue login - // Intent is invoked directly rather than through a browser as WireMock is unavailable once in the background - val appVariant = BuildConfig.FLAVOR_app - val intent = - Intent(Intent.ACTION_VIEW, Uri.parse("$appVariant://magic-login?token=valid_token")) - .setPackage(ApplicationProvider.getApplicationContext().packageName) - ActivityScenario.launch(intent) - return this - } - fun enterUsernameAndPassword(username: String, password: String): LoginFlow { val usernameElement = Espresso.onView( CoreMatchers.allOf( diff --git a/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java b/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java index 3cd170b9e087..bbffbc8033c2 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java +++ b/WordPress/src/androidTest/java/org/wordpress/android/support/BaseTest.java @@ -96,11 +96,11 @@ protected void logoutIfNecessary() { } protected void wpLogin() { - logoutIfNecessary(); - new LoginFlow().chooseContinueWithWpCom(mComposeTestRule) - .enterEmailAddress(WP_COM_USER_EMAIL) - .openMagicLink() - .confirmLogin(); + // WP.com login now uses web-based OAuth flow which cannot be automated via Espresso. + // Tests requiring WP.com login need a different approach (e.g., pre-authenticated state). + throw new UnsupportedOperationException( + "WP.com login is now web-based and cannot be automated via Espresso" + ); } private void wpLogout() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 3e745cc4c601..0fb0c260750a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -46,8 +46,6 @@ import org.wordpress.android.login.LoginEmailFragment; import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginListener; -import org.wordpress.android.login.LoginMagicLinkRequestFragment; -import org.wordpress.android.login.LoginMagicLinkSentFragment; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; import org.wordpress.android.login.LoginUsernamePasswordFragment; @@ -609,19 +607,7 @@ public void onTermsOfServiceClicked() { @Override public void gotWpcomEmail(String email, boolean verifyEmail, @Nullable AuthOptions authOptions) { initSmartLockIfNotFinished(false); - - boolean forceRequestAtStart = authOptions != null && authOptions.isPasswordless(); - showMagicLinkRequestScreen(email, verifyEmail, forceRequestAtStart); - } - - private void showMagicLinkRequestScreen(String email, boolean verifyEmail, - boolean forceRequestAtStart) { - AuthEmailPayloadScheme scheme = mViewModel.getMagicLinkScheme(); - String jetpackConnectionSource = mJetpackConnectSource != null ? mJetpackConnectSource.toString() : null; - LoginMagicLinkRequestFragment loginMagicLinkRequestFragment = LoginMagicLinkRequestFragment - .newInstance(email, scheme, mIsJetpackConnect, jetpackConnectionSource, verifyEmail, - forceRequestAtStart); - slideInFragment(loginMagicLinkRequestFragment, true, LoginMagicLinkRequestFragment.TAG); + showWPcomLoginScreen(this); } @Override @@ -653,13 +639,6 @@ public void loginViaWpcomUsernameInstead() { jumpToUsernamePassword(null, null); } - @Override - public void showMagicLinkSentScreen(String email) { - LoginMagicLinkSentFragment loginMagicLinkSentFragment = - LoginMagicLinkSentFragment.newInstance(email); - slideInFragment(loginMagicLinkSentFragment, true, LoginMagicLinkSentFragment.TAG); - } - @Override public void showSignupMagicLink(String email) { boolean isEmailClientAvailable = WPActivityUtils.isEmailClientAvailable(this); @@ -833,16 +812,6 @@ public void addGoogleLoginFragment(boolean isSignupFromLoginEnabled) { addGoogleFragment(LoginGoogleFragment.newInstance(isSignupFromLoginEnabled), LoginGoogleFragment.TAG); } - @Override - public void helpMagicLinkRequest(String email) { - viewHelp(Origin.LOGIN_MAGIC_LINK); - } - - @Override - public void helpMagicLinkSent(String email) { - viewHelp(Origin.LOGIN_MAGIC_LINK); - } - @Override public void help2FaScreen(String email) { viewHelp(Origin.LOGIN_2FA); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 8cd20b148a39..64aedef0a7a9 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -35,13 +35,8 @@ void gotUnregisteredSocialAccount(String email, String displayName, String idTok void showHelpFindingConnectedEmail(); void onTermsOfServiceClicked(); - // Login Request Magic Link callbacks - void showMagicLinkSentScreen(String email); - void helpMagicLinkRequest(String email); - - // Login Magic Link Sent callbacks + // Login Magic Link Sent callbacks (used by SignupMagicLinkFragment) void openEmailClient(boolean isLogin); - void helpMagicLinkSent(String email); // Login 2FA callbacks void needs2fa(String email, String password); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java deleted file mode 100644 index 509ff7341662..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkRequestFragment.java +++ /dev/null @@ -1,349 +0,0 @@ -package org.wordpress.android.login; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.Dispatcher; -import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayload; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadSource; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthEmailSent; -import org.wordpress.android.login.util.AvatarHelper; -import org.wordpress.android.login.util.AvatarHelper.AvatarRequestListener; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.NetworkUtils; -import org.wordpress.android.util.ToastUtils; - -import java.util.HashMap; - -import javax.inject.Inject; - -import dagger.android.support.AndroidSupportInjection; - -public class LoginMagicLinkRequestFragment extends Fragment { - public static final String TAG = "login_magic_link_request_fragment_tag"; - - private static final String KEY_IN_PROGRESS = "KEY_IN_PROGRESS"; - private static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - private static final String ARG_MAGIC_LINK_SCHEME = "ARG_MAGIC_LINK_SCHEME"; - private static final String ARG_IS_JETPACK_CONNECT = "ARG_IS_JETPACK_CONNECT"; - private static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; - private static final String ARG_VERIFY_MAGIC_LINK_EMAIL = "ARG_VERIFY_MAGIC_LINK_EMAIL"; - private static final String ARG_FORCE_REQUEST_AT_START = "ARG_FORCE_REQUEST_AT_START"; - - private static final String ERROR_KEY = "error"; - - private LoginListener mLoginListener; - - private String mEmail; - private AuthEmailPayloadScheme mMagicLinkScheme; - private String mJetpackConnectSource; - - private View mAvatarProgressBar; - private Button mRequestMagicLinkButton; - private ProgressDialog mProgressDialog; - - private boolean mInProgress; - private boolean mIsJetpackConnect; - private boolean mVerifyMagicLinkEmail; - private boolean mForceRequestAtStart; - - @Inject protected Dispatcher mDispatcher; - - @Inject protected LoginAnalyticsListener mAnalyticsListener; - - public static LoginMagicLinkRequestFragment newInstance(String email, AuthEmailPayloadScheme scheme, - boolean isJetpackConnect, String jetpackConnectSource, - boolean verifyEmail, - boolean forceRequestAtStart) { - LoginMagicLinkRequestFragment fragment = new LoginMagicLinkRequestFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, email); - args.putSerializable(ARG_MAGIC_LINK_SCHEME, scheme); - args.putBoolean(ARG_IS_JETPACK_CONNECT, isJetpackConnect); - args.putString(ARG_JETPACK_CONNECT_SOURCE, jetpackConnectSource); - args.putBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL, verifyEmail); - args.putBoolean(ARG_FORCE_REQUEST_AT_START, forceRequestAtStart); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - if (context instanceof LoginListener) { - mLoginListener = (LoginListener) context; - } else { - throw new RuntimeException(context.toString() + " must implement LoginListener"); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getArguments() != null) { - mEmail = getArguments().getString(ARG_EMAIL_ADDRESS); - mMagicLinkScheme = (AuthEmailPayloadScheme) getArguments().getSerializable(ARG_MAGIC_LINK_SCHEME); - mIsJetpackConnect = getArguments().getBoolean(ARG_IS_JETPACK_CONNECT); - mJetpackConnectSource = getArguments().getString(ARG_JETPACK_CONNECT_SOURCE); - mVerifyMagicLinkEmail = getArguments().getBoolean(ARG_VERIFY_MAGIC_LINK_EMAIL); - mForceRequestAtStart = getArguments().getBoolean(ARG_FORCE_REQUEST_AT_START); - } - - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.login_magic_link_request_screen, container, false); - mRequestMagicLinkButton = view.findViewById(R.id.login_request_magic_link); - mRequestMagicLinkButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAnalyticsListener.trackRequestMagicLinkClick(); - dispatchMagicLinkRequest(); - } - }); - - mAvatarProgressBar = view.findViewById(R.id.avatar_progress); - ImageView avatarView = view.findViewById(R.id.gravatar); - - TextView emailView = view.findViewById(R.id.email); - emailView.setText(mEmail); - - // Design changes added to the Woo Magic link sign-in - - if (mVerifyMagicLinkEmail) { - AvatarHelper.loadAvatarFromEmail(this, mEmail, avatarView, new AvatarRequestListener() { - @Override public void onRequestFinished() { - mAvatarProgressBar.setVisibility(View.GONE); - } - }); - - TextView labelTextView = view.findViewById(R.id.label); - labelTextView.setText(Html.fromHtml(String.format(getResources().getString( - R.string.login_site_credentials_magic_link_label), mEmail))); - } else { - AvatarHelper.loadAvatarFromEmail(this, mEmail, avatarView, new AvatarRequestListener() { - @Override public void onRequestFinished() { - mAvatarProgressBar.setVisibility(View.GONE); - } - }); - } - - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - Toolbar toolbar = view.findViewById(R.id.toolbar); - ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - - ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.log_in); - actionBar.setDisplayHomeAsUpEnabled(true); - } - - if (savedInstanceState == null) { - mAnalyticsListener.trackMagicLinkRequestFormViewed(); - } - - if (mForceRequestAtStart && !mInProgress) { - dispatchMagicLinkRequest(); - } - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (savedInstanceState != null) { - mInProgress = savedInstanceState.getBoolean(KEY_IN_PROGRESS); - if (mInProgress) { - showMagicLinkRequestProgressDialog(); - } - } - // important for accessibility - talkback - getActivity().setTitle(R.string.magic_link_login_title); - } - - @Override public void onResume() { - super.onResume(); - mAnalyticsListener.magicLinkRequestScreenResumed(); - } - - @Override - public void onDetach() { - super.onDetach(); - mLoginListener = null; - } - - @Override public void onDestroyView() { - mRequestMagicLinkButton = null; - - super.onDestroyView(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putBoolean(KEY_IN_PROGRESS, mInProgress); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_login, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.help) { - mAnalyticsListener.trackShowHelpClick(); - if (mLoginListener != null) { - mLoginListener.helpMagicLinkRequest(mEmail); - } - return true; - } - - return false; - } - - @Override - public void onStart() { - super.onStart(); - mDispatcher.register(this); - } - - @Override - public void onStop() { - super.onStop(); - mDispatcher.unregister(this); - } - - private void dispatchMagicLinkRequest() { - if (mLoginListener != null) { - if (NetworkUtils.checkConnection(getActivity())) { - showMagicLinkRequestProgressDialog(); - AuthEmailPayloadSource source = getAuthEmailPayloadSource(); - AuthEmailPayload authEmailPayload = new AuthEmailPayload(mEmail, false, - mIsJetpackConnect ? AccountStore.AuthEmailPayloadFlow.JETPACK : null, - source, mMagicLinkScheme); - mDispatcher.dispatch(AuthenticationActionBuilder.newSendAuthEmailAction(authEmailPayload)); - } - } - } - - private AuthEmailPayloadSource getAuthEmailPayloadSource() { - if (mJetpackConnectSource != null) { - if (mJetpackConnectSource.equalsIgnoreCase(AuthEmailPayloadSource.NOTIFICATIONS.toString())) { - return AuthEmailPayloadSource.NOTIFICATIONS; - } else if (mJetpackConnectSource.equalsIgnoreCase(AuthEmailPayloadSource.STATS.toString())) { - return AuthEmailPayloadSource.STATS; - } else { - return null; - } - } else { - return null; - } - } - - private void showMagicLinkRequestProgressDialog() { - startProgress(getString(R.string.login_magic_link_email_requesting)); - } - - protected void startProgress(String message) { - mRequestMagicLinkButton.setEnabled(false); - mProgressDialog = ProgressDialog.show(getActivity(), "", message, true, true, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - if (mInProgress) { - endProgress(); - } - } - }); - mInProgress = true; - } - - protected void endProgress() { - mInProgress = false; - - if (mProgressDialog != null) { - mProgressDialog.cancel(); - mProgressDialog = null; - } - - // nullify the reference to denote there is no operation in progress - mProgressDialog = null; - - mRequestMagicLinkButton.setEnabled(true); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthEmailSent(OnAuthEmailSent event) { - if (!mInProgress) { - // ignore the response if the magic link request is no longer pending - return; - } - - endProgress(); - - if (event.isError()) { - HashMap errorProperties = new HashMap<>(); - errorProperties.put(ERROR_KEY, event.error.message); - mAnalyticsListener.trackMagicLinkFailed(errorProperties); - mAnalyticsListener.trackFailure(event.error.message); - - AppLog.e(AppLog.T.API, "OnAuthEmailSent has error: " + event.error.type + " - " + event.error.message); - if (isAdded()) { - ToastUtils.showToast(getActivity(), R.string.magic_link_unavailable_error_message, - ToastUtils.Duration.LONG); - } - return; - } - - mAnalyticsListener.trackMagicLinkRequested(); - - if (mLoginListener != null) { - // when magic link request if forced we want to remove this fragment from backstack so user will not be - // able to navigate back to it from "Magic Link Sent" Screen - if (mForceRequestAtStart) { - FragmentManager fragmentManager = getFragmentManager(); - if (fragmentManager != null) { - fragmentManager.popBackStack(); - } - } - mLoginListener.showMagicLinkSentScreen(mEmail); - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java deleted file mode 100644 index bdbc006706c0..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMagicLinkSentFragment.java +++ /dev/null @@ -1,147 +0,0 @@ -package org.wordpress.android.login; - -import android.content.Context; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; - -import org.wordpress.android.login.util.AvatarHelper; -import org.wordpress.android.login.util.AvatarHelper.AvatarRequestListener; - -import javax.inject.Inject; - -import dagger.android.support.AndroidSupportInjection; - -public class LoginMagicLinkSentFragment extends Fragment { - public static final String TAG = "login_magic_link_sent_fragment_tag"; - - private static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - - private LoginListener mLoginListener; - - private String mEmail; - - @Inject protected LoginAnalyticsListener mAnalyticsListener; - - public static LoginMagicLinkSentFragment newInstance(String email) { - LoginMagicLinkSentFragment fragment = new LoginMagicLinkSentFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, email); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - mEmail = getArguments().getString(ARG_EMAIL_ADDRESS); - } - - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.login_magic_link_sent_screen, container, false); - - view.findViewById(R.id.login_open_email_client).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mLoginListener != null) { - mLoginListener.openEmailClient(true); - } - } - }); - - final View avatarProgressBar = view.findViewById(R.id.avatar_progress); - ImageView avatarView = view.findViewById(R.id.gravatar); - - TextView emailView = view.findViewById(R.id.email); - emailView.setText(mEmail); - - AvatarHelper.loadAvatarFromEmail(this, mEmail, avatarView, new AvatarRequestListener() { - @Override public void onRequestFinished() { - avatarProgressBar.setVisibility(View.GONE); - } - }); - - return view; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - Toolbar toolbar = view.findViewById(R.id.toolbar); - ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - - ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.log_in); - actionBar.setDisplayHomeAsUpEnabled(true); - } - - if (savedInstanceState == null) { - mAnalyticsListener.trackLoginMagicLinkOpenEmailClientViewed(); - } - } - - @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - // important for accessibility - talkback - getActivity().setTitle(R.string.magic_link_sent_login_title); - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - if (context instanceof LoginListener) { - mLoginListener = (LoginListener) context; - } else { - throw new RuntimeException(context.toString() + " must implement LoginListener"); - } - } - - @Override public void onResume() { - super.onResume(); - mAnalyticsListener.magicLinkSentScreenResumed(); - } - - @Override - public void onDetach() { - super.onDetach(); - mLoginListener = null; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_login, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.help) { - mAnalyticsListener.trackShowHelpClick(); - if (mLoginListener != null) { - mLoginListener.helpMagicLinkSent(mEmail); - } - return true; - } - - return false; - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index 0c2cbe7c4cc4..267c712d9d1f 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -3,8 +3,6 @@ import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginEmailFragment; import org.wordpress.android.login.LoginGoogleFragment; -import org.wordpress.android.login.LoginMagicLinkRequestFragment; -import org.wordpress.android.login.LoginMagicLinkSentFragment; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; import org.wordpress.android.login.LoginUsernamePasswordFragment; import org.wordpress.android.login.SignupConfirmationFragment; @@ -25,12 +23,6 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract LoginGoogleFragment loginGoogleFragment(); - @ContributesAndroidInjector - abstract LoginMagicLinkRequestFragment loginMagicLinkRequestFragment(); - - @ContributesAndroidInjector - abstract LoginMagicLinkSentFragment loginMagicLinkSentFragment(); - @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); diff --git a/libs/login/src/main/res/layout/login_magic_link_request_screen.xml b/libs/login/src/main/res/layout/login_magic_link_request_screen.xml deleted file mode 100644 index 1eaa2eb1b24c..000000000000 --- a/libs/login/src/main/res/layout/login_magic_link_request_screen.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml b/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml deleted file mode 100644 index 5205985939b8..000000000000 --- a/libs/login/src/main/res/layout/login_magic_link_sent_screen.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - From faf4130b5f3b4ff89aa8746d5ba3020e1d85af8e Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:36:03 -0700 Subject: [PATCH 03/71] Remove username/password WP.com flow --- .../android/ui/accounts/LoginActivity.java | 94 +-- .../android/login/LoginEmailFragment.java | 5 +- .../android/login/LoginListener.java | 15 - .../login/LoginUsernamePasswordFragment.java | 726 ------------------ .../android/login/di/LoginFragmentModule.java | 4 - .../layout/login_username_password_screen.xml | 47 -- 6 files changed, 5 insertions(+), 886 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginUsernamePasswordFragment.java delete mode 100644 libs/login/src/main/res/layout/login_username_password_screen.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 0fb0c260750a..1885a497417d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -48,7 +48,6 @@ import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; -import org.wordpress.android.login.LoginUsernamePasswordFragment; import org.wordpress.android.login.SignupConfirmationFragment; import org.wordpress.android.login.SignupGoogleFragment; import org.wordpress.android.login.SignupMagicLinkFragment; @@ -87,7 +86,6 @@ import org.wordpress.android.util.AppLog.T; import org.wordpress.android.util.BuildConfigWrapper; import org.wordpress.android.util.SelfSignedSSLUtils; -import org.wordpress.android.util.StringUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.ToastUtils.Duration; import org.wordpress.android.util.WPActivityUtils; @@ -123,8 +121,6 @@ public class LoginActivity extends BaseAppCompatActivity implements ConnectionCa private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; - private static final String FORGOT_PASSWORD_URL_SUFFIX = "wp-login.php?action=lostpassword"; - private static final String GOOGLE_ERROR_DIALOG_TAG = "google_error_dialog_tag"; private enum SmartLockHelperState { @@ -509,12 +505,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - private void jumpToUsernamePassword(String username, String password) { - LoginUsernamePasswordFragment loginUsernamePasswordFragment = - LoginUsernamePasswordFragment.newInstance("wordpress.com", "wordpress.com", username, password, true); - slideInFragment(loginUsernamePasswordFragment, true, LoginUsernamePasswordFragment.TAG); - } - private boolean initSmartLockHelperConnection() { mSmartLockHelper = new SmartLockHelper(this); return mSmartLockHelper.initSmartLockForPasswords(); @@ -634,11 +624,6 @@ public void loggedInViaSocialAccount(ArrayList oldSitesIds, boolean doL loggedInAndFinish(oldSitesIds, doLoginUpdate); } - @Override - public void loginViaWpcomUsernameInstead() { - jumpToUsernamePassword(null, null); - } - @Override public void showSignupMagicLink(String email) { boolean isEmailClientAvailable = WPActivityUtils.isEmailClientAvailable(this); @@ -673,12 +658,6 @@ public void openEmailClient(boolean isLogin) { } } - @Override - public void forgotPassword(String url) { - mLoginAnalyticsListener.trackLoginForgotPasswordClicked(); - ActivityLauncher.openUrlExternal(this, url + FORGOT_PASSWORD_URL_SUFFIX); - } - @Override public void needs2fa(String email, String password) { Login2FaFragment login2FaFragment = Login2FaFragment.newInstance(email, password); @@ -729,13 +708,6 @@ public void gotWpcomSiteInfo(String siteAddress) { slideInFragment(loginEmailFragment, true, LoginEmailFragment.TAG); } - @Override - public void gotXmlRpcEndpoint(String inputSiteAddress, String endpointAddress) { - LoginUsernamePasswordFragment loginUsernamePasswordFragment = - LoginUsernamePasswordFragment.newInstance(inputSiteAddress, endpointAddress, null, null, false); - slideInFragment(loginUsernamePasswordFragment, true, LoginUsernamePasswordFragment.TAG); - } - @Override public void handleSslCertificateError(MemorizingTrustManager memorizingTrustManager, final SelfSignedSSLCallback callback) { @@ -777,11 +749,6 @@ public void helpFindingSiteAddress(String username, SiteStore siteStore) { } } - @Override - public void loggedInViaUsernamePassword(ArrayList oldSitesIds) { - loggedInAndFinish(oldSitesIds, false); - } - @Override public void helpEmailScreen(String email) { viewHelp(Origin.LOGIN_EMAIL); @@ -828,39 +795,8 @@ public void startPostLoginServices() { NotificationsUpdateServiceStarter.startService(getApplicationContext()); } - @Override - public void helpUsernamePassword(String url, String username, boolean isWpcom) { - viewHelp(Origin.LOGIN_USERNAME_PASSWORD); - } - // SmartLock - @Override - public void saveCredentialsInSmartLock(@Nullable final String username, @Nullable final String password, - @NonNull final String displayName, @Nullable final Uri profilePicture) { - LoginMode mode = getLoginMode(); - if (mode == LoginMode.SELFHOSTED_ONLY || mode == LoginMode.JETPACK_SELFHOSTED) { - // bail if we are on the selfhosted flow since we haven't initialized SmartLock-for-Passwords for it. - // Otherwise, logging in to WPCOM via the site-picker flow (for example) results in a crash. - // See https://github.com/wordpress-mobile/WordPress-Android/issues/7182#issuecomment-362791364 - // There might be more circumstances that lead to this crash though. Not all crash reports seem to - // originate from the site-picker. - return; - } - - if (mSmartLockHelper == null) { - // log some data to help us debug https://github.com/wordpress-mobile/WordPress-Android/issues/7182 - final String loginModeStr = "LoginMode: " + (getLoginMode() != null ? getLoginMode().name() : "null"); - AppLog.w(AppLog.T.NUX, "Internal inconsistency error! mSmartLockHelper found null!" + loginModeStr); - - // bail - return; - } - - mSmartLockHelper.saveCredentialsInSmartLock(StringUtils.notNullStr(username), StringUtils.notNullStr(password), - displayName, profilePicture); - } - @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { AppLog.d(AppLog.T.NUX, "Connection result: " + connectionResult); @@ -894,13 +830,9 @@ public void onConnected(Bundle bundle) { @Override public void onCredentialRetrieved(Credential credential) { - mLoginAnalyticsListener.trackLoginAutofillCredentialsFilled(); - + // Smart Lock credentials can no longer be used for WP.com login (now web-based) mSmartLockHelperState = SmartLockHelperState.FINISHED; - - final String username = credential.getId(); - final String password = credential.getPassword(); - jumpToUsernamePassword(username, password); + startLogin(); } @Override @@ -1006,28 +938,6 @@ public void gotConnectedSiteInfo( // Not used in WordPress app } - @Override - public void helpHandleDiscoveryError( - String siteAddress, - String endpointAddress, - String username, - String password, - String userAvatarUrl, - int errorMessage) { - // Not used in WordPress app - } - - @Override - public void helpNoJetpackScreen( - String siteAddress, - String endpointAddress, - String username, - String password, - String userAvatarUrl, - Boolean checkJetpackAvailability) { - // Not used in WordPress app - } - @Override public void loginViaSiteCredentials(String inputSiteAddress) { // Not used in WordPress app diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java index 6ee2430ada86..516c78619ef2 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java @@ -588,11 +588,12 @@ public void onAuthOptionsFetched(OnAuthOptionsFetched event) { } break; case EMAIL_LOGIN_NOT_ALLOWED: - // As a security measure, this user needs to log in using an username and password + // As a security measure, this user needs to log in using username and password via web mAnalyticsListener.trackFailure("Login with username required"); ToastUtils.showToast(getContext(), R.string.error_user_username_instead_of_email, Duration.LONG); if (mLoginListener != null) { - mLoginListener.loginViaWpcomUsernameInstead(); + // Redirect to web-based login which supports username/password authentication + mLoginListener.gotWpcomEmail(email, false, null); } break; case GENERIC_ERROR: diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 64aedef0a7a9..566169c8262f 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -1,7 +1,5 @@ package org.wordpress.android.login; -import android.net.Uri; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -27,7 +25,6 @@ void gotUnregisteredSocialAccount(String email, String displayName, String idTok String service); void loginViaSiteAddress(); void loggedInViaSocialAccount(ArrayList oldSiteIds, boolean doLoginUpdate); - void loginViaWpcomUsernameInstead(); void loginViaSiteCredentials(String inputSiteAddress); void helpEmailScreen(String email); void helpSocialEmailScreen(String email); @@ -52,23 +49,11 @@ void needs2faSocial(String email, String userId, String nonceAuthenticator, Stri void alreadyLoggedInWpcom(ArrayList oldSitesIds); void gotWpcomSiteInfo(String siteAddress); void gotConnectedSiteInfo(@NonNull String siteAddress, @Nullable String redirectUrl, boolean hasJetpack); - void gotXmlRpcEndpoint(String inputSiteAddress, String endpointAddress); void handleSslCertificateError(MemorizingTrustManager memorizingTrustManager, SelfSignedSSLCallback callback); void helpSiteAddress(String url); void helpFindingSiteAddress(String username, SiteStore siteStore); void handleSiteAddressError(ConnectSiteInfoPayload siteInfo); - // Login username password callbacks - void forgotPassword(String url); - void saveCredentialsInSmartLock(@Nullable String username, @Nullable String password, - @NonNull String displayName, @Nullable Uri profilePicture); - void loggedInViaUsernamePassword(ArrayList oldSitesIds); - void helpUsernamePassword(String url, String username, boolean isWpcom); - void helpNoJetpackScreen(String siteAddress, String endpointAddress, String username, - String password, String userAvatarUrl, Boolean checkJetpackAvailability); - void helpHandleDiscoveryError(String siteAddress, String endpointAddress, String username, - String password, String userAvatarUrl, int errorMessage); - // Login 2FA screen callbacks void help2FaScreen(String email); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginUsernamePasswordFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginUsernamePasswordFragment.java deleted file mode 100644 index 7aab9762c393..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginUsernamePasswordFragment.java +++ /dev/null @@ -1,726 +0,0 @@ -package org.wordpress.android.login; - -import android.content.Context; -import android.graphics.Rect; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; -import androidx.core.widget.NestedScrollView; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -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.discovery.SelfHostedEndpointFinder.DiscoveryError; -import org.wordpress.android.fluxc.network.xmlrpc.XMLRPCRequest.XmlRpcErrorType; -import org.wordpress.android.fluxc.store.AccountStore.AuthenticatePayload; -import org.wordpress.android.fluxc.store.AccountStore.AuthenticationErrorType; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged; -import org.wordpress.android.fluxc.store.AccountStore.OnTwoFactorAuthStarted; -import org.wordpress.android.fluxc.store.SiteStore.OnProfileFetched; -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.fluxc.utils.extensions.SiteModelExtensionsKt; -import org.wordpress.android.login.util.SiteUtils; -import org.wordpress.android.login.widgets.WPLoginInputRow; -import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener; -import org.wordpress.android.util.ActivityUtils; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.EditTextUtils; -import org.wordpress.android.util.NetworkUtils; -import org.wordpress.android.util.StringUtils; -import org.wordpress.android.util.ToastUtils; -import org.wordpress.android.util.UrlUtils; - -import java.util.ArrayList; -import java.util.List; - -import dagger.android.support.AndroidSupportInjection; - -public class LoginUsernamePasswordFragment extends LoginBaseDiscoveryFragment implements TextWatcher, - OnEditorCommitListener, LoginBaseDiscoveryFragment.LoginBaseDiscoveryListener { - private static final String KEY_LOGIN_FINISHED = "KEY_LOGIN_FINISHED"; - private static final String KEY_LOGIN_STARTED = "KEY_LOGIN_STARTED"; - private static final String KEY_REQUESTED_USERNAME = "KEY_REQUESTED_USERNAME"; - private static final String KEY_REQUESTED_PASSWORD = "KEY_REQUESTED_PASSWORD"; - private static final String KEY_OLD_SITES_IDS = "KEY_OLD_SITES_IDS"; - private static final String KEY_GET_SITE_OPTIONS_INITIATED = "KEY_GET_SITE_OPTIONS_INITIATED"; - - private static final String ARG_INPUT_SITE_ADDRESS = "ARG_INPUT_SITE_ADDRESS"; - private static final String ARG_ENDPOINT_ADDRESS = "ARG_ENDPOINT_ADDRESS"; - private static final String ARG_INPUT_USERNAME = "ARG_INPUT_USERNAME"; - private static final String ARG_INPUT_PASSWORD = "ARG_INPUT_PASSWORD"; - private static final String ARG_IS_WPCOM = "ARG_IS_WPCOM"; - - private static final String FORGOT_PASSWORD_URL_WPCOM = "https://wordpress.com/"; - - public static final String TAG = "login_username_password_fragment_tag"; - - private NestedScrollView mScrollView; - private WPLoginInputRow mUsernameInput; - private WPLoginInputRow mPasswordInput; - - private boolean mAuthFailed; - private boolean mLoginFinished; - private boolean mLoginStarted; - - private String mRequestedUsername; - private String mRequestedPassword; - ArrayList mOldSitesIDs; - private boolean mGetSiteOptionsInitiated; - - private String mInputSiteAddress; - private String mInputSiteAddressWithoutSuffix; - private String mEndpointAddress; - private String mInputUsername; - private String mInputPassword; - private boolean mIsWpcom; - - public static LoginUsernamePasswordFragment newInstance(String inputSiteAddress, String endpointAddress, - String inputUsername, String inputPassword, - boolean isWpcom) { - LoginUsernamePasswordFragment fragment = new LoginUsernamePasswordFragment(); - Bundle args = new Bundle(); - args.putString(ARG_INPUT_SITE_ADDRESS, inputSiteAddress); - args.putString(ARG_ENDPOINT_ADDRESS, endpointAddress); - args.putString(ARG_INPUT_USERNAME, inputUsername); - args.putString(ARG_INPUT_PASSWORD, inputPassword); - args.putBoolean(ARG_IS_WPCOM, isWpcom); - fragment.setArguments(args); - return fragment; - } - - @Override - protected @LayoutRes int getContentLayout() { - return R.layout.login_username_password_screen; - } - - @Override - protected @LayoutRes int getProgressBarText() { - return R.string.logging_in; - } - - @Override - protected void setupLabel(@NonNull TextView label) { - final boolean isWoo = mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE; - final int labelResId = isWoo ? R.string.enter_credentials_for_site : R.string.enter_account_info_for_site; - final String siteAddress = - (mEndpointAddress == null || mEndpointAddress.isEmpty()) ? mInputSiteAddress : mEndpointAddress; - final String formattedSiteAddress = - UrlUtils.removeScheme(UrlUtils.removeXmlrpcSuffix(StringUtils.notNullStr(siteAddress))); - label.setText(getString(labelResId, formattedSiteAddress)); - } - - @Override - protected void setupContent(ViewGroup rootView) { - // important for accessibility - talkback - getActivity().setTitle(R.string.selfhosted_site_login_title); - mScrollView = rootView.findViewById(R.id.scroll_view); - - mInputSiteAddressWithoutSuffix = (mEndpointAddress == null || mEndpointAddress.isEmpty()) - ? mInputSiteAddress : UrlUtils.removeXmlrpcSuffix(mEndpointAddress); - - mUsernameInput = rootView.findViewById(R.id.login_username_row); - mUsernameInput.setText(mInputUsername); - if (BuildConfig.DEBUG && mInputUsername == null) { - mUsernameInput.getEditText().setText(BuildConfig.DEBUG_WPCOM_LOGIN_USERNAME); - } - mUsernameInput.addTextChangedListener(this); - mUsernameInput.setOnEditorCommitListener(new OnEditorCommitListener() { - @Override - public void onEditorCommit() { - showError(null); - mPasswordInput.getEditText().requestFocus(); - } - }); - - mPasswordInput = rootView.findViewById(R.id.login_password_row); - mPasswordInput.setText(mInputPassword); - if (BuildConfig.DEBUG && mInputPassword == null) { - mPasswordInput.getEditText().setText(BuildConfig.DEBUG_WPCOM_LOGIN_PASSWORD); - } - mPasswordInput.addTextChangedListener(this); - - mPasswordInput.setOnEditorCommitListener(this); - - rootView.findViewById(R.id.login_reset_password).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mLoginListener != null) { - if (mIsWpcom) { - mLoginListener.forgotPassword(FORGOT_PASSWORD_URL_WPCOM); - } else { - if (!mInputSiteAddressWithoutSuffix.endsWith("/")) { - mInputSiteAddressWithoutSuffix += "/"; - } - mLoginListener.forgotPassword(mInputSiteAddressWithoutSuffix); - } - } - } - }); - } - - @Override - protected void setupBottomButton(Button button) { - button.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - next(); - } - }); - } - - @Override - protected void buildToolbar(Toolbar toolbar, ActionBar actionBar) { - actionBar.setTitle(R.string.log_in); - } - - @Override - protected EditText getEditTextToFocusOnStart() { - return mUsernameInput.getEditText(); - } - - @Override - protected void onHelp() { - if (mLoginListener != null) { - mLoginListener.helpUsernamePassword(mInputSiteAddress, mRequestedUsername, mIsWpcom); - } - } - - @Override public void onDestroyView() { - if (mPasswordInput != null) { - mPasswordInput.setOnEditorCommitListener(null); - mPasswordInput = null; - } - if (mUsernameInput != null) { - mUsernameInput.setOnEditorCommitListener(null); - mUsernameInput = null; - } - mScrollView = null; - - super.onDestroyView(); - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mInputSiteAddress = getArguments().getString(ARG_INPUT_SITE_ADDRESS); - mEndpointAddress = getArguments().getString(ARG_ENDPOINT_ADDRESS, null); - mInputUsername = getArguments().getString(ARG_INPUT_USERNAME); - mInputPassword = getArguments().getString(ARG_INPUT_PASSWORD); - mIsWpcom = getArguments().getBoolean(ARG_IS_WPCOM); - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (savedInstanceState != null) { - mLoginFinished = savedInstanceState.getBoolean(KEY_LOGIN_FINISHED); - mLoginStarted = savedInstanceState.getBoolean(KEY_LOGIN_STARTED); - - mRequestedUsername = savedInstanceState.getString(KEY_REQUESTED_USERNAME); - mRequestedPassword = savedInstanceState.getString(KEY_REQUESTED_PASSWORD); - mOldSitesIDs = savedInstanceState.getIntegerArrayList(KEY_OLD_SITES_IDS); - mGetSiteOptionsInitiated = savedInstanceState.getBoolean(KEY_GET_SITE_OPTIONS_INITIATED); - } else { - mAnalyticsListener.trackUsernamePasswordFormViewed(); - - // auto-login if username and password are set for wpcom login - if (mIsWpcom && !TextUtils.isEmpty(mInputUsername) && !TextUtils.isEmpty(mInputPassword)) { - getBottomButton().post(new Runnable() { - @Override - public void run() { - getBottomButton().performClick(); - } - }); - } - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putBoolean(KEY_LOGIN_FINISHED, mLoginFinished); - outState.putBoolean(KEY_LOGIN_STARTED, mLoginStarted); - outState.putString(KEY_REQUESTED_USERNAME, mRequestedUsername); - outState.putString(KEY_REQUESTED_PASSWORD, mRequestedPassword); - outState.putIntegerArrayList(KEY_OLD_SITES_IDS, mOldSitesIDs); - outState.putBoolean(KEY_GET_SITE_OPTIONS_INITIATED, mGetSiteOptionsInitiated); - } - - @Override public void onResume() { - super.onResume(); - mAnalyticsListener.usernamePasswordScreenResumed(); - updatePrimaryButtonEnabledStatus(); - } - - private void updatePrimaryButtonEnabledStatus() { - String currentUsername = mUsernameInput.getEditText().getText().toString(); - String currentPassword = mPasswordInput.getEditText().getText().toString(); - getBottomButton().setEnabled(!currentPassword.trim().isEmpty() && !currentUsername.trim().isEmpty()); - } - - protected void next() { - mAnalyticsListener.trackSubmitClicked(); - if (!NetworkUtils.checkConnection(getActivity())) { - return; - } - - if (TextUtils.isEmpty(getCleanedUsername())) { - showUsernameError(getString(R.string.login_empty_username)); - EditTextUtils.showSoftInput(mUsernameInput.getEditText()); - return; - } - - final String password = mPasswordInput.getEditText().getText().toString(); - - if (TextUtils.isEmpty(password)) { - showPasswordError(getString(R.string.login_empty_password)); - EditTextUtils.showSoftInput(mPasswordInput.getEditText()); - return; - } - - mLoginStarted = true; - startProgress(); - - mRequestedUsername = getCleanedUsername(); - mRequestedPassword = password; - - // clear up the authentication-failed flag before - mAuthFailed = false; - - mOldSitesIDs = SiteUtils.getCurrentSiteIds(mSiteStore, false); - - if (mIsWpcom) { - AuthenticatePayload payload = new AuthenticatePayload(mRequestedUsername, mRequestedPassword); - mDispatcher.dispatch(AuthenticationActionBuilder.newAuthenticateAction(payload)); - } else if (mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE - && (mEndpointAddress == null || mEndpointAddress.isEmpty())) { - // mEndpointAddress will only be null/empty when redirecting from the Woo login flow - // initiate the discovery process before fetching the xmlrpc site - mLoginBaseDiscoveryListener = this; - initiateDiscovery(); - } else { - refreshXmlRpcSites(); - } - } - - private void refreshXmlRpcSites() { - RefreshSitesXMLRPCPayload selfHostedPayload = new RefreshSitesXMLRPCPayload( - mRequestedUsername, - mRequestedPassword, - mEndpointAddress - ); - mDispatcher.dispatch(SiteActionBuilder.newFetchSitesXmlRpcAction(selfHostedPayload)); - } - - private String getCleanedUsername() { - return EditTextUtils.getText(mUsernameInput.getEditText()).trim(); - } - - @Override - public void onEditorCommit() { - showError(null); - next(); - } - - @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) { - showError(null); - updatePrimaryButtonEnabledStatus(); - } - - @Override - @NonNull public String getRequestedSiteAddress() { - return mInputSiteAddressWithoutSuffix; - } - - /** - * Woo users: - * [HTTP_AUTH_REQUIRED] is not supported by Jetpack and can only occur if jetpack is not - * available. Redirect to Jetpack required screen. - * - * The other discovery errors can take place even if Jetpack is available. - * Furthermore, for errors such as [MISSING_XMLRPC_METHOD], [XMLRPC_BLOCKED], [XMLRPC_FORBIDDEN] - * [NO_SITE_ERROR] and [GENERIC_ERROR], the jetpack available flag from the CONNECT_SITE_INFO - * API returns false even if Jetpack is available for the site. - * So we redirect to discovery error screen without checking for Jetpack availability. - * */ - @Override - public void handleDiscoveryError(DiscoveryError error, String failedEndpoint) { - ActivityUtils.hideKeyboard(getActivity()); - mAnalyticsListener.trackFailure(error.name() + " - " + failedEndpoint); - if (error == DiscoveryError.HTTP_AUTH_REQUIRED) { - mLoginListener.helpNoJetpackScreen(mInputSiteAddress, mEndpointAddress, - getCleanedUsername(), mPasswordInput.getEditText().getText().toString(), - mAccountStore.getAccount().getAvatarUrl(), true); - } else { - mLoginListener.helpHandleDiscoveryError(mInputSiteAddress, mEndpointAddress, - getCleanedUsername(), mPasswordInput.getEditText().getText().toString(), - mAccountStore.getAccount().getAvatarUrl(), getDiscoveryErrorMessage(error)); - } - } - - private int getDiscoveryErrorMessage(DiscoveryError error) { - int errorMessageId = 0; - switch (error) { - case HTTP_AUTH_REQUIRED: - errorMessageId = R.string.login_discovery_error_http_auth; - break; - case ERRONEOUS_SSL_CERTIFICATE: - errorMessageId = R.string.login_discovery_error_ssl; - break; - case INVALID_URL: - case NO_SITE_ERROR: - case WORDPRESS_COM_SITE: - case GENERIC_ERROR: - errorMessageId = R.string.login_discovery_error_generic; - break; - - case MISSING_XMLRPC_METHOD: - case XMLRPC_BLOCKED: - case XMLRPC_FORBIDDEN: - errorMessageId = R.string.login_discovery_error_xmlrpc; - break; - } - return errorMessageId; - } - - @Override - public void handleWpComDiscoveryError(String failedEndpoint) { - AppLog.e(T.API, "Inputted a wpcom address in site address screen. Redirecting to Email screen"); - mLoginListener.gotWpcomSiteInfo(UrlUtils.removeScheme(failedEndpoint)); - } - - @Override - public void handleDiscoverySuccess(@NonNull String endpointAddress) { - mEndpointAddress = endpointAddress; - refreshXmlRpcSites(); - } - - private void showUsernameError(String errorMessage) { - mAnalyticsListener.trackFailure(errorMessage); - mUsernameInput.setError(errorMessage); - mPasswordInput.setError(null); - - if (errorMessage != null) { - requestScrollToView(mUsernameInput); - } - } - - private void showPasswordError(String errorMessage) { - mUsernameInput.setError(null); - mAnalyticsListener.trackFailure(errorMessage); - mPasswordInput.setError(errorMessage); - - if (errorMessage != null) { - requestScrollToView(mPasswordInput); - } - } - - private void showError(String errorMessage) { - mUsernameInput.setError(errorMessage != null ? " " : null); - mPasswordInput.setError(errorMessage); - mAnalyticsListener.trackFailure(errorMessage); - - if (errorMessage != null) { - requestScrollToView(mPasswordInput); - } - } - - private void requestScrollToView(final View view) { - view.post(new Runnable() { - @Override - public void run() { - Rect rect = new Rect(); // Coordinates to scroll to - view.getHitRect(rect); - mScrollView.requestChildRectangleOnScreen(view, rect, false); - } - }); - } - - private @Nullable SiteModel detectNewlyAddedXMLRPCSite() { - List selfhostedSites = mSiteStore.getSitesAccessedViaXMLRPC(); - for (SiteModel site : selfhostedSites) { - if (!mOldSitesIDs.contains(site.getId())) { - return site; - } - } - - return null; - } - - @Override - protected void endProgress() { - super.endProgress(); - mRequestedUsername = null; - mRequestedPassword = null; - } - - private void handleAuthError(AuthenticationErrorType error, XmlRpcErrorType xmlRpcErrorType, String errorMessage) { - switch (error) { - case INCORRECT_USERNAME_OR_PASSWORD: - case NOT_AUTHENTICATED: // NOT_AUTHENTICATED is the generic error from XMLRPC response on first call. - case HTTP_AUTH_ERROR: - if (error == AuthenticationErrorType.HTTP_AUTH_ERROR - && xmlRpcErrorType == XmlRpcErrorType.AUTH_REQUIRED) { - showError(getString(R.string.login_error_xml_rpc_auth_error_communicating)); - } else { - showError(getString(R.string.username_or_password_incorrect)); - } - - break; - case INVALID_OTP: - case INVALID_TOKEN: - case AUTHORIZATION_REQUIRED: - case NEEDS_2FA: - handle2fa(); - break; - default: - AppLog.e(T.NUX, "Server response: " + errorMessage); - - ToastUtils.showToast(getActivity(), - TextUtils.isEmpty(errorMessage) ? getString(R.string.error_generic) : errorMessage); - break; - } - } - - private void handle2fa() { - if (mIsWpcom) { - if (mLoginListener != null) { - mLoginListener.needs2fa(mRequestedUsername, mRequestedPassword); - } - } else { - showError("2FA not supported for self-hosted sites. Please use an app-password."); - } - } - - // OnChanged events - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onTwoFactorAuthStarted(OnTwoFactorAuthStarted event) { - mLoginStarted = false; - handle2fa(); - endProgress(); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthenticationChanged(OnAuthenticationChanged event) { - // emitted when wpcom site or when the selfhosted login failed (but not when succeeded) - - if (!isAdded() || mLoginFinished) { - // just bail - return; - } - - if (event.isError()) { - mLoginStarted = false; - if (mRequestedUsername == null) { - // just bail since the operation was cancelled - return; - } - - mAuthFailed = true; - AppLog.e(T.API, "Login with username/pass onAuthenticationChanged has error: " + event.error.type - + " - " + event.error.message); - mAnalyticsListener.trackLoginFailed(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - - handleAuthError(event.error.type, event.error.xmlRpcErrorType, event.error.message); - - // end the progress last since it cleans up the requested username/password and those might be needed - // in handleAuthError() - endProgress(); - - return; - } - - AppLog.i(T.NUX, "onAuthenticationChanged: " + event.toString()); - - doFinishLogin(); - } - - @Override - protected void onLoginFinished() { - mAnalyticsListener.trackAnalyticsSignIn(mIsWpcom); - - mLoginListener.startPostLoginServices(); - - mLoginListener.loggedInViaPassword(mOldSitesIDs); - } - - private void finishLogin() { - mAnalyticsListener.trackAnalyticsSignIn(mIsWpcom); - - // mark as finished so any subsequent onSiteChanged (e.g. triggered by WPMainActivity) won't be intercepted - mLoginFinished = true; - - if (mLoginListener != null) { - if (mIsWpcom) { - saveCredentialsInSmartLock(mLoginListener, mRequestedUsername, mRequestedPassword); - } - - mLoginListener.loggedInViaUsernamePassword(mOldSitesIDs); - } - - endProgress(); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSiteChanged(OnSiteChanged event) { - if (!isAdded() || mLoginFinished || !mLoginStarted) { - return; - } - - if (event.isError()) { - mLoginStarted = false; - if (mRequestedUsername == null) { - // just bail since the operation was cancelled - return; - } - - endProgress(); - - if (mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE) { - // Woo users: One of the errors that can happen here is the XML-RPC endpoint could - // be blocked by plugins such as `Disable XML-RPC`. Redirect the user to discovery - // error screen in such cases. - handleDiscoveryError(DiscoveryError.XMLRPC_BLOCKED, mInputSiteAddress); - return; - } - - String errorMessage; - 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 - errorMessage = getString(R.string.cannot_add_duplicate_site); - } else { - // If there is a duplicate site, notify the user something could be wrong, - // but continue the sign in process - errorMessage = getString(R.string.duplicate_site_detected); - } - } else { - switch (event.error.selfHostedErrorType) { - case XML_RPC_SERVICES_DISABLED: - errorMessage = getString(R.string.login_error_xml_rpc_services_disabled); - break; - case UNABLE_TO_READ_SITE: - errorMessage = getString(R.string.login_error_xml_rpc_cannot_read_site); - break; - case NOT_SET: - default: - errorMessage = getString(R.string.login_error_while_adding_site, event.error.type.toString()); - } - } - - AppLog.e(T.API, "Login with username/pass onSiteChanged has error: " + event.error.type - + " - " + errorMessage); - - if (!mAuthFailed) { - // show the error if not already displayed in onAuthenticationChanged (like in username/pass error) - showError(errorMessage); - } - - return; - } - - if (!mIsWpcom && mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE) { - SiteModel lastAddedXMLRPCSite = SiteUtils.getXMLRPCSiteByUrl(mSiteStore, mInputSiteAddress); - if (lastAddedXMLRPCSite != null) { - // the wp.getOptions endpoint is already called - // verify if jetpack user email is available. - // If not, redirect to jetpack required screen. Otherwise, initiate magic sign in - if (mGetSiteOptionsInitiated) { - endProgress(); - mGetSiteOptionsInitiated = false; - String userEmail = lastAddedXMLRPCSite.getJetpackUserEmail(); - ActivityUtils.hideKeyboard(getActivity()); - if (userEmail == null || userEmail.isEmpty()) { - mLoginListener.helpNoJetpackScreen(lastAddedXMLRPCSite.getUrl(), - lastAddedXMLRPCSite.getXmlRpcUrl(), - SiteModelExtensionsKt.getUserNameProcessed(lastAddedXMLRPCSite), - SiteModelExtensionsKt.getPasswordProcessed(lastAddedXMLRPCSite), - mAccountStore.getAccount().getAvatarUrl(), - false); - } else { - mLoginListener.gotWpcomEmail(userEmail, true, null); - } - } else { - // Initiate the wp.getOptions endpoint to fetch the jetpack user email - mGetSiteOptionsInitiated = true; - mDispatcher.dispatch(SiteActionBuilder.newFetchSiteAction(lastAddedXMLRPCSite)); - } - } - return; - } - - SiteModel newlyAddedXMLRPCSite = detectNewlyAddedXMLRPCSite(); - // newlyAddedSite will be null if the user sign in with wpcom credentials - if (newlyAddedXMLRPCSite != null && !newlyAddedXMLRPCSite.isUsingWpComRestApi()) { - mDispatcher.dispatch(SiteActionBuilder.newFetchProfileXmlRpcAction(newlyAddedXMLRPCSite)); - } else { - finishLogin(); - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onProfileFetched(OnProfileFetched event) { - if (!isAdded() || mLoginFinished) { - return; - } - - if (event.isError()) { - if (mRequestedUsername == null) { - // just bail since the operation was cancelled - return; - } - - endProgress(); - - AppLog.e(T.API, "Fetching selfhosted site profile has error: " + event.error.type + " - " - + event.error.message); - - // continue with success, even if the operation was cancelled since the user got logged in regardless. - // So, go on with finishing the login process - } - finishLogin(); - } -} - diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index 267c712d9d1f..c72e57cc6627 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -4,7 +4,6 @@ import org.wordpress.android.login.LoginEmailFragment; import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; -import org.wordpress.android.login.LoginUsernamePasswordFragment; import org.wordpress.android.login.SignupConfirmationFragment; import org.wordpress.android.login.SignupGoogleFragment; import org.wordpress.android.login.SignupMagicLinkFragment; @@ -26,9 +25,6 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); - @ContributesAndroidInjector - abstract LoginUsernamePasswordFragment loginUsernamePasswordFragment(); - @ContributesAndroidInjector abstract SignupGoogleFragment signupGoogleFragment(); diff --git a/libs/login/src/main/res/layout/login_username_password_screen.xml b/libs/login/src/main/res/layout/login_username_password_screen.xml deleted file mode 100644 index 2dd5b824a902..000000000000 --- a/libs/login/src/main/res/layout/login_username_password_screen.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - From 0272e2e0f6f8574e3d68b4dfb73c99cedd5ef4ac Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:46:20 -0700 Subject: [PATCH 04/71] Remove Google Login --- .../android/ui/accounts/LoginActivity.java | 127 +------- .../android/login/GoogleFragment.java | 277 ----------------- .../android/login/Login2FaFragment.java | 39 +-- .../android/login/LoginBaseFormFragment.java | 9 - .../android/login/LoginEmailFragment.java | 91 ++---- .../android/login/LoginGoogleFragment.java | 249 ---------------- .../android/login/LoginListener.java | 10 - .../login/SignupConfirmationFragment.kt | 150 ---------- .../android/login/SignupGoogleFragment.java | 281 ------------------ .../android/login/di/LoginFragmentModule.java | 12 - .../res/layout/signup_confirmation_screen.xml | 49 --- 11 files changed, 21 insertions(+), 1273 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/GoogleFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/SignupConfirmationFragment.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java delete mode 100644 libs/login/src/main/res/layout/signup_confirmation_screen.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 1885a497417d..f581faabe8af 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -39,17 +39,12 @@ import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; import org.wordpress.android.util.SiteUtils; import org.wordpress.android.login.AuthOptions; -import org.wordpress.android.login.GoogleFragment; -import org.wordpress.android.login.GoogleFragment.GoogleListener; import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginAnalyticsListener; import org.wordpress.android.login.LoginEmailFragment; -import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; -import org.wordpress.android.login.SignupConfirmationFragment; -import org.wordpress.android.login.SignupGoogleFragment; import org.wordpress.android.login.SignupMagicLinkFragment; import org.wordpress.android.support.SupportWebViewActivity; import org.wordpress.android.support.ZendeskExtraTags; @@ -109,7 +104,7 @@ @AndroidEntryPoint public class LoginActivity extends BaseAppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener, - Callback, LoginListener, GoogleListener, LoginPrologueListener, + Callback, LoginListener, LoginPrologueListener, HasAndroidInjector, BasicDialogPositiveClickInterface { public static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; public static final String MAGIC_LOGIN = "magic-login"; @@ -121,8 +116,6 @@ public class LoginActivity extends BaseAppCompatActivity implements ConnectionCa private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; - private static final String GOOGLE_ERROR_DIALOG_TAG = "google_error_dialog_tag"; - private enum SmartLockHelperState { NOT_TRIGGERED, TRIGGER_FILL_IN_ON_CONNECT, @@ -378,13 +371,6 @@ private void slideInFragment(Fragment fragment, boolean shouldAddToBackStack, St fragmentTransaction.commitAllowingStateLoss(); } - private void addGoogleFragment(GoogleFragment googleFragment, String tag) { - FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); - googleFragment.setRetainInstance(true); - fragmentTransaction.add(googleFragment, tag); - fragmentTransaction.commit(); - } - private LoginPrologueRevampedFragment getLoginPrologueRevampedFragment() { Fragment fragment = getSupportFragmentManager().findFragmentByTag(LoginPrologueRevampedFragment.TAG); return fragment == null ? null : (LoginPrologueRevampedFragment) fragment; @@ -605,25 +591,11 @@ public void gotUnregisteredEmail(String email) { showSignupMagicLink(email); } - @Override - public void gotUnregisteredSocialAccount(String email, String displayName, String idToken, String photoUrl, - String service) { - SignupConfirmationFragment signupConfirmationFragment = - SignupConfirmationFragment.newInstance(email, displayName, idToken, photoUrl, service); - slideInFragment(signupConfirmationFragment, true, SignupConfirmationFragment.TAG); - } - @Override public void loginViaSiteAddress() { slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); } - @Override - public void loggedInViaSocialAccount(ArrayList oldSitesIds, boolean doLoginUpdate) { - mLoginAnalyticsListener.trackLoginSocialSuccess(); - loggedInAndFinish(oldSitesIds, doLoginUpdate); - } - @Override public void showSignupMagicLink(String email) { boolean isEmailClientAvailable = WPActivityUtils.isEmailClientAvailable(this); @@ -634,14 +606,6 @@ public void showSignupMagicLink(String email) { slideInFragment(signupMagicLinkFragment, true, SignupMagicLinkFragment.TAG); } - @Override - public void showSignupSocial(String email, String displayName, String idToken, String photoUrl, String service) { - if (GoogleFragment.SERVICE_TYPE_GOOGLE.equals(service)) { - addGoogleFragment(SignupGoogleFragment.newInstance(email, displayName, idToken, photoUrl), - SignupGoogleFragment.TAG); - } - } - @Override public void openEmailClient(boolean isLogin) { mUnifiedLoginTracker.trackClick(Click.OPEN_EMAIL_CLIENT); @@ -673,24 +637,6 @@ public void needs2fa(String email, String password, String userId, String webaut slideInFragment(login2FaFragment, true, Login2FaFragment.TAG); } - @Override - public void needs2faSocial(String email, String userId, String nonceAuthenticator, String nonceBackup, - String nonceSms, String nonceWebauthn, List authTypes) { - mLoginAnalyticsListener.trackLoginSocial2faNeeded(); - Login2FaFragment login2FaFragment = Login2FaFragment.newInstanceSocial(email, userId, - nonceAuthenticator, nonceBackup, - nonceSms, nonceWebauthn, authTypes); - slideInFragment(login2FaFragment, true, Login2FaFragment.TAG); - } - - @Override - public void needs2faSocialConnect(String email, String password, String idToken, String service) { - mLoginAnalyticsListener.trackLoginSocial2faNeeded(); - Login2FaFragment login2FaFragment = - Login2FaFragment.newInstanceSocialConnect(email, password, idToken, service); - slideInFragment(login2FaFragment, true, Login2FaFragment.TAG); - } - @Override public void loggedInViaPassword(ArrayList oldSitesIds) { loggedInAndFinish(oldSitesIds, false); @@ -764,21 +710,6 @@ public void helpSignupMagicLinkScreen(String email) { viewHelp(Origin.SIGNUP_MAGIC_LINK); } - @Override - public void helpSignupConfirmationScreen(String email) { - viewHelp(Origin.SIGNUP_CONFIRMATION); - } - - @Override - public void helpSocialEmailScreen(String email) { - viewHelp(Origin.LOGIN_SOCIAL); - } - - @Override - public void addGoogleLoginFragment(boolean isSignupFromLoginEnabled) { - addGoogleFragment(LoginGoogleFragment.newInstance(isSignupFromLoginEnabled), LoginGoogleFragment.TAG); - } - @Override public void help2FaScreen(String email) { viewHelp(Origin.LOGIN_2FA); @@ -858,63 +789,9 @@ public void showSignupToLoginMessage() { ).show(); } - // GoogleListener - - @Override - public void onGoogleEmailSelected(String email) { - LoginEmailFragment loginEmailFragment = - (LoginEmailFragment) getSupportFragmentManager().findFragmentByTag(LoginEmailFragment.TAG); - if (loginEmailFragment != null) { - loginEmailFragment.setGoogleEmail(email); - } - } - - @Override - public void onGoogleLoginFinished() { - LoginEmailFragment loginEmailFragment = - (LoginEmailFragment) getSupportFragmentManager().findFragmentByTag(LoginEmailFragment.TAG); - if (loginEmailFragment != null) { - loginEmailFragment.finishLogin(); - } - } - - @Override - public void onGoogleSignupFinished(String name, String email, String photoUrl, String username) { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_SOCIAL_SUCCESS); - if (mIsJetpackConnect) { - ActivityLauncher.showSignupEpilogueForResult(this, name, email, photoUrl, username, false); - } else { - ActivityLauncher.showMainActivityAndSignupEpilogue(this, name, email, photoUrl, username); - } - - setResult(Activity.RESULT_OK); - finish(); - } - - @Override - public void onGoogleSignupError(String msg) { - mUnifiedLoginTracker.trackFailure(msg); - // Only show the error dialog if the activity is still active - if (!getSupportFragmentManager().isStateSaved()) { - BasicFragmentDialog dialog = new BasicFragmentDialog(); - dialog.initialize(GOOGLE_ERROR_DIALOG_TAG, getString(R.string.error), - msg, - getString(org.wordpress.android.login.R.string.login_error_button), - null, - null); - dialog.show(getSupportFragmentManager(), GOOGLE_ERROR_DIALOG_TAG); - } else { - AppLog.d(T.MAIN, "'Google sign up failed' dialog not shown, because the activity wasn't visible."); - } - } - @Override public void onPositiveClicked(@NonNull String instanceTag) { - switch (instanceTag) { - case GOOGLE_ERROR_DIALOG_TAG: - // just dismiss the dialog - break; - } + // No dialog tags currently handled } @Override public AndroidInjector androidInjector() { diff --git a/libs/login/src/main/java/org/wordpress/android/login/GoogleFragment.java b/libs/login/src/main/java/org/wordpress/android/login/GoogleFragment.java deleted file mode 100644 index 3a2dfa665099..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/GoogleFragment.java +++ /dev/null @@ -1,277 +0,0 @@ -package org.wordpress.android.login; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Bundle; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.signin.GoogleSignInOptions; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; -import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; - -import org.wordpress.android.fluxc.Dispatcher; -import org.wordpress.android.fluxc.store.SiteStore; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.inject.Inject; - -import static android.app.Activity.RESULT_OK; - -public abstract class GoogleFragment extends Fragment implements ConnectionCallbacks, OnConnectionFailedListener { - private static final String STATE_SHOULD_RESOLVE_ERROR = "STATE_SHOULD_RESOLVE_ERROR"; - private static final String STATE_FINISHED = "STATE_FINISHED"; - private static final String STATE_DISPLAY_NAME = "STATE_DISPLAY_NAME"; - private static final String STATE_GOOGLE_EMAIL = "STATE_GOOGLE_EMAIL"; - private static final String STATE_GOOGLE_TOKEN_ID = "STATE_GOOGLE_TOKEN_ID"; - private static final String STATE_GOOGLE_PHOTO_URL = "STATE_GOOGLE_PHOTO_URL"; - private boolean mIsResolvingError; - private boolean mShouldResolveError; - /** - * This flag is used to store the information the finishFlow was called when the fragment wasn't attached to an - * activity (for example an EventBus event was received during ongoing configuration change). - */ - private boolean mFinished; - - private static final String STATE_RESOLVING_ERROR = "STATE_RESOLVING_ERROR"; - private static final int REQUEST_CONNECT = 1000; - - protected GoogleApiClient mGoogleApiClient; - protected GoogleListener mGoogleListener; - protected LoginListener mLoginListener; - protected String mDisplayName; - protected String mGoogleEmail; - protected String mIdToken; - protected String mPhotoUrl; - - protected ProgressDialog mProgressDialog; - - public static final String SERVICE_TYPE_GOOGLE = "google"; - - @Inject protected Dispatcher mDispatcher; - @Inject protected SiteStore mSiteStore; - - @Inject protected LoginAnalyticsListener mAnalyticsListener; - - public interface GoogleListener { - void onGoogleEmailSelected(String email); - void onGoogleLoginFinished(); - void onGoogleSignupFinished(String name, String email, String photoUrl, String username); - void onGoogleSignupError(String msg); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mDispatcher.register(this); - if (savedInstanceState != null) { - mIsResolvingError = savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false); - mShouldResolveError = savedInstanceState.getBoolean(STATE_SHOULD_RESOLVE_ERROR, false); - mFinished = savedInstanceState.getBoolean(STATE_FINISHED, false); - mDisplayName = savedInstanceState.getString(STATE_DISPLAY_NAME); - mGoogleEmail = savedInstanceState.getString(STATE_GOOGLE_EMAIL); - mIdToken = savedInstanceState.getString(STATE_GOOGLE_TOKEN_ID); - mPhotoUrl = savedInstanceState.getString(STATE_GOOGLE_PHOTO_URL); - } - - // Configure sign-in to request user's ID, basic profile, email address, and ID token. - // ID and basic profile are included in DEFAULT_SIGN_IN. - GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestServerAuthCode(getString(R.string.default_web_client_id)) - .requestIdToken(getString(R.string.default_web_client_id)) - .requestProfile() - .requestEmail() - .build(); - - // Build Google API client with access to sign-in API and options specified above. - mGoogleApiClient = new GoogleApiClient.Builder(getActivity().getApplicationContext()) - .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - - if (!mIsResolvingError) { - connectGoogleClient(); - } - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(STATE_RESOLVING_ERROR, mIsResolvingError); - outState.putBoolean(STATE_SHOULD_RESOLVE_ERROR, mShouldResolveError); - outState.putBoolean(STATE_FINISHED, mFinished); - outState.putString(STATE_DISPLAY_NAME, mDisplayName); - outState.putString(STATE_GOOGLE_EMAIL, mGoogleEmail); - outState.putString(STATE_GOOGLE_TOKEN_ID, mIdToken); - outState.putString(STATE_GOOGLE_PHOTO_URL, mPhotoUrl); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - mProgressDialog = ProgressDialog.show(getActivity(), null, getProgressDialogText(), true, false, null); - mLoginListener = (LoginListener) context; - - try { - mGoogleListener = (GoogleListener) context; - } catch (ClassCastException exception) { - throw new ClassCastException(context.toString() + " must implement GoogleListener"); - } - if (mFinished) { - finishFlow(); - } - } - - @Override - public void onDetach() { - dismissProgressDialog(); - super.onDetach(); - } - - @Override - public void onDestroy() { - disconnectGoogleClient(); - AppLog.d(T.MAIN, "GOOGLE SIGNUP/LOGIN: disconnecting google client"); - mDispatcher.unregister(this); - super.onDestroy(); - } - - @Override - public void onResume() { - super.onResume(); - // Show account dialog when Google API onConnected callback returns before fragment is attached. - if (mGoogleApiClient != null && mGoogleApiClient.isConnected() && !mIsResolvingError && !mShouldResolveError) { - startFlow(); - } - } - - @Override - public void onConnected(Bundle bundle) { - // Indicates account was selected, that account has granted any required permissions, and a - // connection to Google Play services has been established. - if (mShouldResolveError) { - mShouldResolveError = false; - - // if the fragment is not attached to an activity, the process is started in the onResume - if (isAdded()) { - startFlow(); - } - } - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - // Could not connect to Google Play Services. The user needs to select an account, grant - // permissions or resolve an error in order to sign in. Refer to the documentation for - // ConnectionResult to see possible error codes. - if (!mIsResolvingError && mShouldResolveError) { - if (connectionResult.hasResolution()) { - try { - mIsResolvingError = true; - connectionResult.startResolutionForResult(getActivity(), REQUEST_CONNECT); - } catch (IntentSender.SendIntentException exception) { - mIsResolvingError = false; - mGoogleApiClient.connect(); - } - } else { - mIsResolvingError = false; - AppLog.e(AppLog.T.NUX, GoogleApiAvailability.getInstance().getErrorString( - connectionResult.getErrorCode())); - showError(getString(R.string.login_error_generic)); - } - } - } - - @Override - public void onConnectionSuspended(int i) { - // Connection to Google Play services was lost. GoogleApiClient will automatically attempt - // to re-connect. Any UI elements depending on connection to Google APIs should be hidden - // or disabled until onConnected is called again. - Log.w(LoginGoogleFragment.class.getSimpleName(), "onConnectionSuspended: " + i); - } - - public void connectGoogleClient() { - if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { - mShouldResolveError = true; - mGoogleApiClient.connect(); - } else { - startFlow(); - } - } - - protected void disconnectGoogleClient() { - if (mGoogleApiClient.isConnected()) { - Auth.GoogleSignInApi.signOut(mGoogleApiClient); - mGoogleApiClient.disconnect(); - } - } - - protected abstract String getProgressDialogText(); - - protected abstract void startFlow(); - - protected void finishFlow() { - /* This flag might get lost when the finishFlow is called after the fragment's - onSaveInstanceState was called - however it's a very rare case, since the fragment is retained across - config changes. */ - mFinished = true; - if (getActivity() != null) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP/LOGIN: finishing signup/login"); - getActivity().getSupportFragmentManager().beginTransaction().remove(this).commitAllowingStateLoss(); - } - } - - protected void showError(String message) { - finishFlow(); - mGoogleListener.onGoogleSignupError(message); - } - - @Override - public void onActivityResult(int request, int result, Intent data) { - super.onActivityResult(request, result, data); - - switch (request) { - case REQUEST_CONNECT: - if (result != RESULT_OK) { - mShouldResolveError = false; - } - - if (!mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { - mGoogleApiClient.connect(); - } else { - startFlow(); - } - - mIsResolvingError = false; - break; - } - } - - private void dismissProgressDialog() { - if (mProgressDialog != null && mProgressDialog.isShowing()) { - mProgressDialog.dismiss(); - } - } - - // Remove scale from photo URL path string. Current URL matches /s96-c, which returns a 96 x 96 - // pixel image. Removing /s96-c from the string returns a 512 x 512 pixel image. Using regular - // expressions may help if the photo URL scale value in the returned path changes. - protected String removeScaleFromGooglePhotoUrl(String photoUrl) { - Pattern pattern = Pattern.compile("(/s[0-9]+-c)"); - Matcher matcher = pattern.matcher(photoUrl); - return matcher.find() ? photoUrl.replace(matcher.group(1), "") : photoUrl; - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java b/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java index 3fd2c66b825e..c811ae629ebe 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java @@ -146,39 +146,6 @@ public static Login2FaFragment newInstance(String emailAddress, String password, return fragment; } - public static Login2FaFragment newInstanceSocial(String emailAddress, String userId, - String nonceAuthenticator, String nonceBackup, - String nonceSms, String nonceWebauthn, - List authTypes) { - Login2FaFragment fragment = new Login2FaFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, emailAddress); - args.putString(ARG_2FA_USER_ID, userId); - args.putString(ARG_2FA_NONCE_AUTHENTICATOR, nonceAuthenticator); - args.putString(ARG_2FA_NONCE_BACKUP, nonceBackup); - args.putString(ARG_2FA_NONCE_SMS, nonceSms); - args.putString(ARG_WEBAUTHN_NONCE, nonceWebauthn); - args.putBoolean(ARG_2FA_IS_SOCIAL, true); - // Social account connected, connect call not needed. - args.putBoolean(ARG_2FA_IS_SOCIAL_CONNECT, false); - args.putStringArrayList(ARG_2FA_SUPPORTED_AUTH_TYPES, new ArrayList<>(authTypes)); - fragment.setArguments(args); - return fragment; - } - - public static Login2FaFragment newInstanceSocialConnect(String emailAddress, String password, - String idToken, String service) { - Login2FaFragment fragment = new Login2FaFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, emailAddress); - args.putString(ARG_PASSWORD, password); - args.putString(ARG_2FA_ID_TOKEN, idToken); - args.putBoolean(ARG_2FA_IS_SOCIAL_CONNECT, true); - args.putString(ARG_2FA_SOCIAL_SERVICE, service); - fragment.setArguments(args); - return fragment; - } - @Override protected @LayoutRes int getContentLayout() { return R.layout.login_2fa_screen; @@ -574,11 +541,7 @@ protected void onLoginFinished() { mLoginListener.startPostLoginServices(); - if (mIsSocialLogin) { - mLoginListener.loggedInViaSocialAccount(mOldSitesIDs, false); - } else { - mLoginListener.loggedInViaPassword(mOldSitesIDs); - } + mLoginListener.loggedInViaPassword(mOldSitesIDs); } private void setTextForSms() { diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java index 75083474b415..b154f45a46b9 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java @@ -2,7 +2,6 @@ import android.app.ProgressDialog; import android.content.Context; -import android.net.Uri; import android.os.Bundle; import android.text.TextWatcher; import android.view.LayoutInflater; @@ -300,14 +299,6 @@ protected void onLoginFinished(boolean success) { endProgress(); } - protected void saveCredentialsInSmartLock(LoginListener loginListener, String username, String password) { - // mUsername and mPassword are null when the user log in with a magic link - if (loginListener != null) { - loginListener.saveCredentialsInSmartLock(username, password, mAccountStore.getAccount().getDisplayName(), - Uri.parse(mAccountStore.getAccount().getAvatarUrl())); - } - } - // OnChanged events @SuppressWarnings("unused") diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java index 516c78619ef2..4f034e6ed6e2 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java @@ -43,7 +43,6 @@ import org.wordpress.android.fluxc.store.AccountStore.FetchAuthOptionsPayload; import org.wordpress.android.fluxc.store.AccountStore.OnAuthOptionsFetched; import org.wordpress.android.login.util.ContextExtensionsKt; -import org.wordpress.android.login.util.SiteUtils; import org.wordpress.android.login.widgets.WPLoginInputRow; import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener; import org.wordpress.android.util.ActivityUtils; @@ -55,7 +54,6 @@ import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.ToastUtils.Duration; -import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -65,11 +63,8 @@ public class LoginEmailFragment extends LoginBaseFormFragment implements TextWatcher, OnEditorCommitListener, ConnectionCallbacks, OnConnectionFailedListener { - private static final String KEY_GOOGLE_EMAIL = "KEY_GOOGLE_EMAIL"; private static final String KEY_HAS_DISMISSED_EMAIL_HINTS = "KEY_HAS_DISMISSED_EMAIL_HINTS"; private static final String KEY_IS_DISPLAYING_EMAIL_HINTS = "KEY_IS_DISPLAYING_EMAIL_HINTS"; - private static final String KEY_IS_SOCIAL = "KEY_IS_SOCIAL"; - private static final String KEY_OLD_SITES_IDS = "KEY_OLD_SITES_IDS"; private static final String KEY_REQUESTED_EMAIL = "KEY_REQUESTED_EMAIL"; private static final String KEY_EMAIL_ERROR_RES = "KEY_EMAIL_ERROR_RES"; private static final String LOG_TAG = LoginEmailFragment.class.getSimpleName(); @@ -85,11 +80,8 @@ public class LoginEmailFragment extends LoginBaseFormFragment imp public static final String TAG_SITE_CREDS_LAYOUT = "login_email_fragment_site_creds_layout_tag"; public static final int MAX_EMAIL_LENGTH = 100; - private ArrayList mOldSitesIDs = new ArrayList<>(); private GoogleApiClient mGoogleApiClient; - private String mGoogleEmail; private String mRequestedEmail; - private boolean mIsSocialLogin; private Integer mCurrentEmailErrorRes = null; private boolean mIsSignupFromLoginEnabled; private boolean mOptionalSiteCredsLayout; @@ -141,7 +133,7 @@ private static LoginEmailFragment newInstance(String url, @Override protected @LayoutRes int getProgressBarText() { - return mIsSocialLogin ? R.string.logging_in : R.string.checking_email; + return R.string.checking_email; } @Override @@ -197,10 +189,8 @@ protected void setupContent(ViewGroup rootView) { (Button) rootView.findViewById(R.id.login_find_connected_email)); } else { setupContinueButton((Button) rootView.findViewById(R.id.login_continue_button)); - setupTosButtons( - (Button) rootView.findViewById(R.id.continue_tos), - (Button) rootView.findViewById(R.id.continue_with_google_tos)); - setupSocialButtons((Button) rootView.findViewById(R.id.continue_with_google)); + setupTosButton((Button) rootView.findViewById(R.id.continue_tos)); + hideGoogleButton(rootView); } } @@ -262,38 +252,29 @@ private void updateContinueButtonEnabledStatus() { super.onDestroyView(); } - private void setupTosButtons(Button continueTosButton, Button continueWithGoogleTosButton) { + private void setupTosButton(Button continueTosButton) { if (mHideTos) { - // Hide the TOS buttons continueTosButton.setVisibility(View.GONE); - continueWithGoogleTosButton.setVisibility(View.GONE); } else { - // Show the TOS buttons continueTosButton.setVisibility(View.VISIBLE); - continueWithGoogleTosButton.setVisibility(View.VISIBLE); - - OnClickListener onClickListener = new OnClickListener() { + continueTosButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { mLoginListener.onTermsOfServiceClicked(); } - }; - - continueTosButton.setOnClickListener(onClickListener); + }); continueTosButton.setText(formatTosText(R.string.continue_terms_of_service_text)); - - continueWithGoogleTosButton.setOnClickListener(onClickListener); - continueWithGoogleTosButton - .setText(formatTosText(R.string.continue_with_google_terms_of_service_text)); } } - private void setupSocialButtons(Button continueWithGoogleButton) { - continueWithGoogleButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - onGoogleSigninClicked(); - } - }); + private void hideGoogleButton(ViewGroup rootView) { + View googleTosButton = rootView.findViewById(R.id.continue_with_google_tos); + if (googleTosButton != null) { + googleTosButton.setVisibility(View.GONE); + } + View googleButton = rootView.findViewById(R.id.continue_with_google); + if (googleButton != null) { + googleButton.setVisibility(View.GONE); + } } private void setupSiteCredsButton(Button continueWithSiteCreds) { @@ -328,32 +309,10 @@ private void onContinueClicked() { next(getCleanedEmail()); } - private void onGoogleSigninClicked() { - mAnalyticsListener.trackSocialButtonClick(); - ActivityUtils.hideKeyboardForced(mEmailInput.getEditText()); - - if (NetworkUtils.checkConnection(getActivity())) { - if (isAdded()) { - mOldSitesIDs = SiteUtils.getCurrentSiteIds(mSiteStore, false); - mIsSocialLogin = true; - mLoginListener.addGoogleLoginFragment(mIsSignupFromLoginEnabled); - } else { - AppLog.e(T.NUX, "Google login could not be started. LoginEmailFragment was not attached."); - showErrorDialog(getString(R.string.login_error_generic_start)); - } - } - } - @Override protected void onHelp() { if (mLoginListener != null) { - if (mIsSocialLogin) { - // Send last email chosen from Google login if available. - mLoginListener.helpSocialEmailScreen(mGoogleEmail); - } else { - // Send exact string the user has inputted for email - mLoginListener.helpEmailScreen(EditTextUtils.getText(mEmailInput.getEditText())); - } + mLoginListener.helpEmailScreen(EditTextUtils.getText(mEmailInput.getEditText())); } } @@ -403,10 +362,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { - mOldSitesIDs = savedInstanceState.getIntegerArrayList(KEY_OLD_SITES_IDS); mRequestedEmail = savedInstanceState.getString(KEY_REQUESTED_EMAIL); - mGoogleEmail = savedInstanceState.getString(KEY_GOOGLE_EMAIL); - mIsSocialLogin = savedInstanceState.getBoolean(KEY_IS_SOCIAL); mIsDisplayingEmailHints = savedInstanceState.getBoolean(KEY_IS_DISPLAYING_EMAIL_HINTS); mHasDismissedEmailHints = savedInstanceState.getBoolean(KEY_HAS_DISMISSED_EMAIL_HINTS); if (savedInstanceState.containsKey(KEY_EMAIL_ERROR_RES)) { @@ -427,10 +383,7 @@ public void onResume() { @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putIntegerArrayList(KEY_OLD_SITES_IDS, mOldSitesIDs); outState.putString(KEY_REQUESTED_EMAIL, mRequestedEmail); - outState.putString(KEY_GOOGLE_EMAIL, mGoogleEmail); - outState.putBoolean(KEY_IS_SOCIAL, mIsSocialLogin); outState.putBoolean(KEY_IS_DISPLAYING_EMAIL_HINTS, mIsDisplayingEmailHints); outState.putBoolean(KEY_HAS_DISMISSED_EMAIL_HINTS, mHasDismissedEmailHints); if (mCurrentEmailErrorRes != null) { @@ -515,7 +468,6 @@ public void beforeTextChanged(CharSequence s, int start, int count, int after) { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { mEmailInput.setError(null); - mIsSocialLogin = false; clearEmailError(); updateContinueButtonEnabledStatus(); } @@ -608,18 +560,11 @@ public void onAuthOptionsFetched(OnAuthOptionsFetched event) { } } - public void setGoogleEmail(String email) { - mGoogleEmail = email; - } - - public void finishLogin() { - doFinishLogin(); - } - @Override protected void onLoginFinished() { + // This method is called from doFinishLogin() but is no longer used + // since Google login has been removed mAnalyticsListener.trackAnalyticsSignIn(true); - mLoginListener.loggedInViaSocialAccount(mOldSitesIDs, false); } @Override diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java deleted file mode 100644 index a8da875c28f9..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginGoogleFragment.java +++ /dev/null @@ -1,249 +0,0 @@ -package org.wordpress.android.login; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.signin.GoogleSignInAccount; -import com.google.android.gms.auth.api.signin.GoogleSignInResult; -import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.generated.AccountActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged; -import org.wordpress.android.fluxc.store.AccountStore.OnSocialChanged; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialPayload; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; - -import dagger.android.support.AndroidSupportInjection; - -import static android.app.Activity.RESULT_CANCELED; -import static android.app.Activity.RESULT_OK; -import static org.wordpress.android.fluxc.store.AccountStore.AccountSocialErrorType.UNKNOWN_USER; -import static org.wordpress.android.fluxc.store.AccountStore.AccountSocialErrorType.USER_EXISTS; - -public class LoginGoogleFragment extends GoogleFragment { - private static final String ARG_SIGNUP_FROM_LOGIN_ENABLED = "ARG_SIGNUP_FROM_LOGIN_ENABLED"; - private static final int REQUEST_LOGIN = 1001; - private boolean mLoginRequested = false; - private boolean mIsSignupFromLoginEnabled; - - public static final String TAG = "login_google_fragment_tag"; - - public static LoginGoogleFragment newInstance(boolean isSignupFromLoginEnabled) { - LoginGoogleFragment fragment = new LoginGoogleFragment(); - Bundle args = new Bundle(); - args.putBoolean(ARG_SIGNUP_FROM_LOGIN_ENABLED, isSignupFromLoginEnabled); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - Bundle args = getArguments(); - if (args != null) { - mIsSignupFromLoginEnabled = args.getBoolean(ARG_SIGNUP_FROM_LOGIN_ENABLED, false); - } - } - - @Override - protected String getProgressDialogText() { - return getString(R.string.logging_in); - } - - @Override - protected void startFlow() { - if (!mLoginRequested) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: startFlow"); - mLoginRequested = true; - Intent loginIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); - mAnalyticsListener.trackSocialButtonStart(); - startActivityForResult(loginIntent, REQUEST_LOGIN); - } else { - AppLog.d(T.MAIN, "GOOGLE LOGIN: startFlow called, but is already in progress"); - } - } - - @Override - public void onActivityResult(int request, int result, Intent data) { - super.onActivityResult(request, result, data); - - switch (request) { - case REQUEST_LOGIN: - disconnectGoogleClient(); - mLoginRequested = false; - if (result == RESULT_OK) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: Google has returned a sign in result - succcess"); - GoogleSignInResult loginResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data); - - if (loginResult.isSuccess()) { - try { - GoogleSignInAccount account = loginResult.getSignInAccount(); - - if (account != null) { - mDisplayName = account.getDisplayName() != null ? account.getDisplayName() : ""; - mGoogleEmail = account.getEmail() != null ? account.getEmail() : ""; - mGoogleListener.onGoogleEmailSelected(mGoogleEmail); - mIdToken = account.getIdToken() != null ? account.getIdToken() : ""; - mPhotoUrl = removeScaleFromGooglePhotoUrl( - account.getPhotoUrl() != null ? account.getPhotoUrl().toString() : ""); - } - - AppLog.d(T.MAIN, - "GOOGLE LOGIN: Google has returned a sign in result - dispatching " - + "SocialLoginAction"); - PushSocialPayload payload = new PushSocialPayload(mIdToken, SERVICE_TYPE_GOOGLE); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialLoginAction(payload)); - } catch (NullPointerException exception) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: Google has returned a sign in result - NPE"); - AppLog.e(T.NUX, "Cannot get ID token from Google login account.", exception); - showError(getString(R.string.login_error_generic)); - } - } else { - AppLog.d(T.MAIN, "GOOGLE LOGIN: Google has returned a sign in result - error"); - mAnalyticsListener.trackSocialButtonFailure(); - switch (loginResult.getStatus().getStatusCode()) { - // Internal error. - case GoogleSignInStatusCodes.INTERNAL_ERROR: - AppLog.e(T.NUX, "Google Login Failed: internal error."); - showError(getString(R.string.login_error_generic)); - break; - // Attempted to connect with an invalid account name specified. - case GoogleSignInStatusCodes.INVALID_ACCOUNT: - AppLog.e(T.NUX, "Google Login Failed: invalid account name."); - showError(getString(R.string.login_error_generic) - + getString(R.string.login_error_suffix)); - break; - // Network error. - case GoogleSignInStatusCodes.NETWORK_ERROR: - AppLog.e(T.NUX, "Google Login Failed: network error."); - showError(getString(R.string.error_generic_network)); - break; - // Cancelled by the user. - case GoogleSignInStatusCodes.SIGN_IN_CANCELLED: - AppLog.e(T.NUX, "Google Login Failed: cancelled by user."); - break; - // Attempt didn't succeed with the current account. - case GoogleSignInStatusCodes.SIGN_IN_FAILED: - AppLog.e(T.NUX, "Google Login Failed: current account failed."); - showError(getString(R.string.login_error_generic)); - break; - // Attempted to connect, but the user is not signed in. - case GoogleSignInStatusCodes.SIGN_IN_REQUIRED: - AppLog.e(T.NUX, "Google Login Failed: user is not signed in."); - showError(getString(R.string.login_error_generic)); - break; - // Timeout error. - case GoogleSignInStatusCodes.TIMEOUT: - AppLog.e(T.NUX, "Google Login Failed: timeout error."); - showError(getString(R.string.google_error_timeout)); - break; - // Unknown error. - default: - AppLog.e(T.NUX, "Google Login Failed: unknown error."); - showError(getString(R.string.login_error_generic)); - break; - } - } - } else if (result == RESULT_CANCELED) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: Google has returned a sign in result - canceled"); - mAnalyticsListener.trackSocialButtonFailure(); - AppLog.e(T.NUX, "Google Login Failed: result was CANCELED."); - finishFlow(); - } else { - AppLog.d(T.MAIN, "GOOGLE LOGIN: Google has returned a sign in result - unknown"); - mAnalyticsListener.trackSocialButtonFailure(); - AppLog.e(T.NUX, "Google Login Failed: result was not OK or CANCELED."); - showError(getString(R.string.login_error_generic)); - } - - break; - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthenticationChanged(OnAuthenticationChanged event) { - if (event.isError()) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: onAuthenticationChanged - error"); - AppLog.e(T.API, "LoginGoogleFragment.onAuthenticationChanged: " + event.error.type - + " - " + event.error.message); - mAnalyticsListener.trackLoginFailed(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - - mAnalyticsListener.trackSocialFailure(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - - showError(getString(R.string.login_error_generic)); - } else { - AppLog.d(T.MAIN, "GOOGLE LOGIN: onAuthenticationChanged - success"); - AppLog.i(T.NUX, "LoginGoogleFragment.onAuthenticationChanged: " + event.toString()); - mGoogleListener.onGoogleLoginFinished(); - finishFlow(); - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSocialChanged(OnSocialChanged event) { - // Response returns error for non-existing account and existing account not connected. - if (event.isError()) { - AppLog.e(T.API, "LoginGoogleFragment.onSocialChanged: " + event.error.type + " - " + event.error.message); - - // We don't want to track these errors: USER_EXISTS and UNKNOWN_USER (if signup from login is enabled) - if (event.error.type != USER_EXISTS && (!mIsSignupFromLoginEnabled || event.error.type != UNKNOWN_USER)) { - mAnalyticsListener.trackLoginFailed(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - - mAnalyticsListener.trackSocialFailure(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - } - - switch (event.error.type) { - // WordPress account exists with input email address, but not connected. - case USER_EXISTS: - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - wordpress account exists but not connected"); - mAnalyticsListener.trackSocialAccountsNeedConnecting(); - showError(getString(R.string.login_error_email_not_connected_to_google)); - break; - // WordPress account does not exist with input email address. - case UNKNOWN_USER: - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - wordpress account doesn't exist"); - if (mIsSignupFromLoginEnabled) { - mLoginListener.gotUnregisteredSocialAccount(mGoogleEmail, mDisplayName, mIdToken, mPhotoUrl, - SERVICE_TYPE_GOOGLE); - } else { - mAnalyticsListener.trackSocialErrorUnknownUser(); - showError(getString(R.string.login_error_email_not_found_v2)); - } - break; - // Too many attempts on sending SMS verification code. The user has to wait before they try again - case SMS_CODE_THROTTLED: - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - error - sms code throttled"); - showError(getString(R.string.login_error_sms_throttled)); - break; - // Unknown error. - case GENERIC_ERROR: - // Do nothing for now (included to show all error types) and just fall through to 'default' - default: - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - unknown error"); - showError(getString(R.string.login_error_generic)); - break; - } - // Response does not return error when two-factor authentication is required. - } else if (event.requiresTwoStepAuth || Login2FaFragment.TWO_FACTOR_TYPE_SMS.equals(event.notificationSent)) { - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - needs 2fa"); - mLoginListener.needs2faSocial(mGoogleEmail, event.userId, event.nonceAuthenticator, event.nonceBackup, - event.nonceSms, event.nonceWebauthn, event.twoStepTypes); - } else { - AppLog.d(T.MAIN, "GOOGLE LOGIN: onSocialChanged - success"); - mGoogleListener.onGoogleLoginFinished(); - } - finishFlow(); - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 566169c8262f..1b9314f8a11e 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -21,14 +21,9 @@ interface SelfSignedSSLCallback { // Login Email input callbacks void gotWpcomEmail(String email, boolean verifyEmail, @Nullable AuthOptions authOptions); void gotUnregisteredEmail(String email); - void gotUnregisteredSocialAccount(String email, String displayName, String idToken, String photoUrl, - String service); void loginViaSiteAddress(); - void loggedInViaSocialAccount(ArrayList oldSiteIds, boolean doLoginUpdate); void loginViaSiteCredentials(String inputSiteAddress); void helpEmailScreen(String email); - void helpSocialEmailScreen(String email); - void addGoogleLoginFragment(boolean isSignupFromLoginEnabled); void showHelpFindingConnectedEmail(); void onTermsOfServiceClicked(); @@ -40,9 +35,6 @@ void gotUnregisteredSocialAccount(String email, String displayName, String idTok void needs2fa(String email, String password, String userId, String webauthnNonce, String nonceAuthenticator, String nonceBackup, String noncePush, List supportedAuthTypes); - void needs2faSocial(String email, String userId, String nonceAuthenticator, String nonceBackup, - String nonceSms, String nonceWebauthn, List supportedAuthTypes); - void needs2faSocialConnect(String email, String password, String idToken, String service); void loggedInViaPassword(ArrayList oldSitesIds); // Login Site Address input callbacks @@ -64,8 +56,6 @@ void needs2faSocial(String email, String userId, String nonceAuthenticator, Stri // Signup void helpSignupEmailScreen(String email); void helpSignupMagicLinkScreen(String email); - void helpSignupConfirmationScreen(String email); void showSignupMagicLink(String email); - void showSignupSocial(String email, String displayName, String idToken, String photoUrl, String service); void showSignupToLoginMessage(); } diff --git a/libs/login/src/main/java/org/wordpress/android/login/SignupConfirmationFragment.kt b/libs/login/src/main/java/org/wordpress/android/login/SignupConfirmationFragment.kt deleted file mode 100644 index 43e72c778754..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/SignupConfirmationFragment.kt +++ /dev/null @@ -1,150 +0,0 @@ -package org.wordpress.android.login - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import android.widget.ProgressBar -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.AppCompatButton -import androidx.appcompat.widget.Toolbar -import androidx.core.view.MenuProvider -import androidx.fragment.app.Fragment -import androidx.lifecycle.Lifecycle -import dagger.android.support.AndroidSupportInjection -import org.wordpress.android.login.util.AvatarHelper.AvatarRequestListener -import org.wordpress.android.login.util.AvatarHelper.loadAvatarFromUrl -import javax.inject.Inject - -class SignupConfirmationFragment : Fragment(), MenuProvider { - private var mLoginListener: LoginListener? = null - - private var mEmail: String? = null - private var mDisplayName: String? = null - private var mIdToken: String? = null - private var mPhotoUrl: String? = null - private var mService: String? = null - - @Inject lateinit var mAnalyticsListener: LoginAnalyticsListener - - companion object { - const val TAG = "signup_confirmation_fragment_tag" - - private const val ARG_EMAIL = "ARG_EMAIL" - private const val ARG_SOCIAL_DISPLAY_NAME = "ARG_SOCIAL_DISPLAY_NAME" - private const val ARG_SOCIAL_ID_TOKEN = "ARG_SOCIAL_ID_TOKEN" - private const val ARG_SOCIAL_PHOTO_URL = "ARG_SOCIAL_PHOTO_URL" - private const val ARG_SOCIAL_SERVICE = "ARG_SOCIAL_SERVICE" - - @JvmStatic fun newInstance( - email: String?, - displayName: String?, - idToken: String?, - photoUrl: String?, - service: String? - ): SignupConfirmationFragment { - return SignupConfirmationFragment().apply { - arguments = Bundle().apply { - putString(ARG_EMAIL, email) - putString(ARG_SOCIAL_DISPLAY_NAME, displayName) - putString(ARG_SOCIAL_ID_TOKEN, idToken) - putString(ARG_SOCIAL_PHOTO_URL, photoUrl) - putString(ARG_SOCIAL_SERVICE, service) - } - } - } - } - - override fun onAttach(context: Context) { - AndroidSupportInjection.inject(this) - super.onAttach(context) - if (context !is LoginListener) { - @Suppress("TooGenericExceptionThrown") - throw RuntimeException("$context must implement LoginListener") - } - mLoginListener = context - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - mEmail = it.getString(ARG_EMAIL) - mDisplayName = it.getString(ARG_SOCIAL_DISPLAY_NAME) - mIdToken = it.getString(ARG_SOCIAL_ID_TOKEN) - mPhotoUrl = it.getString(ARG_SOCIAL_PHOTO_URL) - mService = it.getString(ARG_SOCIAL_SERVICE) - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = inflater.inflate(R.layout.signup_confirmation_screen, container, false) - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - (activity as AppCompatActivity?)?.apply { - val toolbar = view.findViewById(R.id.toolbar) - setSupportActionBar(toolbar) - supportActionBar?.apply { - setTitle(R.string.sign_up_label) - setDisplayHomeAsUpEnabled(true) - } - } - - view.findViewById(R.id.email).text = mEmail - - val avatarProgressBar = view.findViewById(R.id.avatar_progress) - val avatarRequestListener = object : AvatarRequestListener { - override fun onRequestFinished() { - avatarProgressBar.visibility = View.GONE - } - } - - loadAvatarFromUrl(this, mPhotoUrl, view.findViewById(R.id.gravatar), avatarRequestListener) - view.findViewById(R.id.signup_confirmation_button).setOnClickListener { - mAnalyticsListener.trackCreateAccountClick() - mLoginListener?.showSignupSocial(mEmail, mDisplayName, mIdToken, mPhotoUrl, mService) - } - - if (savedInstanceState == null) { - mAnalyticsListener.trackSocialSignupConfirmationViewed() - } - - requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED) - } - - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - // important for accessibility - talkback - activity?.setTitle(R.string.signup_confirmation_title) - } - - override fun onDetach() { - super.onDetach() - mLoginListener = null - } - - override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.menu_login, menu) - } - - override fun onMenuItemSelected(item: MenuItem): Boolean { - if (item.itemId == R.id.help) { - mAnalyticsListener.trackShowHelpClick() - if (mLoginListener != null) { - mLoginListener?.helpSignupConfirmationScreen(mEmail) - } - return true - } - return false - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java b/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java deleted file mode 100644 index 55d75b378b12..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/SignupGoogleFragment.java +++ /dev/null @@ -1,281 +0,0 @@ -package org.wordpress.android.login; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; - -import androidx.annotation.NonNull; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.signin.GoogleSignInAccount; -import com.google.android.gms.auth.api.signin.GoogleSignInResult; -import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.generated.AccountActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged; -import org.wordpress.android.fluxc.store.AccountStore.OnSocialChanged; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialPayload; -import org.wordpress.android.login.util.SiteUtils; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; - -import java.util.ArrayList; - -import dagger.android.support.AndroidSupportInjection; - -import static android.app.Activity.RESULT_CANCELED; -import static android.app.Activity.RESULT_OK; -import static org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource.GOOGLE; - -public class SignupGoogleFragment extends GoogleFragment { - private static final String OLD_SITES_IDS = "old_sites_ids"; - private static final String SIGN_UP_REQUESTED = "sign_up_requested"; - - private static final String ARG_GOOGLE_EMAIL = "ARG_GOOGLE_EMAIL"; - private static final String ARG_DISPLAY_NAME = "ARG_DISPLAY_NAME"; - private static final String ARG_ID_TOKEN = "ARG_ID_TOKEN"; - private static final String ARG_PHOTO_URL = "ARG_PHOTO_URL"; - private static final String ARG_FORCE_SIGNUP_AT_START = "ARG_FORCE_SIGNUP_AT_START"; - - private ArrayList mOldSitesIds; - private boolean mSignupRequested; - private boolean mForceSignupAtStart; - - private static final int REQUEST_SIGNUP = 1002; - - public static final String TAG = "signup_google_fragment_tag"; - - public static SignupGoogleFragment newInstance(String email, String displayName, String idToken, String photoUrl) { - SignupGoogleFragment fragment = new SignupGoogleFragment(); - Bundle args = new Bundle(); - args.putString(ARG_GOOGLE_EMAIL, email); - args.putString(ARG_DISPLAY_NAME, displayName); - args.putString(ARG_ID_TOKEN, idToken); - args.putString(ARG_PHOTO_URL, photoUrl); - args.putBoolean(ARG_FORCE_SIGNUP_AT_START, true); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - Bundle args = getArguments(); - if (args != null) { - mDisplayName = args.getString(ARG_DISPLAY_NAME); - mGoogleEmail = args.getString(ARG_GOOGLE_EMAIL); - mIdToken = args.getString(ARG_ID_TOKEN); - mPhotoUrl = args.getString(ARG_PHOTO_URL); - mForceSignupAtStart = args.getBoolean(ARG_FORCE_SIGNUP_AT_START); - } - } - - @Override - protected String getProgressDialogText() { - return getString(R.string.signup_with_google_progress); - } - - @Override public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState != null) { - mOldSitesIds = savedInstanceState.getIntegerArrayList(OLD_SITES_IDS); - mSignupRequested = savedInstanceState.getBoolean(SIGN_UP_REQUESTED); - } - } - - @Override public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - outState.putIntegerArrayList(OLD_SITES_IDS, mOldSitesIds); - outState.putBoolean(SIGN_UP_REQUESTED, mSignupRequested); - } - - @Override - protected void startFlow() { - if (mForceSignupAtStart) { - dispatchSocialSignup(mIdToken); - } else { - if (!mSignupRequested) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: startFlow"); - mSignupRequested = true; - Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); - startActivityForResult(signInIntent, REQUEST_SIGNUP); - } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: startFlow called, but is already in progress"); - } - } - } - - @Override - public void onActivityResult(int request, int result, Intent data) { - super.onActivityResult(request, result, data); - - switch (request) { - case REQUEST_SIGNUP: - disconnectGoogleClient(); - mSignupRequested = false; - if (result == RESULT_OK) { - GoogleSignInResult signInResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data); - - if (signInResult.isSuccess()) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - succcess"); - try { - GoogleSignInAccount account = signInResult.getSignInAccount(); - - if (account != null) { - mDisplayName = account.getDisplayName() != null ? account.getDisplayName() : ""; - mGoogleEmail = account.getEmail() != null ? account.getEmail() : ""; - mIdToken = account.getIdToken() != null ? account.getIdToken() : ""; - mPhotoUrl = removeScaleFromGooglePhotoUrl( - account.getPhotoUrl() != null ? account.getPhotoUrl().toString() : ""); - } - - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - dispatching SocialSignupAction"); - - dispatchSocialSignup(mIdToken); - } catch (NullPointerException exception) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - NPE"); - AppLog.e(T.NUX, "Cannot get ID token from Google signup account.", exception); - showError(getString(R.string.login_error_generic)); - } - } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - error"); - mAnalyticsListener.trackSignupSocialButtonFailure(); - switch (signInResult.getStatus().getStatusCode()) { - // Internal error. - case GoogleSignInStatusCodes.INTERNAL_ERROR: - AppLog.e(T.NUX, "Google Signup Failed: internal error."); - showError(getString(R.string.login_error_generic)); - break; - // Attempted to connect with an invalid account name specified. - case GoogleSignInStatusCodes.INVALID_ACCOUNT: - AppLog.e(T.NUX, "Google Signup Failed: invalid account name."); - showError(getString(R.string.login_error_generic) - + getString(R.string.login_error_suffix)); - break; - // Network error. - case GoogleSignInStatusCodes.NETWORK_ERROR: - AppLog.e(T.NUX, "Google Signup Failed: network error."); - showError(getString(R.string.error_generic_network)); - break; - // Cancelled by the user. - case GoogleSignInStatusCodes.SIGN_IN_CANCELLED: - AppLog.e(T.NUX, "Google Signup Failed: cancelled by user."); - break; - // Attempt didn't succeed with the current account. - case GoogleSignInStatusCodes.SIGN_IN_FAILED: - AppLog.e(T.NUX, "Google Signup Failed: current account failed."); - showError(getString(R.string.login_error_generic)); - break; - // Attempted to connect, but the user is not signed in. - case GoogleSignInStatusCodes.SIGN_IN_REQUIRED: - AppLog.e(T.NUX, "Google Signup Failed: user is not signed in."); - showError(getString(R.string.login_error_generic)); - break; - // Timeout error. - case GoogleSignInStatusCodes.TIMEOUT: - AppLog.e(T.NUX, "Google Signup Failed: timeout error."); - showError(getString(R.string.google_error_timeout)); - break; - // Unknown error. - default: - AppLog.e(T.NUX, "Google Signup Failed: unknown error."); - showError(getString(R.string.login_error_generic)); - break; - } - } - } else if (result == RESULT_CANCELED) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - canceled"); - mAnalyticsListener.trackSignupSocialButtonFailure(); - AppLog.e(T.NUX, "Google Signup Failed: result was CANCELED."); - finishFlow(); - } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: sign up result returned - unknown"); - mAnalyticsListener.trackSignupSocialButtonFailure(); - AppLog.e(T.NUX, "Google Signup Failed: result was not OK or CANCELED."); - showError(getString(R.string.login_error_generic)); - } - - break; - } - } - - private void dispatchSocialSignup(String idToken) { - PushSocialPayload payload = new PushSocialPayload(idToken, SERVICE_TYPE_GOOGLE); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialSignupAction(payload)); - mOldSitesIds = SiteUtils.getCurrentSiteIds(mSiteStore, false); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthenticationChanged(OnAuthenticationChanged event) { - if (event.isError()) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onAuthenticationChanged - error"); - AppLog.e(T.API, - "SignupGoogleFragment.onAuthenticationChanged: " + event.error.type + " - " + event.error.message); - } else if (event.createdAccount) { - AppLog.d(T.MAIN, - "GOOGLE SIGNUP: onAuthenticationChanged - new wordpress account created"); - mAnalyticsListener.trackCreatedAccount(event.userName, mGoogleEmail, GOOGLE); - mAnalyticsListener.trackAnalyticsSignIn(true); - mGoogleListener.onGoogleSignupFinished(mDisplayName, mGoogleEmail, mPhotoUrl, event.userName); - // Continue with login since existing account was selected. - } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onAuthenticationChanged - the email is already attached to an account"); - mAnalyticsListener.trackSignupSocialToLogin(); - mLoginListener.loggedInViaSocialAccount(mOldSitesIds, true); - } - finishFlow(); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSocialChanged(OnSocialChanged event) { - if (event.isError()) { - AppLog.e(T.API, "SignupGoogleFragment.onSocialChanged: " + event.error.type + " - " + event.error.message); - - switch (event.error.type) { - // WordPress account exists with input email address, and two-factor authentication is required. - case TWO_STEP_ENABLED: - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - error - two step authentication"); - mAnalyticsListener.trackSignupSocialToLogin(); - mLoginListener.showSignupToLoginMessage(); - // Dispatch social login action to retrieve data required for two-factor authentication. - PushSocialPayload payload = new PushSocialPayload(mIdToken, SERVICE_TYPE_GOOGLE); - AppLog.d(T.MAIN, - "GOOGLE SIGNUP: onSocialChanged error - two step authentication - dispatching " - + "pushSocialLoginAction"); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialLoginAction(payload)); - break; - // WordPress account exists with input email address, but not connected. - case USER_EXISTS: - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - error - user already exists"); - mAnalyticsListener.trackSignupSocialAccountsNeedConnecting(); - showError(getString(R.string.login_error_email_not_connected_to_google)); - break; - // Too many attempts on sending SMS verification code. The user has to wait before they try again - case SMS_CODE_THROTTLED: - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - error - sms code throttled"); - showError(getString(R.string.login_error_sms_throttled)); - break; - default: - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - error - unknown"); - showError(getString(R.string.login_error_generic)); - break; - } - // Response does not return error when two-factor authentication is required. - } else if (event.requiresTwoStepAuth || Login2FaFragment.TWO_FACTOR_TYPE_SMS.equals(event.notificationSent)) { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - 2fa required"); - mAnalyticsListener.trackSignupSocialToLogin(); - mLoginListener.needs2faSocial(mGoogleEmail, event.userId, event.nonceAuthenticator, event.nonceBackup, - event.nonceSms, event.nonceWebauthn, event.twoStepTypes); - finishFlow(); - } else { - AppLog.d(T.MAIN, "GOOGLE SIGNUP: onSocialChanged - account exists but not connected"); - mAnalyticsListener.trackSignupSocialAccountsNeedConnecting(); - showError(getString(R.string.login_error_email_not_connected_to_google)); - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index c72e57cc6627..be05467edaa6 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -2,10 +2,7 @@ import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginEmailFragment; -import org.wordpress.android.login.LoginGoogleFragment; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; -import org.wordpress.android.login.SignupConfirmationFragment; -import org.wordpress.android.login.SignupGoogleFragment; import org.wordpress.android.login.SignupMagicLinkFragment; import dagger.Module; @@ -19,18 +16,9 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract LoginEmailFragment loginEmailFragment(); - @ContributesAndroidInjector - abstract LoginGoogleFragment loginGoogleFragment(); - @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); - @ContributesAndroidInjector - abstract SignupGoogleFragment signupGoogleFragment(); - @ContributesAndroidInjector abstract SignupMagicLinkFragment signupMagicLinkFragment(); - - @ContributesAndroidInjector - abstract SignupConfirmationFragment signupConfirmationScreen(); } diff --git a/libs/login/src/main/res/layout/signup_confirmation_screen.xml b/libs/login/src/main/res/layout/signup_confirmation_screen.xml deleted file mode 100644 index 0ed00631d658..000000000000 --- a/libs/login/src/main/res/layout/signup_confirmation_screen.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - From 4f95ddeaf49fe6682648f56e9dc74cbf96dd2ff6 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:51:04 -0700 Subject: [PATCH 05/71] Remove LoginEmailFragment --- .../wordpress/android/e2e/flows/LoginFlow.kt | 9 +- .../android/ui/accounts/LoginActivity.java | 70 +- .../android/login/LoginEmailFragment.java | 653 ------------------ .../android/login/LoginListener.java | 11 +- .../android/login/di/LoginFragmentModule.java | 4 - ...login_email_optional_site_creds_screen.xml | 88 --- .../main/res/layout/login_email_screen.xml | 123 ---- 7 files changed, 6 insertions(+), 952 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java delete mode 100644 libs/login/src/main/res/layout/login_email_optional_site_creds_screen.xml delete mode 100644 libs/login/src/main/res/layout/login_email_screen.xml diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt index bb09f5481e2e..aeb324ba6029 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt @@ -16,18 +16,11 @@ import org.wordpress.android.login.R as LoginR class LoginFlow { fun chooseContinueWithWpCom(composeTestRule: ComposeTestRule?): LoginFlow { // Login Prologue – We want to Continue with WordPress.com, not a site address + // Note: WP.com login now uses web-based OAuth flow via Custom Tabs tapContinueWithWpCom(composeTestRule!!) return this } - fun enterEmailAddress(emailAddress: String?): LoginFlow { - // Email Address Screen – Fill it in and click "Continue" - // See LoginEmailFragment - WPSupportUtils.populateTextField(R.id.input, emailAddress) - WPSupportUtils.clickOn(LoginR.id.login_continue_button) - return this - } - fun enterUsernameAndPassword(username: String, password: String): LoginFlow { val usernameElement = Espresso.onView( CoreMatchers.allOf( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index f581faabe8af..027888a0a7db 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -38,10 +38,8 @@ import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload; import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; import org.wordpress.android.util.SiteUtils; -import org.wordpress.android.login.AuthOptions; import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginAnalyticsListener; -import org.wordpress.android.login.LoginEmailFragment; import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; @@ -376,11 +374,6 @@ private LoginPrologueRevampedFragment getLoginPrologueRevampedFragment() { return fragment == null ? null : (LoginPrologueRevampedFragment) fragment; } - private LoginEmailFragment getLoginEmailFragment() { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(LoginEmailFragment.TAG); - return fragment == null ? null : (LoginEmailFragment) fragment; - } - @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (item.getItemId() == android.R.id.home) { @@ -525,22 +518,11 @@ private void initSmartLockIfNotFinished(boolean triggerFillInOnConnect) { } private void startLogin() { - if (getLoginEmailFragment() != null) { - // email screen is already shown so, login has already started. Just bail. - return; - } - - if (getLoginPrologueRevampedFragment() == null) { - // prologue fragment is not shown so, the email screen will be the initial screen on the fragment container - showFragment(LoginEmailFragment.newInstance(mIsSignupFromLoginEnabled), LoginEmailFragment.TAG); - - if (getLoginMode() == LoginMode.JETPACK_STATS) { - mIsJetpackConnect = true; - } - } else { - // prologue fragment is shown so, slide in the email screen (and add to history) - slideInFragment(LoginEmailFragment.newInstance(mIsSignupFromLoginEnabled), true, LoginEmailFragment.TAG); + if (getLoginMode() == LoginMode.JETPACK_STATS) { + mIsJetpackConnect = true; } + // Use web-based WP.com login + showWPcomLoginScreen(this); } // LoginPrologueListener implementation methods @@ -571,26 +553,8 @@ public void showWPcomLoginScreen(@NonNull Context context) { .build(); } - @Override - public void onTermsOfServiceClicked() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_TERMS_OF_SERVICE_TAPPED); - mUnifiedLoginTracker.trackClick(Click.TERMS_OF_SERVICE_CLICKED); - ActivityLauncher.openUrlExternal(this, WPUrlUtils.buildTermsOfServiceUrl(this)); - } - // LoginListener implementation methods - @Override - public void gotWpcomEmail(String email, boolean verifyEmail, @Nullable AuthOptions authOptions) { - initSmartLockIfNotFinished(false); - showWPcomLoginScreen(this); - } - - @Override - public void gotUnregisteredEmail(String email) { - showSignupMagicLink(email); - } - @Override public void loginViaSiteAddress() { slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); @@ -648,12 +612,6 @@ public void alreadyLoggedInWpcom(ArrayList oldSitesIds) { loggedInAndFinish(oldSitesIds, false); } - @Override - public void gotWpcomSiteInfo(String siteAddress) { - LoginEmailFragment loginEmailFragment = LoginEmailFragment.newInstance(siteAddress); - slideInFragment(loginEmailFragment, true, LoginEmailFragment.TAG); - } - @Override public void handleSslCertificateError(MemorizingTrustManager memorizingTrustManager, final SelfSignedSSLCallback callback) { @@ -695,16 +653,6 @@ public void helpFindingSiteAddress(String username, SiteStore siteStore) { } } - @Override - public void helpEmailScreen(String email) { - viewHelp(Origin.LOGIN_EMAIL); - } - - @Override - public void helpSignupEmailScreen(String email) { - viewHelp(Origin.SIGNUP_EMAIL); - } - @Override public void helpSignupMagicLinkScreen(String email) { viewHelp(Origin.SIGNUP_MAGIC_LINK); @@ -802,11 +750,6 @@ public void onPositiveClicked(@NonNull String instanceTag) { // Not used in WordPress app } - @Override - public void showHelpFindingConnectedEmail() { - // Not used in WordPress app - } - @Override public void gotConnectedSiteInfo( @NonNull String siteAddress, @@ -815,11 +758,6 @@ public void gotConnectedSiteInfo( // Not used in WordPress app } - @Override - public void loginViaSiteCredentials(String inputSiteAddress) { - // Not used in WordPress app - } - @Override public void handleSiteAddressError(ConnectSiteInfoPayload siteInfo) { mViewModel.onHandleSiteAddressError(siteInfo); diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java deleted file mode 100644 index 4f034e6ed6e2..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginEmailFragment.java +++ /dev/null @@ -1,653 +0,0 @@ -package org.wordpress.android.login; - -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Bundle; -import android.text.Editable; -import android.text.Html; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Patterns; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.view.autofill.AutofillManager; -import android.widget.Button; -import android.widget.TextView; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.Toolbar; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.credentials.Credential; -import com.google.android.gms.auth.api.credentials.CredentialPickerConfig; -import com.google.android.gms.auth.api.credentials.HintRequest; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; -import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.generated.AccountActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore.FetchAuthOptionsPayload; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthOptionsFetched; -import org.wordpress.android.login.util.ContextExtensionsKt; -import org.wordpress.android.login.widgets.WPLoginInputRow; -import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener; -import org.wordpress.android.util.ActivityUtils; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.EditTextUtils; -import org.wordpress.android.util.HtmlUtils; -import org.wordpress.android.util.NetworkUtils; -import org.wordpress.android.util.ToastUtils; -import org.wordpress.android.util.ToastUtils.Duration; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import dagger.android.support.AndroidSupportInjection; - -import static android.app.Activity.RESULT_OK; - -public class LoginEmailFragment extends LoginBaseFormFragment implements TextWatcher, - OnEditorCommitListener, ConnectionCallbacks, OnConnectionFailedListener { - private static final String KEY_HAS_DISMISSED_EMAIL_HINTS = "KEY_HAS_DISMISSED_EMAIL_HINTS"; - private static final String KEY_IS_DISPLAYING_EMAIL_HINTS = "KEY_IS_DISPLAYING_EMAIL_HINTS"; - private static final String KEY_REQUESTED_EMAIL = "KEY_REQUESTED_EMAIL"; - private static final String KEY_EMAIL_ERROR_RES = "KEY_EMAIL_ERROR_RES"; - private static final String LOG_TAG = LoginEmailFragment.class.getSimpleName(); - private static final int GOOGLE_API_CLIENT_ID = 1002; - private static final int EMAIL_CREDENTIALS_REQUEST_CODE = 25100; - - private static final String ARG_LOGIN_SITE_URL = "ARG_LOGIN_SITE_URL"; - private static final String ARG_SIGNUP_FROM_LOGIN_ENABLED = "ARG_SIGNUP_FROM_LOGIN_ENABLED"; - private static final String ARG_OPTIONAL_SITE_CREDS_LAYOUT = "ARG_OPTIONAL_SITE_CREDS_LAYOUT"; - private static final String ARG_HIDE_TOS = "ARG_HIDE_TOS"; - - public static final String TAG = "login_email_fragment_tag"; - public static final String TAG_SITE_CREDS_LAYOUT = "login_email_fragment_site_creds_layout_tag"; - public static final int MAX_EMAIL_LENGTH = 100; - - private GoogleApiClient mGoogleApiClient; - private String mRequestedEmail; - private Integer mCurrentEmailErrorRes = null; - private boolean mIsSignupFromLoginEnabled; - private boolean mOptionalSiteCredsLayout; - private boolean mHideTos; - - protected WPLoginInputRow mEmailInput; - protected boolean mHasDismissedEmailHints; - protected boolean mIsDisplayingEmailHints; - protected String mLoginSiteUrl; - - public static LoginEmailFragment newInstance(String url) { - return newInstance(url, false, false, false); - } - - public static LoginEmailFragment newInstance(String url, boolean optionalSiteCredsLayout) { - return newInstance(url, optionalSiteCredsLayout, false, false); - } - - public static LoginEmailFragment newInstance(boolean isSignupFromLoginEnabled) { - return newInstance(null, false, isSignupFromLoginEnabled, false); - } - - public static LoginEmailFragment newInstance(boolean isSignupFromLoginEnabled, boolean hideTos) { - return newInstance(null, false, isSignupFromLoginEnabled, hideTos); - } - - private static LoginEmailFragment newInstance(String url, - boolean optionalSiteCredsLayout, - boolean isSignupFromLoginEnabled, - boolean hideTos) { - LoginEmailFragment fragment = new LoginEmailFragment(); - Bundle args = new Bundle(); - args.putString(ARG_LOGIN_SITE_URL, url); - args.putBoolean(ARG_OPTIONAL_SITE_CREDS_LAYOUT, optionalSiteCredsLayout); - args.putBoolean(ARG_SIGNUP_FROM_LOGIN_ENABLED, isSignupFromLoginEnabled); - args.putBoolean(ARG_HIDE_TOS, hideTos); - fragment.setArguments(args); - return fragment; - } - - @Override - protected @LayoutRes int getContentLayout() { - if (mOptionalSiteCredsLayout) { - return R.layout.login_email_optional_site_creds_screen; - } else { - return R.layout.login_email_screen; - } - } - - @Override - protected @LayoutRes int getProgressBarText() { - return R.string.checking_email; - } - - @Override - protected void setupLabel(@NonNull TextView label) { - switch (mLoginListener.getLoginMode()) { - case WPCOM_LOGIN_DEEPLINK: - label.setText(R.string.login_log_in_for_deeplink); - break; - case SHARE_INTENT: - label.setText(R.string.login_log_in_for_share_intent); - break; - case FULL: - case WPCOM_LOGIN_ONLY: - case JETPACK_LOGIN_ONLY: - case JETPACK_SELFHOSTED: - case SELFHOSTED_ONLY: - if (!TextUtils.isEmpty(mLoginSiteUrl)) { - label.setText(Html.fromHtml( - getString(R.string.enter_email_for_site, mLoginSiteUrl))); - } else { - label.setText(R.string.enter_email_to_continue_wordpress_com); - } - break; - case WOO_LOGIN_MODE: - if (mOptionalSiteCredsLayout) { - String siteAddressClean = mLoginSiteUrl.replaceFirst("^(http[s]?://)", ""); - label.setText(Html.fromHtml( - getString(R.string.enter_email_for_site, siteAddressClean))); - } else { - label.setText(getString(R.string.enter_email_wordpress_com)); - } - break; - case JETPACK_STATS: - label.setText(R.string.login_to_to_connect_jetpack); - break; - case WPCOM_REAUTHENTICATE: - label.setText(R.string.auth_required); - break; - } - } - - @Override - protected void setupContent(ViewGroup rootView) { - // important for accessibility - talkback - getActivity().setTitle(R.string.email_address_login_title); - - setupEmailInput((WPLoginInputRow) rootView.findViewById(R.id.login_email_row)); - - if (mOptionalSiteCredsLayout) { - setupContinueButton((Button) rootView.findViewById(R.id.login_continue_button)); - setupSiteCredsButton((Button) rootView.findViewById(R.id.login_site_creds)); - setupFindEmailHelpButton( - (Button) rootView.findViewById(R.id.login_find_connected_email)); - } else { - setupContinueButton((Button) rootView.findViewById(R.id.login_continue_button)); - setupTosButton((Button) rootView.findViewById(R.id.continue_tos)); - hideGoogleButton(rootView); - } - } - - private void setupEmailInput(WPLoginInputRow emailInput) { - mEmailInput = emailInput; - if (BuildConfig.DEBUG) { - mEmailInput.getEditText().setText(BuildConfig.DEBUG_WPCOM_LOGIN_EMAIL); - } - mEmailInput.post(new Runnable() { - @Override public void run() { - if (mEmailInput != null) { - mEmailInput.addTextChangedListener(LoginEmailFragment.this); - } - } - }); - - mEmailInput.setOnEditorCommitListener(this); - mEmailInput.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if (hasFocus && !mIsDisplayingEmailHints && !mHasDismissedEmailHints) { - mAnalyticsListener.trackSelectEmailField(); - showHintPickerDialogIfNeeded(); - } - } - }); - mEmailInput.getEditText().setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - mAnalyticsListener.trackSelectEmailField(); - if (!mIsDisplayingEmailHints && !mHasDismissedEmailHints) { - mAnalyticsListener.trackSelectEmailField(); - showHintPickerDialogIfNeeded(); - } - } - }); - } - - private void setupContinueButton(Button continueButton) { - continueButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - onContinueClicked(); - } - }); - } - - private void updateContinueButtonEnabledStatus() { - View view = getView(); - if (view != null) { - Button continueButton = (Button) view.findViewById(R.id.login_continue_button); - String currentEmail = mEmailInput.getEditText().getText().toString(); - continueButton.setEnabled(!currentEmail.trim().isEmpty()); - } - } - - @Override public void onDestroyView() { - mEmailInput = null; - - super.onDestroyView(); - } - - private void setupTosButton(Button continueTosButton) { - if (mHideTos) { - continueTosButton.setVisibility(View.GONE); - } else { - continueTosButton.setVisibility(View.VISIBLE); - continueTosButton.setOnClickListener(new OnClickListener() { - public void onClick(View view) { - mLoginListener.onTermsOfServiceClicked(); - } - }); - continueTosButton.setText(formatTosText(R.string.continue_terms_of_service_text)); - } - } - - private void hideGoogleButton(ViewGroup rootView) { - View googleTosButton = rootView.findViewById(R.id.continue_with_google_tos); - if (googleTosButton != null) { - googleTosButton.setVisibility(View.GONE); - } - View googleButton = rootView.findViewById(R.id.continue_with_google); - if (googleButton != null) { - googleButton.setVisibility(View.GONE); - } - } - - private void setupSiteCredsButton(Button continueWithSiteCreds) { - continueWithSiteCreds.setOnClickListener(new OnClickListener() { - @Override public void onClick(View v) { - mLoginListener.loginViaSiteCredentials(mLoginSiteUrl); - } - }); - } - - private void setupFindEmailHelpButton(Button findConnectedEmail) { - findConnectedEmail.setOnClickListener(new OnClickListener() { - @Override public void onClick(View v) { - mLoginListener.showHelpFindingConnectedEmail(); - } - }); - } - - @Override - protected void setupBottomButton(Button button) { - button.setVisibility(View.GONE); - } - - private Spanned formatTosText(int stringResId) { - final int primaryColorResId = ContextExtensionsKt.getColorResIdFromAttribute(getContext(), - com.google.android.material.R.attr.colorSecondary); - final String primaryColorHtml = HtmlUtils.colorResToHtmlColor(getContext(), primaryColorResId); - return Html.fromHtml(getString(stringResId, "", "")); - } - - private void onContinueClicked() { - next(getCleanedEmail()); - } - - @Override - protected void onHelp() { - if (mLoginListener != null) { - mLoginListener.helpEmailScreen(EditTextUtils.getText(mEmailInput.getEditText())); - } - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle args = getArguments(); - if (args != null) { - mLoginSiteUrl = args.getString(ARG_LOGIN_SITE_URL, ""); - mIsSignupFromLoginEnabled = args.getBoolean(ARG_SIGNUP_FROM_LOGIN_ENABLED, false); - mOptionalSiteCredsLayout = args.getBoolean(ARG_OPTIONAL_SITE_CREDS_LAYOUT, false); - mHideTos = args.getBoolean(ARG_HIDE_TOS, false); - } - } - - @Override - public void onStart() { - super.onStart(); - mGoogleApiClient = new GoogleApiClient.Builder(getActivity()) - .addConnectionCallbacks(LoginEmailFragment.this) - .enableAutoManage(getActivity(), GOOGLE_API_CLIENT_ID, LoginEmailFragment.this) - .addApi(Auth.CREDENTIALS_API) - .build(); - showEmailError(); - } - - @Override - public void onStop() { - super.onStop(); - if (mGoogleApiClient != null) { - mGoogleApiClient.stopAutoManage(getActivity()); - if (mGoogleApiClient.isConnected()) { - mGoogleApiClient.disconnect(); - } - } - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (savedInstanceState != null) { - mRequestedEmail = savedInstanceState.getString(KEY_REQUESTED_EMAIL); - mIsDisplayingEmailHints = savedInstanceState.getBoolean(KEY_IS_DISPLAYING_EMAIL_HINTS); - mHasDismissedEmailHints = savedInstanceState.getBoolean(KEY_HAS_DISMISSED_EMAIL_HINTS); - if (savedInstanceState.containsKey(KEY_EMAIL_ERROR_RES)) { - mCurrentEmailErrorRes = savedInstanceState.getInt(KEY_EMAIL_ERROR_RES); - } - } else { - mAnalyticsListener.trackEmailFormViewed(); - } - } - - @Override - public void onResume() { - super.onResume(); - mAnalyticsListener.emailFormScreenResumed(); - updateContinueButtonEnabledStatus(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(KEY_REQUESTED_EMAIL, mRequestedEmail); - outState.putBoolean(KEY_IS_DISPLAYING_EMAIL_HINTS, mIsDisplayingEmailHints); - outState.putBoolean(KEY_HAS_DISMISSED_EMAIL_HINTS, mHasDismissedEmailHints); - if (mCurrentEmailErrorRes != null) { - outState.putInt(KEY_EMAIL_ERROR_RES, mCurrentEmailErrorRes); - } - } - - @Override - protected void buildToolbar(Toolbar toolbar, ActionBar actionBar) { - if (mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE) { - actionBar.setTitle(R.string.log_in); - return; - } - - if (mOptionalSiteCredsLayout) { - super.buildToolbar(toolbar, actionBar); - } else { - actionBar.setTitle(R.string.get_started); - } - } - - protected void next(String email) { - mAnalyticsListener.trackSubmitClicked(); - if (!NetworkUtils.checkConnection(getActivity())) { - return; - } - - if (isValidEmail(email)) { - clearEmailError(); - startProgress(); - mRequestedEmail = email; - mDispatcher.dispatch(AccountActionBuilder.newFetchAuthOptionsAction(new FetchAuthOptionsPayload(email))); - } else { - showEmailError(R.string.email_invalid); - } - } - - /** - * This is cleared every time the text is changed or the email is valid so that if the user rotates the device, they - * don't receive an unnecessary warning from a previous error. - */ - private void clearEmailError() { - mCurrentEmailErrorRes = null; - } - - private void showEmailError() { - if (mCurrentEmailErrorRes != null) { - showEmailError(mCurrentEmailErrorRes); - } - } - - @Override - public void onDetach() { - super.onDetach(); - mLoginListener = null; - } - - private String getCleanedEmail() { - return EditTextUtils.getText(mEmailInput.getEditText()).trim(); - } - - private boolean isValidEmail(String email) { - Pattern emailRegExPattern = Patterns.EMAIL_ADDRESS; - Matcher matcher = emailRegExPattern.matcher(email); - - return matcher.find() && email.length() <= MAX_EMAIL_LENGTH; - } - - @Override - public void onEditorCommit() { - next(getCleanedEmail()); - } - - @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) { - mEmailInput.setError(null); - clearEmailError(); - updateContinueButtonEnabledStatus(); - } - - private void showEmailError(int messageId) { - mCurrentEmailErrorRes = messageId; - String errorMessage = getString(messageId); - mAnalyticsListener.trackFailure(errorMessage); - mEmailInput.setError(errorMessage); - } - - private void showErrorDialog(String message) { - AlertDialog dialog = new MaterialAlertDialogBuilder(getActivity()) - .setMessage(message) - .setPositiveButton(R.string.login_error_button, null) - .create(); - dialog.show(); - } - - @Override - protected void endProgress() { - super.endProgress(); - mRequestedEmail = null; - } - - // OnChanged events - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthOptionsFetched(OnAuthOptionsFetched event) { - if (mRequestedEmail == null) { - // bail if user canceled - return; - } - - final String email = mRequestedEmail; - - if (isInProgress()) { - endProgress(); - } - - // hide the keyboard - ActivityUtils.hideKeyboardForced(mEmailInput); - - if (event.isError()) { - // report the error but don't bail yet. - AppLog.e(T.API, "OnAuthOptionsFetched has error: " + event.error.type + " - " + event.error.message); - - switch (event.error.type) { - case UNKNOWN_USER: - // This email does not correspond to an existing account - - // Will be true if in the Woo app and currently in the WPcom login - // flow. We need to check this to know if we should display the - // 'No WPcom account found' error screen. - boolean isWooWPcomLoginFlow = false; - if (mLoginListener != null - && mLoginListener.getLoginMode() == LoginMode.WOO_LOGIN_MODE - && !mOptionalSiteCredsLayout) { - isWooWPcomLoginFlow = true; - } - - if (mIsSignupFromLoginEnabled || isWooWPcomLoginFlow) { - if (mLoginListener != null) { - mLoginListener.gotUnregisteredEmail(email); - } - } else { - mAnalyticsListener.trackFailure("Email not registered WP.com"); - showEmailError(R.string.email_not_registered_wpcom); - } - break; - case EMAIL_LOGIN_NOT_ALLOWED: - // As a security measure, this user needs to log in using username and password via web - mAnalyticsListener.trackFailure("Login with username required"); - ToastUtils.showToast(getContext(), R.string.error_user_username_instead_of_email, Duration.LONG); - if (mLoginListener != null) { - // Redirect to web-based login which supports username/password authentication - mLoginListener.gotWpcomEmail(email, false, null); - } - break; - case GENERIC_ERROR: - default: - showErrorDialog(getString(R.string.error_generic_network)); - } - } else { - if (mLoginListener != null) { - mLoginListener - .gotWpcomEmail(email, false, new AuthOptions(event.isPasswordless, event.isEmailVerified)); - } - } - } - - @Override - protected void onLoginFinished() { - // This method is called from doFinishLogin() but is no longer used - // since Google login has been removed - mAnalyticsListener.trackAnalyticsSignIn(true); - } - - @Override - public void onConnected(Bundle bundle) { - AppLog.d(T.NUX, LOG_TAG + ": Google API client connected"); - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - AppLog.d(T.NUX, LOG_TAG + ": Google API connection result: " + connectionResult); - } - - @Override - public void onConnectionSuspended(int i) { - AppLog.d(T.NUX, LOG_TAG + ": Google API client connection suspended"); - } - - private void showHintPickerDialogIfNeeded() { - // If autofill is available and enabled, we favor the active autofill service over the hint picker dialog. - final AutofillManager autofillManager = requireContext().getSystemService(AutofillManager.class); - if (autofillManager != null && autofillManager.isEnabled()) { - AppLog.d(T.NUX, LOG_TAG + ": Autofill framework is enabled. Disabling hint picker dialog."); - return; - } - - AppLog.d(T.NUX, LOG_TAG + ": Autofill framework is unavailable or disabled. Showing hint picker dialog."); - - showHintPickerDialog(); - } - - private void showHintPickerDialog() { - GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); - if (getContext() == null - || googleApiAvailability.isGooglePlayServicesAvailable(getContext()) != ConnectionResult.SUCCESS) { - AppLog.w(T.NUX, LOG_TAG + ": Couldn't start hint picker - Play Services unavailable"); - return; - } - HintRequest hintRequest = new HintRequest.Builder() - .setHintPickerConfig(new CredentialPickerConfig.Builder() - .setShowCancelButton(true) - .build()) - .setEmailAddressIdentifierSupported(true) - .build(); - - PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest); - - try { - startIntentSenderForResult(intent.getIntentSender(), EMAIL_CREDENTIALS_REQUEST_CODE, null, 0, 0, 0, null); - mIsDisplayingEmailHints = true; - } catch (IntentSender.SendIntentException exception) { - AppLog.d(T.NUX, LOG_TAG + "Could not start email hint picker" + exception); - } catch (ActivityNotFoundException exception) { - AppLog.d(T.NUX, LOG_TAG + "Could not find any activity to handle email hint picker" + exception); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == EMAIL_CREDENTIALS_REQUEST_CODE) { - if (mEmailInput == null) { - // Activity result received before the fragments onCreateView(), disregard result. - return; - } - - if (resultCode == RESULT_OK) { - Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); - mEmailInput.getEditText().setText(credential.getId()); - next(getCleanedEmail()); - } else { - mHasDismissedEmailHints = true; - mEmailInput.getEditText().postDelayed(new Runnable() { - @Override - public void run() { - if (isAdded()) { - ActivityUtils.showKeyboard(mEmailInput.getEditText()); - } - } - }, getResources().getInteger(android.R.integer.config_mediumAnimTime)); - } - - mIsDisplayingEmailHints = false; - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 1b9314f8a11e..1a79ad98ec91 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -18,14 +18,8 @@ interface SelfSignedSSLCallback { LoginMode getLoginMode(); void startOver(); - // Login Email input callbacks - void gotWpcomEmail(String email, boolean verifyEmail, @Nullable AuthOptions authOptions); - void gotUnregisteredEmail(String email); + // Login navigation void loginViaSiteAddress(); - void loginViaSiteCredentials(String inputSiteAddress); - void helpEmailScreen(String email); - void showHelpFindingConnectedEmail(); - void onTermsOfServiceClicked(); // Login Magic Link Sent callbacks (used by SignupMagicLinkFragment) void openEmailClient(boolean isLogin); @@ -39,7 +33,6 @@ void needs2fa(String email, String password, String userId, String webauthnNonce // Login Site Address input callbacks void alreadyLoggedInWpcom(ArrayList oldSitesIds); - void gotWpcomSiteInfo(String siteAddress); void gotConnectedSiteInfo(@NonNull String siteAddress, @Nullable String redirectUrl, boolean hasJetpack); void handleSslCertificateError(MemorizingTrustManager memorizingTrustManager, SelfSignedSSLCallback callback); void helpSiteAddress(String url); @@ -50,11 +43,9 @@ void needs2fa(String email, String password, String userId, String webauthnNonce void help2FaScreen(String email); // General post-login callbacks - // TODO This should have a more generic name, it more or less means any kind of login was finished void startPostLoginServices(); // Signup - void helpSignupEmailScreen(String email); void helpSignupMagicLinkScreen(String email); void showSignupMagicLink(String email); void showSignupToLoginMessage(); diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index be05467edaa6..b7d07b91eb7d 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -1,7 +1,6 @@ package org.wordpress.android.login.di; import org.wordpress.android.login.Login2FaFragment; -import org.wordpress.android.login.LoginEmailFragment; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; import org.wordpress.android.login.SignupMagicLinkFragment; @@ -13,9 +12,6 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract Login2FaFragment login2FaFragment(); - @ContributesAndroidInjector - abstract LoginEmailFragment loginEmailFragment(); - @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); diff --git a/libs/login/src/main/res/layout/login_email_optional_site_creds_screen.xml b/libs/login/src/main/res/layout/login_email_optional_site_creds_screen.xml deleted file mode 100644 index 9baf3b28ff43..000000000000 --- a/libs/login/src/main/res/layout/login_email_optional_site_creds_screen.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/layout/login_email_screen.xml b/libs/login/src/main/res/layout/login_email_screen.xml deleted file mode 100644 index d107a1486c9f..000000000000 --- a/libs/login/src/main/res/layout/login_email_screen.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From 94e1d90a510e06d90d7cb58fab68bd9455bcf15c Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 17:59:06 -0700 Subject: [PATCH 06/71] Remove Login 2FA fragment --- .../android/ui/accounts/LoginActivity.java | 26 - .../android/login/Login2FaFragment.java | 649 ------------------ .../android/login/LoginListener.java | 11 - .../android/login/di/LoginFragmentModule.java | 4 - .../src/main/res/layout/login_2fa_screen.xml | 42 -- 5 files changed, 732 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java delete mode 100644 libs/login/src/main/res/layout/login_2fa_screen.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 027888a0a7db..0de3bb71af2d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -38,7 +38,6 @@ import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload; import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; import org.wordpress.android.util.SiteUtils; -import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginAnalyticsListener; import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; @@ -586,26 +585,6 @@ public void openEmailClient(boolean isLogin) { } } - @Override - public void needs2fa(String email, String password) { - Login2FaFragment login2FaFragment = Login2FaFragment.newInstance(email, password); - slideInFragment(login2FaFragment, true, Login2FaFragment.TAG); - } - - @Override - public void needs2fa(String email, String password, String userId, String webauthnNonce, String nonceAuthenticator, - String nonceBackup, String noncePush, List supportedAuthTypes) { - mLoginAnalyticsListener.trackLogin2faNeeded(); - Login2FaFragment login2FaFragment = Login2FaFragment.newInstance(email, password, userId, webauthnNonce, - nonceAuthenticator, nonceBackup, noncePush, supportedAuthTypes); - slideInFragment(login2FaFragment, true, Login2FaFragment.TAG); - } - - @Override - public void loggedInViaPassword(ArrayList oldSitesIds) { - loggedInAndFinish(oldSitesIds, false); - } - @Override public void alreadyLoggedInWpcom(ArrayList oldSitesIds) { ToastUtils.showToast(this, R.string.already_logged_in_wpcom, ToastUtils.Duration.LONG); @@ -658,11 +637,6 @@ public void helpSignupMagicLinkScreen(String email) { viewHelp(Origin.SIGNUP_MAGIC_LINK); } - @Override - public void help2FaScreen(String email) { - viewHelp(Origin.LOGIN_2FA); - } - @Override public void startPostLoginServices() { // Get reader tags so they're available as soon as the Reader is accessed - done for diff --git a/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java b/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java deleted file mode 100644 index c811ae629ebe..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/Login2FaFragment.java +++ /dev/null @@ -1,649 +0,0 @@ -package org.wordpress.android.login; - -import android.content.ClipboardManager; -import android.content.Context; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.DigitsKeyListener; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; - -import androidx.annotation.LayoutRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.Toolbar; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.generated.AccountActionBuilder; -import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore.AuthenticateTwoFactorPayload; -import org.wordpress.android.fluxc.store.AccountStore.AuthenticationErrorType; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthenticationChanged; -import org.wordpress.android.fluxc.store.AccountStore.OnSocialChanged; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialAuthPayload; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialPayload; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialSmsPayload; -import org.wordpress.android.fluxc.store.AccountStore.StartWebauthnChallengePayload; -import org.wordpress.android.fluxc.store.AccountStore.WebauthnChallengeReceived; -import org.wordpress.android.fluxc.store.AccountStore.WebauthnPasskeyAuthenticated; -import org.wordpress.android.login.util.SiteUtils; -import org.wordpress.android.login.webauthn.PasskeyRequest; -import org.wordpress.android.login.webauthn.PasskeyRequest.PasskeyRequestData; -import org.wordpress.android.login.widgets.WPLoginInputRow; -import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.NetworkUtils; -import org.wordpress.android.util.ToastUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import dagger.android.support.AndroidSupportInjection; - -import static android.content.Context.CLIPBOARD_SERVICE; - -public class Login2FaFragment extends LoginBaseFormFragment implements TextWatcher, - OnEditorCommitListener { - private static final String KEY_2FA_TYPE = "KEY_2FA_TYPE"; - private static final String KEY_IN_PROGRESS_MESSAGE_ID = "KEY_IN_PROGRESS_MESSAGE_ID"; - private static final String KEY_NONCE_AUTHENTICATOR = "KEY_NONCE_AUTHENTICATOR"; - private static final String KEY_NONCE_BACKUP = "KEY_NONCE_BACKUP"; - private static final String KEY_NONCE_SMS = "KEY_NONCE_SMS"; - private static final String KEY_OLD_SITES_IDS = "KEY_OLD_SITES_IDS"; - private static final String KEY_SMS_NUMBER = "KEY_SMS_NUMBER"; - private static final String KEY_SMS_SENT = "KEY_SMS_SENT"; - - private static final String ARG_2FA_ID_TOKEN = "ARG_2FA_ID_TOKEN"; - private static final String ARG_2FA_IS_SOCIAL = "ARG_2FA_IS_SOCIAL"; - private static final String ARG_2FA_IS_SOCIAL_CONNECT = "ARG_2FA_IS_SOCIAL_CONNECT"; - private static final String ARG_2FA_NONCE_AUTHENTICATOR = "ARG_2FA_NONCE_AUTHENTICATOR"; - private static final String ARG_2FA_NONCE_BACKUP = "ARG_2FA_NONCE_BACKUP"; - private static final String ARG_2FA_NONCE_SMS = "ARG_2FA_NONCE_SMS"; - private static final String ARG_2FA_SOCIAL_SERVICE = "ARG_2FA_SOCIAL_SERVICE"; - private static final String ARG_2FA_USER_ID = "ARG_2FA_USER_ID"; - private static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - private static final String ARG_PASSWORD = "ARG_PASSWORD"; - private static final String ARG_WEBAUTHN_NONCE = "WEBAUTHN_NONCE"; - private static final String ARG_2FA_SUPPORTED_AUTH_TYPES = "ARG_2FA_SUPPORTED_AUTH_TYPES"; - private static final int LENGTH_NONCE_AUTHENTICATOR = 6; - private static final int LENGTH_NONCE_BACKUP = 8; - private static final int LENGTH_NONCE_SMS = 7; - - private static final String TWO_FACTOR_TYPE_AUTHENTICATOR = "authenticator"; - private static final String TWO_FACTOR_TYPE_BACKUP = "backup"; - - public static final String TWO_FACTOR_TYPE_SMS = "sms"; - public static final String TAG = "login_2fa_fragment_tag"; - - private static final Pattern TWO_STEP_AUTH_CODE = Pattern.compile("^[0-9]{6}"); - - private WPLoginInputRow m2FaInput; - - private static final @StringRes int DEFAULT_PROGRESS_MESSAGE_ID = R.string.logging_in; - private @StringRes int mInProgressMessageId = DEFAULT_PROGRESS_MESSAGE_ID; - - ArrayList mOldSitesIDs; - - private Button mOtpButton; - private Button mSecurityKeyButton; - private String mEmailAddress; - private String mIdToken; - private String mNonce; - private String mNonceAuthenticator; - private String mWebauthnNonce; - private String mNonceBackup; - private String mNonceSms; - private String mPassword; - private String mPhoneNumber; - private String mService; - private String mType; - private String mUserId; - private TextView mLabel; - private boolean mIsSocialLogin; - private boolean mIsSocialLoginConnect; - private boolean mSentSmsCode; - private List mSupportedAuthTypes; - - public static Login2FaFragment newInstance(String emailAddress, String password) { - Login2FaFragment fragment = new Login2FaFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, emailAddress); - args.putString(ARG_PASSWORD, password); - fragment.setArguments(args); - return fragment; - } - - public static Login2FaFragment newInstance(String emailAddress, String password, - String userId, String webauthnNonce, - String authenticatorNonce, String backupNonce, - String smsNonce, List authTypes) { - Login2FaFragment fragment = new Login2FaFragment(); - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, emailAddress); - args.putString(ARG_PASSWORD, password); - args.putString(ARG_2FA_USER_ID, userId); - args.putString(ARG_WEBAUTHN_NONCE, webauthnNonce); - args.putString(ARG_2FA_NONCE_AUTHENTICATOR, authenticatorNonce); - args.putString(ARG_2FA_NONCE_BACKUP, backupNonce); - args.putString(ARG_2FA_NONCE_SMS, smsNonce); - args.putStringArrayList(ARG_2FA_SUPPORTED_AUTH_TYPES, new ArrayList<>(authTypes)); - fragment.setArguments(args); - return fragment; - } - - @Override - protected @LayoutRes int getContentLayout() { - return R.layout.login_2fa_screen; - } - - @Override - protected @LayoutRes int getProgressBarText() { - return mInProgressMessageId; - } - - @Override - protected void setupLabel(@NonNull TextView label) { - label.setText(mSentSmsCode ? getString(R.string.enter_verification_code_sms, mPhoneNumber) - : getString(R.string.enter_verification_code)); - mLabel = label; - } - - @Override - protected void setupContent(ViewGroup rootView) { - // important for accessibility - talkback - getActivity().setTitle(R.string.verification_2fa_screen_title); - m2FaInput = rootView.findViewById(R.id.login_2fa_row); - m2FaInput.addTextChangedListener(this); - m2FaInput.setOnEditorCommitListener(this); - - // restrict the allowed input chars to just numbers - m2FaInput.getEditText().setKeyListener(DigitsKeyListener.getInstance("0123456789")); - - // If we didn't get a list of supported auth types, then the flow is not using webauthn, - // We should treat it as if SMS is enabled for the user - boolean isSmsEnabled = mSupportedAuthTypes.isEmpty() || mSupportedAuthTypes.contains(SupportedAuthTypes.PUSH); - mOtpButton = rootView.findViewById(R.id.login_otp_button); - mOtpButton.setVisibility(isSmsEnabled ? View.VISIBLE : View.GONE); - mOtpButton.setText(mSentSmsCode ? R.string.login_text_otp_another : R.string.login_text_otp); - mOtpButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (isAdded()) { - mAnalyticsListener.trackSendCodeWithTextClicked(); - doAuthAction(R.string.requesting_otp, "", true); - } - } - }); - - boolean isSecurityKeyEnabled = mSupportedAuthTypes.contains(SupportedAuthTypes.WEBAUTHN); - mSecurityKeyButton = rootView.findViewById(R.id.login_security_key_button); - mSecurityKeyButton.setVisibility(isSecurityKeyEnabled ? View.VISIBLE : View.GONE); - mSecurityKeyButton.setOnClickListener(view -> doAuthWithSecurityKeyAction()); - } - - @Override - protected void setupBottomButton(Button button) { - button.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - next(); - } - }); - } - - @Override - protected void buildToolbar(Toolbar toolbar, ActionBar actionBar) { - actionBar.setTitle(R.string.log_in); - } - - @Override - protected EditText getEditTextToFocusOnStart() { - return m2FaInput.getEditText(); - } - - @Override - protected void onHelp() { - if (mLoginListener != null) { - mLoginListener.help2FaScreen(mEmailAddress); - } - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mEmailAddress = getArguments().getString(ARG_EMAIL_ADDRESS); - mPassword = getArguments().getString(ARG_PASSWORD); - mNonceAuthenticator = getArguments().getString(ARG_2FA_NONCE_AUTHENTICATOR); - mNonceBackup = getArguments().getString(ARG_2FA_NONCE_BACKUP); - mNonceSms = getArguments().getString(ARG_2FA_NONCE_SMS); - mUserId = getArguments().getString(ARG_2FA_USER_ID); - mIdToken = getArguments().getString(ARG_2FA_ID_TOKEN); - mIsSocialLogin = getArguments().getBoolean(ARG_2FA_IS_SOCIAL); - mIsSocialLoginConnect = getArguments().getBoolean(ARG_2FA_IS_SOCIAL_CONNECT); - mService = getArguments().getString(ARG_2FA_SOCIAL_SERVICE); - mWebauthnNonce = getArguments().getString(ARG_WEBAUTHN_NONCE); - mSupportedAuthTypes = handleSupportedAuthTypesParameter( - getArguments().getStringArrayList(ARG_2FA_SUPPORTED_AUTH_TYPES)); - - if (savedInstanceState != null) { - // Overwrite argument nonce values with saved state values on device rotation. - mNonceAuthenticator = savedInstanceState.getString(KEY_NONCE_AUTHENTICATOR); - mNonceBackup = savedInstanceState.getString(KEY_NONCE_BACKUP); - mNonceSms = savedInstanceState.getString(KEY_NONCE_SMS); - // Restore set two-factor authentication type value on device rotation. - mType = savedInstanceState.getString(KEY_2FA_TYPE); - mPhoneNumber = savedInstanceState.getString(KEY_SMS_NUMBER); - mSentSmsCode = savedInstanceState.getBoolean(KEY_SMS_SENT); - } - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - // retrieve mInProgressMessageId before super.onActivityCreated() so the string will be available to the - // progress bar helper if in progress - if (savedInstanceState != null) { - mInProgressMessageId = savedInstanceState.getInt(KEY_IN_PROGRESS_MESSAGE_ID, DEFAULT_PROGRESS_MESSAGE_ID); - mOldSitesIDs = savedInstanceState.getIntegerArrayList(KEY_OLD_SITES_IDS); - } else { - mAnalyticsListener.trackTwoFactorFormViewed(); - } - super.onActivityCreated(savedInstanceState); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putInt(KEY_IN_PROGRESS_MESSAGE_ID, mInProgressMessageId); - outState.putIntegerArrayList(KEY_OLD_SITES_IDS, mOldSitesIDs); - outState.putString(KEY_NONCE_AUTHENTICATOR, mNonceAuthenticator); - outState.putString(KEY_NONCE_BACKUP, mNonceBackup); - outState.putString(KEY_NONCE_SMS, mNonceSms); - outState.putString(KEY_2FA_TYPE, mType); - outState.putString(KEY_SMS_NUMBER, mPhoneNumber); - outState.putBoolean(KEY_SMS_SENT, mSentSmsCode); - } - - @Override - public void onResume() { - super.onResume(); - - // Insert authentication code if copied to clipboard - if (TextUtils.isEmpty(m2FaInput.getEditText().getText())) { - m2FaInput.setText(getAuthCodeFromClipboard()); - } - - updateContinueButtonEnabledStatus(); - } - - protected void next() { - mAnalyticsListener.trackSubmit2faCodeClicked(); - if (TextUtils.isEmpty(m2FaInput.getEditText().getText())) { - show2FaError(getString(R.string.login_empty_2fa)); - return; - } - - doAuthAction(R.string.logging_in, m2FaInput.getEditText().getText().toString(), false); - } - - private void doAuthAction(@StringRes int messageId, String twoStepCode, boolean shouldSendTwoStepSMS) { - if (!NetworkUtils.checkConnection(getActivity())) { - return; - } - - mInProgressMessageId = messageId; - startProgress(); - - mOldSitesIDs = SiteUtils.getCurrentSiteIds(mSiteStore, false); - - if (mIsSocialLogin) { - if (shouldSendTwoStepSMS) { - PushSocialSmsPayload payload = new PushSocialSmsPayload(mUserId, mNonceSms); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialSmsAction(payload)); - } else { - setAuthCodeTypeAndNonce(twoStepCode); - PushSocialAuthPayload payload = new PushSocialAuthPayload(mUserId, mType, mNonce, - twoStepCode); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialAuthAction(payload)); - } - } else { - AuthenticateTwoFactorPayload payload = new AuthenticateTwoFactorPayload(mEmailAddress, - mPassword, twoStepCode, shouldSendTwoStepSMS); - mDispatcher.dispatch(AuthenticationActionBuilder - .newAuthenticateTwoFactorAction(payload)); - } - } - - 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(); - - final Matcher twoStepAuthCodeMatcher = TWO_STEP_AUTH_CODE.matcher(""); - twoStepAuthCodeMatcher.reset(code); - - if (!code.isEmpty() && twoStepAuthCodeMatcher.matches()) { - return code; - } - } - - return ""; - } - - private void setAuthCodeTypeAndNonce(String twoStepCode) { - switch (twoStepCode.length()) { - case LENGTH_NONCE_AUTHENTICATOR: - mType = TWO_FACTOR_TYPE_AUTHENTICATOR; - mNonce = mNonceAuthenticator; - break; - case LENGTH_NONCE_BACKUP: - mType = TWO_FACTOR_TYPE_BACKUP; - mNonce = mNonceBackup; - break; - case LENGTH_NONCE_SMS: - mType = TWO_FACTOR_TYPE_SMS; - mNonce = mNonceSms; - break; - } - } - - @Override - public void onEditorCommit() { - next(); - } - - @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) { - show2FaError(null); - updateContinueButtonEnabledStatus(); - } - - private void show2FaError(@Nullable String message) { - if (!TextUtils.isEmpty(message)) { - mAnalyticsListener.trackFailure(message); - } - m2FaInput.setError(message); - } - - private void updateContinueButtonEnabledStatus() { - String currentVerificationCode = m2FaInput.getEditText().getText().toString(); - getBottomButton().setEnabled(!currentVerificationCode.trim().isEmpty()); - } - - @Override - protected void endProgress() { - super.endProgress(); - mInProgressMessageId = DEFAULT_PROGRESS_MESSAGE_ID; - } - - private void handleAuthError(AuthenticationErrorType error, String errorMessage) { - switch (error) { - case INVALID_OTP: - show2FaError(getString(R.string.invalid_verification_code)); - break; - case NEEDS_2FA: - // we get this error when requesting a verification code sent via SMS so, just ignore it. - break; - case INVALID_REQUEST: - // TODO: FluxC: could be specific? - case WEBAUTHN_FAILED: - mAnalyticsListener.trackLoginSecurityKeyFailure(); - ToastUtils.showToast(getActivity(), - errorMessage == null ? getString(R.string.error_generic) : errorMessage); - break; - default: - AppLog.e(T.NUX, "Server response: " + errorMessage); - mAnalyticsListener.trackFailure(errorMessage); - ToastUtils.showToast(getActivity(), - errorMessage == null ? getString(R.string.error_generic) : errorMessage); - break; - } - } - - private void showErrorDialog(String message) { - mAnalyticsListener.trackFailure(message); - AlertDialog dialog = new MaterialAlertDialogBuilder(getActivity()) - .setMessage(message) - .setPositiveButton(R.string.login_error_button, null) - .create(); - dialog.show(); - } - - // OnChanged events - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthenticationChanged(OnAuthenticationChanged event) { - if (event.isError()) { - endProgress(); - - AppLog.e(T.API, "onAuthenticationChanged has error: " + event.error.type + " - " + event.error.message); - mAnalyticsListener.trackLoginFailed(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - - if (mIsSocialLogin) { - mAnalyticsListener.trackSocialFailure(event.getClass().getSimpleName(), - event.error.type.toString(), event.error.message); - } - - if (isAdded()) { - handleAuthError(event.error.type, event.error.message); - } - - return; - } - - AppLog.i(T.NUX, "onAuthenticationChanged: " + event.toString()); - - if (mIsSocialLoginConnect) { - PushSocialPayload payload = new PushSocialPayload(mIdToken, mService); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialConnectAction(payload)); - } else { - doFinishLogin(); - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSocialChanged(OnSocialChanged event) { - if (event.isError()) { - switch (event.error.type) { - // Two-factor authentication code was incorrect; save new nonce for another try. - case INVALID_TWO_STEP_CODE: - endProgress(); - - switch (mType) { - case TWO_FACTOR_TYPE_AUTHENTICATOR: - mNonceAuthenticator = event.error.nonce; - break; - case TWO_FACTOR_TYPE_BACKUP: - mNonceBackup = event.error.nonce; - break; - case TWO_FACTOR_TYPE_SMS: - mNonceSms = event.error.nonce; - break; - } - - show2FaError(getString(R.string.invalid_verification_code)); - break; - // Two-factor authentication via SMS failed; show message, log error, - // and replace SMS nonce with response. - case INVALID_TWO_STEP_NONCE: - case NO_PHONE_NUMBER_FOR_ACCOUNT: - case SMS_AUTHENTICATION_UNAVAILABLE: - case SMS_CODE_THROTTLED: - endProgress(); - showErrorDialog(event.error.message); - AppLog.e(T.API, event.error.type + ": " + event.error.message); - mNonceSms = event.error.nonce; - break; - case UNABLE_CONNECT: - AppLog.e(T.API, "Unable to connect WordPress.com account to social account."); - break; - case USER_ALREADY_ASSOCIATED: - AppLog.e(T.API, "This social account is already associated with a WordPress.com account."); - break; - } - - // Finish login on social connect error. - if (mIsSocialLoginConnect) { - mAnalyticsListener.trackSocialConnectFailure(); - doFinishLogin(); - } - // Two-factor authentication code was sent via SMS to account phone number; replace SMS nonce with response. - } else if (!TextUtils.isEmpty(event.phoneNumber) && !TextUtils.isEmpty(event.nonce)) { - endProgress(); - mPhoneNumber = event.phoneNumber; - mNonceSms = event.nonce; - setTextForSms(); - } else { - if (mIsSocialLoginConnect) { - mAnalyticsListener.trackSocialConnectSuccess(); - } - doFinishLogin(); - } - } - - @Override - protected void onLoginFinished() { - mAnalyticsListener.trackAnalyticsSignIn(true); - - mLoginListener.startPostLoginServices(); - - mLoginListener.loggedInViaPassword(mOldSitesIDs); - } - - private void setTextForSms() { - mLabel.setText(getString(R.string.enter_verification_code_sms, mPhoneNumber)); - mOtpButton.setText(getString(R.string.login_text_otp_another)); - mSentSmsCode = true; - } - - private void doAuthWithSecurityKeyAction() { - mAnalyticsListener.trackUseSecurityKeyClicked(); - if (!NetworkUtils.checkConnection(getActivity())) { - return; - } - - startProgress(); - mOldSitesIDs = SiteUtils.getCurrentSiteIds(mSiteStore, false); - - StartWebauthnChallengePayload payload = new StartWebauthnChallengePayload( - mUserId, mWebauthnNonce); - mDispatcher.dispatch(AuthenticationActionBuilder - .newStartSecurityKeyChallengeAction(payload)); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onWebauthnChallengeReceived(WebauthnChallengeReceived event) { - if (event.isError()) { - handleWebauthnError(event.error.type, getString(R.string.login_error_security_key)); - return; - } - - PasskeyRequestData passkeyRequestData = new PasskeyRequestData( - event.mUserId, - event.getWebauthnNonce(), - event.mJsonResponse.toString() - ); - - PasskeyRequest.create( - requireContext(), - passkeyRequestData, - result -> { - mDispatcher.dispatch(result); - return null; - }, - error -> { - String errorMessage = getString(R.string.login_error_security_key); - handleWebauthnError(AuthenticationErrorType.WEBAUTHN_FAILED, errorMessage); - return null; - } - ); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSecurityKeyCheckFinished(WebauthnPasskeyAuthenticated event) { - if (event.isError()) { - handleWebauthnError(event.error.type, getString(R.string.login_error_security_key)); - return; - } - mAnalyticsListener.trackLoginSecurityKeySuccess(); - doFinishLogin(); - } - - private void handleWebauthnError(AuthenticationErrorType errorType, String errorMessage) { - endProgress(); - handleAuthError(errorType, errorMessage); - getParentFragmentManager().popBackStack(); - } - - @NonNull private ArrayList handleSupportedAuthTypesParameter( - ArrayList supportedTypes) { - ArrayList supportedAuthTypes = new ArrayList<>(); - if (supportedTypes != null) { - for (String type : supportedTypes) { - SupportedAuthTypes parsedType = SupportedAuthTypes.fromString(type); - if (parsedType != SupportedAuthTypes.UNKNOWN) { - supportedAuthTypes.add(parsedType); - } - } - } - return supportedAuthTypes; - } - - public enum SupportedAuthTypes { - WEBAUTHN, - BACKUP, - AUTHENTICATOR, - PUSH, - UNKNOWN; - - static SupportedAuthTypes fromString(String value) { - switch (value) { - case "webauthn": - return WEBAUTHN; - case "backup": - return BACKUP; - case "authenticator": - return AUTHENTICATOR; - case "push": - return PUSH; - default: - return UNKNOWN; - } - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 1a79ad98ec91..7e1346ef76ef 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -8,7 +8,6 @@ import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload; import java.util.ArrayList; -import java.util.List; public interface LoginListener { interface SelfSignedSSLCallback { @@ -24,13 +23,6 @@ interface SelfSignedSSLCallback { // Login Magic Link Sent callbacks (used by SignupMagicLinkFragment) void openEmailClient(boolean isLogin); - // Login 2FA callbacks - void needs2fa(String email, String password); - void needs2fa(String email, String password, String userId, String webauthnNonce, - String nonceAuthenticator, String nonceBackup, String noncePush, - List supportedAuthTypes); - void loggedInViaPassword(ArrayList oldSitesIds); - // Login Site Address input callbacks void alreadyLoggedInWpcom(ArrayList oldSitesIds); void gotConnectedSiteInfo(@NonNull String siteAddress, @Nullable String redirectUrl, boolean hasJetpack); @@ -39,9 +31,6 @@ void needs2fa(String email, String password, String userId, String webauthnNonce void helpFindingSiteAddress(String username, SiteStore siteStore); void handleSiteAddressError(ConnectSiteInfoPayload siteInfo); - // Login 2FA screen callbacks - void help2FaScreen(String email); - // General post-login callbacks void startPostLoginServices(); diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index b7d07b91eb7d..ba4a0da66157 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -1,6 +1,5 @@ package org.wordpress.android.login.di; -import org.wordpress.android.login.Login2FaFragment; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; import org.wordpress.android.login.SignupMagicLinkFragment; @@ -9,9 +8,6 @@ @Module public abstract class LoginFragmentModule { - @ContributesAndroidInjector - abstract Login2FaFragment login2FaFragment(); - @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); diff --git a/libs/login/src/main/res/layout/login_2fa_screen.xml b/libs/login/src/main/res/layout/login_2fa_screen.xml deleted file mode 100644 index cae797c60230..000000000000 --- a/libs/login/src/main/res/layout/login_2fa_screen.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - From 684e5d3b64b6821c178f84bfa2444fd5c7c5a9a6 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:05:54 -0700 Subject: [PATCH 07/71] Remove SignupMagicLinkFragment --- .../org/wordpress/android/e2e/SignUpTests.kt | 37 --- .../wordpress/android/e2e/flows/SignupFlow.kt | 102 ------ .../android/ui/accounts/LoginActivity.java | 33 -- .../android/login/LoginListener.java | 5 - .../login/SignupMagicLinkFragment.java | 306 ------------------ .../android/login/di/LoginFragmentModule.java | 4 - .../res/layout/signup_magic_link_screen.xml | 56 ---- 7 files changed, 543 deletions(-) delete mode 100644 WordPress/src/androidTest/java/org/wordpress/android/e2e/SignUpTests.kt delete mode 100644 WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/SignupFlow.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/SignupMagicLinkFragment.java delete mode 100644 libs/login/src/main/res/layout/signup_magic_link_screen.xml diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/SignUpTests.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/SignUpTests.kt deleted file mode 100644 index b97fca579f46..000000000000 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/SignUpTests.kt +++ /dev/null @@ -1,37 +0,0 @@ -package org.wordpress.android.e2e - -import dagger.hilt.android.testing.HiltAndroidTest -import org.junit.Before -import org.junit.Test -import org.wordpress.android.e2e.flows.SignupFlow -import org.wordpress.android.support.BaseTest -import org.wordpress.android.support.ComposeEspressoLink -import org.wordpress.android.support.E2ECredentials - -@HiltAndroidTest -class SignUpTests : BaseTest() { - @Before - fun setUp() { - ComposeEspressoLink().unregister() - logoutIfNecessary() - } - - @Test - fun e2eSignUpWithMagicLink() { - try { - SignupFlow().chooseContinueWithWpCom(super.mComposeTestRule) - .enterEmail(E2ECredentials.SIGNUP_EMAIL) - .openMagicLink() - .checkEpilogue( - E2ECredentials.SIGNUP_DISPLAY_NAME, - E2ECredentials.SIGNUP_USERNAME - ) - .enterPassword(E2ECredentials.SIGNUP_PASSWORD) - .dismissInterstitial() - .dismissJetpackAd() - .confirmSignup() - } finally { - logoutIfNecessary() - } - } -} diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/SignupFlow.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/SignupFlow.kt deleted file mode 100644 index f98217559cb0..000000000000 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/SignupFlow.kt +++ /dev/null @@ -1,102 +0,0 @@ -package org.wordpress.android.e2e.flows - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri -import androidx.compose.ui.test.junit4.ComposeTestRule -import androidx.test.core.app.ActivityScenario -import androidx.test.core.app.ApplicationProvider -import androidx.test.espresso.Espresso -import androidx.test.espresso.matcher.ViewMatchers -import org.hamcrest.Matchers -import org.wordpress.android.BuildConfig -import org.wordpress.android.R -import org.wordpress.android.e2e.pages.LandingPage.tapContinueWithWpCom -import org.wordpress.android.support.WPSupportUtils -import org.wordpress.android.login.R as LoginR - -class SignupFlow { - fun chooseContinueWithWpCom(composeTestRule: ComposeTestRule?): SignupFlow { - // Login Prologue – We want to Continue with WordPress.com, not a site address - tapContinueWithWpCom(composeTestRule!!) - return this - } - - fun enterEmail(email: String?): SignupFlow { - // Email file = id/input - WPSupportUtils.populateTextField(Espresso.onView(ViewMatchers.withId(R.id.input)), email) - WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withId(LoginR.id.login_continue_button))) - return this - } - - fun openMagicLink(): SignupFlow { - // Should see "Check email" button - // See SignupMagicLinkFragment - WPSupportUtils.waitForElementToBeDisplayed(LoginR.id.signup_magic_link_button) - - // Follow the magic link to continue login - // Intent is invoked directly rather than through a browser as WireMock is unavailable once in the background - val appVariant = BuildConfig.FLAVOR_app // Either "wordpress" or "jetpack" - val intent = Intent( - Intent.ACTION_VIEW, - Uri.parse("$appVariant://magic-login?token=valid_token&new_user=1") - ).setPackage(ApplicationProvider.getApplicationContext().packageName) - ActivityScenario.launch(intent) - return this - } - - fun checkEpilogue(displayName: String?, username: String?): SignupFlow { - // Check Epilogue data - val emailHeaderView = - Espresso.onView(ViewMatchers.withId(R.id.login_epilogue_header_subtitle)) - WPSupportUtils.waitForElementToBeDisplayed(emailHeaderView) - val displayNameField = Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.input), - ViewMatchers.withText(displayName) - ) - ) - val usernameField = Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.input), - ViewMatchers.withText(username) - ) - ) - WPSupportUtils.waitForElementToBeDisplayed(displayNameField) - WPSupportUtils.waitForElementToBeDisplayed(usernameField) - return this - } - - fun enterPassword(password: String?): SignupFlow { - // Enter Password - val passwordField = Espresso.onView( - Matchers.allOf( - ViewMatchers.withId(R.id.input), - ViewMatchers.withHint("Password (optional)") - ) - ) - WPSupportUtils.waitForElementToBeDisplayed(passwordField) - WPSupportUtils.populateTextField(passwordField, password) - - // Click continue - WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withId(R.id.bottom_button))) - return this - } - - fun dismissInterstitial(): SignupFlow { - // Dismiss post-signup interstitial - WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withId(R.id.dismiss_button))) - return this - } - - fun dismissJetpackAd(): SignupFlow { - WPSupportUtils.dismissJetpackAdIfPresent() - return this - } - - fun confirmSignup() { - // Confirm signup - WPSupportUtils.waitForElementToBeDisplayed(R.id.nav_sites) - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 0de3bb71af2d..cd1d58906fa1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -32,7 +32,6 @@ import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.network.MemorizingTrustManager; import org.wordpress.android.fluxc.store.AccountStore; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme; import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged; import org.wordpress.android.fluxc.store.SiteStore; import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload; @@ -42,7 +41,6 @@ import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; -import org.wordpress.android.login.SignupMagicLinkFragment; import org.wordpress.android.support.SupportWebViewActivity; import org.wordpress.android.support.ZendeskExtraTags; import org.wordpress.android.support.ZendeskHelper; @@ -559,32 +557,6 @@ public void loginViaSiteAddress() { slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); } - @Override - public void showSignupMagicLink(String email) { - boolean isEmailClientAvailable = WPActivityUtils.isEmailClientAvailable(this); - AuthEmailPayloadScheme scheme = mViewModel.getMagicLinkScheme(); - SignupMagicLinkFragment signupMagicLinkFragment = SignupMagicLinkFragment.newInstance(email, mIsJetpackConnect, - mJetpackConnectSource != null ? mJetpackConnectSource.toString() : null, isEmailClientAvailable, - scheme); - slideInFragment(signupMagicLinkFragment, true, SignupMagicLinkFragment.TAG); - } - - @Override - public void openEmailClient(boolean isLogin) { - mUnifiedLoginTracker.trackClick(Click.OPEN_EMAIL_CLIENT); - if (WPActivityUtils.isEmailClientAvailable(this)) { - if (isLogin) { - mLoginAnalyticsListener.trackLoginMagicLinkOpenEmailClientClicked(); - } else { - mLoginAnalyticsListener.trackSignupMagicLinkOpenEmailClientClicked(); - } - - WPActivityUtils.openEmailClientChooser(this, getString(R.string.login_select_email_client)); - } else { - ToastUtils.showToast(this, R.string.login_email_client_not_found); - } - } - @Override public void alreadyLoggedInWpcom(ArrayList oldSitesIds) { ToastUtils.showToast(this, R.string.already_logged_in_wpcom, ToastUtils.Duration.LONG); @@ -632,11 +604,6 @@ public void helpFindingSiteAddress(String username, SiteStore siteStore) { } } - @Override - public void helpSignupMagicLinkScreen(String email) { - viewHelp(Origin.SIGNUP_MAGIC_LINK); - } - @Override public void startPostLoginServices() { // Get reader tags so they're available as soon as the Reader is accessed - done for diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 7e1346ef76ef..5e5a54b577ce 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -20,9 +20,6 @@ interface SelfSignedSSLCallback { // Login navigation void loginViaSiteAddress(); - // Login Magic Link Sent callbacks (used by SignupMagicLinkFragment) - void openEmailClient(boolean isLogin); - // Login Site Address input callbacks void alreadyLoggedInWpcom(ArrayList oldSitesIds); void gotConnectedSiteInfo(@NonNull String siteAddress, @Nullable String redirectUrl, boolean hasJetpack); @@ -35,7 +32,5 @@ interface SelfSignedSSLCallback { void startPostLoginServices(); // Signup - void helpSignupMagicLinkScreen(String email); - void showSignupMagicLink(String email); void showSignupToLoginMessage(); } diff --git a/libs/login/src/main/java/org/wordpress/android/login/SignupMagicLinkFragment.java b/libs/login/src/main/java/org/wordpress/android/login/SignupMagicLinkFragment.java deleted file mode 100644 index a07d339fef65..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/SignupMagicLinkFragment.java +++ /dev/null @@ -1,306 +0,0 @@ -package org.wordpress.android.login; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; - -import androidx.annotation.Nullable; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -import org.wordpress.android.fluxc.Dispatcher; -import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayload; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadFlow; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadScheme; -import org.wordpress.android.fluxc.store.AccountStore.AuthEmailPayloadSource; -import org.wordpress.android.fluxc.store.AccountStore.OnAuthEmailSent; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.NetworkUtils; - -import javax.inject.Inject; - -import dagger.android.support.AndroidSupportInjection; - -public class SignupMagicLinkFragment extends Fragment { - private static final String ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS"; - private static final String ARG_IS_JETPACK_CONNECT = "ARG_IS_JETPACK_CONNECT"; - private static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; - private static final String ARG_IS_EMAIL_CLIENT_AVAILABLE = "ARG_IS_EMAIL_CLIENT_AVAILABLE"; - private static final String ARG_MAGIC_LINK_SCHEME = "ARG_MAGIC_LINK_SCHEME"; - private static final String SIGNUP_FLOW_NAME = "mobile-android"; - - public static final String TAG = "signup_magic_link_fragment_tag"; - - private Button mOpenMailButton; - private ProgressDialog mProgressDialog; - private String mJetpackConnectSource; - private boolean mIsJetpackConnect; - private AuthEmailPayloadScheme mScheme; - - @Inject protected Dispatcher mDispatcher; - @Inject protected LoginAnalyticsListener mAnalyticsListener; - protected LoginListener mLoginListener; - protected String mEmail; - protected boolean mInProgress; - - public static SignupMagicLinkFragment newInstance(String email, boolean isJetpackConnect, - String jetpackConnectSource) { - return newInstance(email, isJetpackConnect, jetpackConnectSource, null, null); - } - - public static SignupMagicLinkFragment newInstance(String email, boolean isJetpackConnect, - String jetpackConnectSource, - Boolean isEmailClientAvailable) { - return newInstance(email, isJetpackConnect, jetpackConnectSource, isEmailClientAvailable, - null); - } - - public static SignupMagicLinkFragment newInstance(String email, boolean isJetpackConnect, - String jetpackConnectSource, - Boolean isEmailClientAvailable, - AuthEmailPayloadScheme scheme) { - Bundle args = new Bundle(); - args.putString(ARG_EMAIL_ADDRESS, email); - args.putBoolean(ARG_IS_JETPACK_CONNECT, isJetpackConnect); - args.putString(ARG_JETPACK_CONNECT_SOURCE, jetpackConnectSource); - if (scheme != null) { - args.putSerializable(ARG_MAGIC_LINK_SCHEME, scheme); - } - if (isEmailClientAvailable != null) { - args.putBoolean(ARG_IS_EMAIL_CLIENT_AVAILABLE, isEmailClientAvailable); - } - SignupMagicLinkFragment fragment = new SignupMagicLinkFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getArguments() != null) { - mEmail = getArguments().getString(ARG_EMAIL_ADDRESS); - } - - setHasOptionsMenu(true); - } - - /** Determines whether to hide the "Check email button". - * When we know that the email client is not available, rather than toasting an error, we hide the button instead. - * @return - */ - private boolean shouldHideButton() { - Bundle args = getArguments(); - // preserve default behavior - if (args == null || !args.containsKey(ARG_IS_EMAIL_CLIENT_AVAILABLE)) { - return false; - } - // hide button if we know the client is not available - return !args.getBoolean(ARG_IS_EMAIL_CLIENT_AVAILABLE); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View layout = inflater.inflate(R.layout.signup_magic_link_screen, container, false); - - mOpenMailButton = layout.findViewById(R.id.signup_magic_link_button); - - if (shouldHideButton()) { - mOpenMailButton.setVisibility(View.GONE); - } else { - mOpenMailButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (mLoginListener != null) { - mLoginListener.openEmailClient(false); - } - } - }); - } - - if (getArguments() != null) { - mIsJetpackConnect = getArguments().getBoolean(ARG_IS_JETPACK_CONNECT); - mJetpackConnectSource = getArguments().getString(ARG_JETPACK_CONNECT_SOURCE); - mScheme = (AuthEmailPayloadScheme) getArguments() - .getSerializable(ARG_MAGIC_LINK_SCHEME); - } - - if (savedInstanceState == null) { - sendMagicLinkEmail(); - } - - return layout; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - Toolbar toolbar = view.findViewById(R.id.toolbar); - ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); - ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); - - if (actionBar != null) { - actionBar.setTitle(R.string.sign_up_label); - actionBar.setDisplayHomeAsUpEnabled(true); - } - - if (savedInstanceState == null) { - mAnalyticsListener.trackSignupMagicLinkOpenEmailClientViewed(); - } - } - - @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - // important for accessibility - talkback - getActivity().setTitle(R.string.signup_magic_link_title); - } - - @Override - public void onAttach(Context context) { - AndroidSupportInjection.inject(this); - super.onAttach(context); - - if (context instanceof LoginListener) { - mLoginListener = (LoginListener) context; - } else { - throw new RuntimeException(context.toString() + " must implement LoginListener"); - } - - mDispatcher.register(this); - } - - @Override - public void onDetach() { - super.onDetach(); - mLoginListener = null; - mDispatcher.unregister(this); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_login, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.help) { - mAnalyticsListener.trackShowHelpClick(); - if (mLoginListener != null) { - mLoginListener.helpSignupMagicLinkScreen(mEmail); - } - - return true; - } - - return false; - } - - protected void startProgress(String message) { - mOpenMailButton.setEnabled(false); - - mProgressDialog = ProgressDialog.show(getActivity(), "", message, true, true, - new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - if (mInProgress) { - endProgress(); - } - } - }); - - mInProgress = true; - } - - protected void endProgress() { - mInProgress = false; - - if (mProgressDialog != null) { - mProgressDialog.cancel(); - } - - mProgressDialog = null; - mOpenMailButton.setEnabled(true); - } - - protected void sendMagicLinkEmail() { - if (NetworkUtils.checkConnection(getActivity())) { - startProgress(getString(R.string.signup_magic_link_progress)); - AuthEmailPayloadSource source = getAuthEmailPayloadSource(); - AuthEmailPayload authEmailPayload = new AuthEmailPayload(mEmail, true, - mIsJetpackConnect ? AuthEmailPayloadFlow.JETPACK : null, source); - authEmailPayload.signupFlowName = SIGNUP_FLOW_NAME; - authEmailPayload.scheme = mScheme; - mDispatcher.dispatch(AuthenticationActionBuilder.newSendAuthEmailAction(authEmailPayload)); - } - } - - private AuthEmailPayloadSource getAuthEmailPayloadSource() { - if (mJetpackConnectSource != null) { - if (mJetpackConnectSource.equalsIgnoreCase(AuthEmailPayloadSource.NOTIFICATIONS.toString())) { - return AuthEmailPayloadSource.NOTIFICATIONS; - } else if (mJetpackConnectSource.equalsIgnoreCase(AuthEmailPayloadSource.STATS.toString())) { - return AuthEmailPayloadSource.STATS; - } else { - return null; - } - } else { - return null; - } - } - - protected void showErrorDialog(String message) { - mAnalyticsListener.trackFailure(message); - DialogInterface.OnClickListener dialogListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - sendMagicLinkEmail(); - break; - // DialogInterface.BUTTON_NEGATIVE is intentionally ignored. Just dismiss dialog. - } - } - }; - - AlertDialog dialog = new MaterialAlertDialogBuilder(getActivity()) - .setMessage(message) - .setNegativeButton(R.string.signup_magic_link_error_button_negative, dialogListener) - .setPositiveButton(R.string.signup_magic_link_error_button_positive, dialogListener) - .create(); - dialog.show(); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onAuthEmailSent(OnAuthEmailSent event) { - if (mInProgress) { - endProgress(); - - if (event.isError()) { - mAnalyticsListener.trackSignupMagicLinkFailed(); - AppLog.e(T.API, "OnAuthEmailSent error: " + event.error.type + " - " + event.error.message); - showErrorDialog(getString(R.string.signup_magic_link_error)); - } else { - mAnalyticsListener.trackSignupMagicLinkSent(); - } - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java index ba4a0da66157..77fde3c352d0 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java +++ b/libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java @@ -1,7 +1,6 @@ package org.wordpress.android.login.di; import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment; -import org.wordpress.android.login.SignupMagicLinkFragment; import dagger.Module; import dagger.android.ContributesAndroidInjector; @@ -10,7 +9,4 @@ public abstract class LoginFragmentModule { @ContributesAndroidInjector abstract LoginSiteAddressHelpDialogFragment loginSiteAddressHelpDialogFragment(); - - @ContributesAndroidInjector - abstract SignupMagicLinkFragment signupMagicLinkFragment(); } diff --git a/libs/login/src/main/res/layout/signup_magic_link_screen.xml b/libs/login/src/main/res/layout/signup_magic_link_screen.xml deleted file mode 100644 index 5ec0d7ed3e77..000000000000 --- a/libs/login/src/main/res/layout/signup_magic_link_screen.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - From 95373f7ce94edb5b62470ccf568a453eb499da50 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:13:35 -0700 Subject: [PATCH 08/71] Remove orphaned files --- WordPress/src/main/AndroidManifest.xml | 6 - .../wordpress/android/modules/LoginModule.kt | 3 +- .../wordpress/android/login/AuthOptions.kt | 6 - .../login/LoginBaseDiscoveryFragment.java | 83 ---- .../login/LoginHttpAuthDialogFragment.java | 135 ----- .../android/login/LoginWpcomService.java | 462 ------------------ .../android/login/di/LoginServiceModule.java | 12 - .../android/login/webauthn/PasskeyRequest.kt | 92 ---- .../main/res/layout/login_alert_http_auth.xml | 37 -- .../res/layout/login_include_email_header.xml | 37 -- .../src/main/res/layout/login_or_layout.xml | 26 - 11 files changed, 1 insertion(+), 898 deletions(-) delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/AuthOptions.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginBaseDiscoveryFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginHttpAuthDialogFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginWpcomService.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/di/LoginServiceModule.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/webauthn/PasskeyRequest.kt delete mode 100644 libs/login/src/main/res/layout/login_alert_http_auth.xml delete mode 100644 libs/login/src/main/res/layout/login_include_email_header.xml delete mode 100644 libs/login/src/main/res/layout/login_or_layout.xml diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 981df4ce2d2b..59bd2dca2ff2 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -1172,12 +1172,6 @@ android:exported="false" android:label="Installation Referrer Service" /> - - { - LoginBaseDiscoveryListener mLoginBaseDiscoveryListener; - - public interface LoginBaseDiscoveryListener { - String getRequestedSiteAddress(); - void handleWpComDiscoveryError(String failedEndpoint); - void handleDiscoverySuccess(String endpointAddress); - void handleDiscoveryError(DiscoveryError error, String failedEndpoint); - } - - @Override - public void onDetach() { - super.onDetach(); - mLoginBaseDiscoveryListener = null; - } - - void initiateDiscovery() { - if (mLoginBaseDiscoveryListener == null || !NetworkUtils.checkConnection(getActivity())) { - // Fragment was detached or there's no active network connection - return; - } - - // Start the discovery process - mDispatcher.dispatch(AuthenticationActionBuilder.newDiscoverEndpointAction( - mLoginBaseDiscoveryListener.getRequestedSiteAddress())); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onDiscoverySucceeded(OnDiscoveryResponse event) { - if (mLoginBaseDiscoveryListener == null) { - // Ignore the event if the fragment is detached - return; - } - // hold the URL in a variable to use below otherwise it gets cleared up by endProgress - // bail if user canceled - String mRequestedSiteAddress = mLoginBaseDiscoveryListener.getRequestedSiteAddress(); - if (mRequestedSiteAddress == null) { - return; - } - - if (!isAdded()) { - return; - } - - if (event.isError()) { - if (isInProgress()) { - endProgress(); - } - - mAnalyticsListener.trackLoginFailed(event.getClass().getSimpleName(), - event.error.name(), event.error.toString()); - - AppLog.e(T.API, "onDiscoveryResponse has error: " + event.error.name() - + " - " + event.error.toString()); - handleDiscoveryError(event.error, event.failedEndpoint); - return; - } - - AppLog.i(T.NUX, "Discovery succeeded, endpoint: " + event.xmlRpcEndpoint); - mLoginBaseDiscoveryListener.handleDiscoverySuccess(event.xmlRpcEndpoint); - } - - private void handleDiscoveryError(DiscoveryError error, final String failedEndpoint) { - mAnalyticsListener.trackFailure(error.name() + " - " + failedEndpoint); - if (error == DiscoveryError.WORDPRESS_COM_SITE) { - mLoginBaseDiscoveryListener.handleWpComDiscoveryError(failedEndpoint); - } else { - mLoginBaseDiscoveryListener.handleDiscoveryError(error, failedEndpoint); - } - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginHttpAuthDialogFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginHttpAuthDialogFragment.java deleted file mode 100644 index 9669ab105164..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginHttpAuthDialogFragment.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.wordpress.android.login; - -import android.app.Activity; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.view.KeyEvent; -import android.view.View; -import android.widget.EditText; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog; -import androidx.fragment.app.DialogFragment; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; - -import org.wordpress.android.util.EditTextUtils; - -public class LoginHttpAuthDialogFragment extends DialogFragment { - public static final String TAG = "login_http_auth_dialog_fragment_tag"; - - public static final int DO_HTTP_AUTH = Activity.RESULT_FIRST_USER + 1; - - public static final String ARG_URL = "ARG_URL"; - public static final String ARG_MESSAGE = "ARG_MESSAGE"; - public static final String ARG_USERNAME = "ARG_USERNAME"; - public static final String ARG_PASSWORD = "ARG_PASSWORD"; - - private String mUrl; - private String mMessage; - - public static LoginHttpAuthDialogFragment newInstance(@NonNull final String url) { - return newInstance(url, ""); - } - - public static LoginHttpAuthDialogFragment newInstance(@NonNull final String url, @NonNull final String message) { - LoginHttpAuthDialogFragment fragment = new LoginHttpAuthDialogFragment(); - Bundle args = new Bundle(); - args.putString(ARG_URL, url); - args.putString(ARG_MESSAGE, message); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mUrl = getArguments().getString(ARG_URL); - mMessage = getArguments().getString(ARG_MESSAGE); - } - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog.Builder alert = new MaterialAlertDialogBuilder(getActivity()); - alert.setTitle(R.string.http_authorization_required); - if (!TextUtils.isEmpty(mMessage)) alert.setMessage(mMessage); - - //noinspection InflateParams - View httpAuth = getActivity().getLayoutInflater().inflate(R.layout.login_alert_http_auth, null); - alert.setView(httpAuth); - - final EditText usernameEditText = (EditText) httpAuth.findViewById(R.id.login_http_username); - final EditText passwordEditText = (EditText) httpAuth.findViewById(R.id.login_http_password); - - passwordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - String username = EditTextUtils.getText(usernameEditText); - String password = EditTextUtils.getText(passwordEditText); - sendResult(username, password); - - dismiss(); - - return true; - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dismiss(); - } - }); - alert.setPositiveButton(R.string.next, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - String username = EditTextUtils.getText(usernameEditText); - String password = EditTextUtils.getText(passwordEditText); - sendResult(username, password); - } - }); - - final AlertDialog alertDialog = alert.create(); - - // update the Next button when username edit box changes - usernameEditText.addTextChangedListener(new 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) { - updateButton(alertDialog, usernameEditText); - } - }); - - // update the Next button on first appearance - alertDialog.setOnShowListener(new DialogInterface.OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - updateButton(alertDialog, usernameEditText); - } - }); - - return alertDialog; - } - - private void updateButton(AlertDialog alertDialog, EditText editText) { - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled((editText.getText().length() > 0)); - } - - private void sendResult(String username, String password) { - Intent intent = new Intent(); - intent.putExtra(ARG_URL, mUrl); - intent.putExtra(ARG_USERNAME, username); - intent.putExtra(ARG_PASSWORD, password); - getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, intent); - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginWpcomService.java b/libs/login/src/main/java/org/wordpress/android/login/LoginWpcomService.java deleted file mode 100644 index 445e10f3ba74..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginWpcomService.java +++ /dev/null @@ -1,462 +0,0 @@ -package org.wordpress.android.login; - -import android.app.Notification; -import android.content.Context; -import android.content.Intent; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; -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.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.OnSocialChanged; -import org.wordpress.android.fluxc.store.AccountStore.OnTwoFactorAuthStarted; -import org.wordpress.android.fluxc.store.AccountStore.PushSocialPayload; -import org.wordpress.android.fluxc.store.SiteStore.FetchSitesPayload; -import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; -import org.wordpress.android.fluxc.store.SiteStore.SiteErrorType; -import org.wordpress.android.login.LoginWpcomService.LoginState; -import org.wordpress.android.login.util.SiteUtils; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.AutoForeground; -import org.wordpress.android.util.AutoForegroundNotification; -import org.wordpress.android.util.ToastUtils; - -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; - -public class LoginWpcomService extends AutoForeground { - private static final String ARG_EMAIL = "ARG_EMAIL"; - private static final String ARG_PASSWORD = "ARG_PASSWORD"; - private static final String ARG_SOCIAL_ID_TOKEN = "ARG_SOCIAL_ID_TOKEN"; - private static final String ARG_SOCIAL_LOGIN = "ARG_SOCIAL_LOGIN"; - private static final String ARG_JETPACK_APP_LOGIN = "ARG_JETPACK_APP_LOGIN"; - private static final String ARG_WOO_APP_LOGIN = "ARG_WOO_APP_LOGIN"; - private static final String ARG_SOCIAL_SERVICE = "ARG_SOCIAL_SERVICE"; - - public enum LoginStep { - IDLE, - AUTHENTICATING(25), - SOCIAL_LOGIN(25), - FETCHING_ACCOUNT(50), - FETCHING_SETTINGS(75), - FETCHING_SITES(100), - SUCCESS, - FAILURE_EMAIL_WRONG_PASSWORD, - FAILURE_2FA, - FAILURE_SOCIAL_2FA, - SECURITY_KEY_NEEDED, - FAILURE_FETCHING_ACCOUNT, - FAILURE_CANNOT_ADD_DUPLICATE_SITE, - FAILURE_USE_WPCOM_USERNAME_INSTEAD_OF_EMAIL, - FAILURE; - - public final int progressPercent; - - LoginStep() { - this.progressPercent = 0; - } - - LoginStep(int progressPercent) { - this.progressPercent = progressPercent; - } - } - - public static class LoginState implements AutoForeground.ServiceState { - private final LoginStep mStep; - - LoginState(@NonNull LoginStep step) { - this.mStep = step; - } - - public LoginStep getStep() { - return mStep; - } - - @Override - public boolean isIdle() { - return mStep == LoginStep.IDLE; - } - - @Override - public boolean isInProgress() { - return mStep != LoginStep.IDLE && !isTerminal(); - } - - @Override - public boolean isError() { - return mStep == LoginStep.FAILURE - || mStep == LoginStep.FAILURE_EMAIL_WRONG_PASSWORD - || mStep == LoginStep.FAILURE_2FA - || mStep == LoginStep.FAILURE_SOCIAL_2FA - || mStep == LoginStep.SECURITY_KEY_NEEDED - || mStep == LoginStep.FAILURE_FETCHING_ACCOUNT - || mStep == LoginStep.FAILURE_CANNOT_ADD_DUPLICATE_SITE - || mStep == LoginStep.FAILURE_USE_WPCOM_USERNAME_INSTEAD_OF_EMAIL; - } - - @Override - public boolean isTerminal() { - return mStep == LoginStep.SUCCESS || isError(); - } - - @Override - public String getStepName() { - return mStep.name(); - } - } - - private static class LoginNotification { - static Notification progress(Context context, int progress) { - return AutoForegroundNotification.progress(context, - context.getString(R.string.login_notification_channel_id), - progress, - R.string.notification_login_title_in_progress, - R.string.notification_logging_in, - R.drawable.login_notification_icon, - R.color.login_notification_accent_color); - } - - static Notification success(Context context) { - return AutoForegroundNotification.success(context, - context.getString(R.string.login_notification_channel_id), - R.string.notification_login_title_success, - R.string.notification_logged_in, - R.drawable.login_notification_icon, - R.color.login_notification_accent_color); - } - - static Notification failure(Context context, @StringRes int content) { - return AutoForegroundNotification.failure(context, - context.getString(R.string.login_notification_channel_id), - R.string.notification_login_title_stopped, - content, - R.drawable.login_notification_icon, - R.color.login_notification_accent_color); - } - } - - static class OnCredentialsOK { - OnCredentialsOK() {} - } - - static class TwoFactorRequested { - public final String userId; - public final String webauthnNonce; - public final String backupNonce; - public final String authenticatorNonce; - public final String pushNonce; - public final List supportedAuthTypes; - - TwoFactorRequested(String userId, String webauthnNonce, String backupNonce, - String authenticatorNonce, String pushNonce, - List supportedAuthTypes) { - this.userId = userId; - this.webauthnNonce = webauthnNonce; - this.backupNonce = backupNonce; - this.authenticatorNonce = authenticatorNonce; - this.pushNonce = pushNonce; - this.supportedAuthTypes = supportedAuthTypes; - } - } - - @Inject Dispatcher mDispatcher; - - @Inject LoginAnalyticsListener mAnalyticsListener; - - private String mIdToken; - private String mService; - private boolean mIsSocialLogin; - private boolean mIsJetpackAppLogin; - private boolean mIsWooAppLogin; - - public static void loginWithEmailAndPassword( - Context context, - String email, - String password, - String idToken, String service, - boolean isSocialLogin, - boolean isJetpackAppLogin, - boolean isWooAppLogin) { - Intent intent = new Intent(context, LoginWpcomService.class); - intent.putExtra(ARG_EMAIL, email); - intent.putExtra(ARG_PASSWORD, password); - intent.putExtra(ARG_SOCIAL_ID_TOKEN, idToken); - intent.putExtra(ARG_SOCIAL_SERVICE, service); - intent.putExtra(ARG_SOCIAL_LOGIN, isSocialLogin); - intent.putExtra(ARG_JETPACK_APP_LOGIN, isJetpackAppLogin); - intent.putExtra(ARG_WOO_APP_LOGIN, isWooAppLogin); - context.startService(intent); - } - - public static void clearLoginServiceState() { - clearServiceState(LoginState.class); - } - - public LoginWpcomService() { - super(new LoginState(LoginStep.IDLE)); - } - - @Override - protected void onProgressStart() { - mDispatcher.register(this); - } - - @Override - protected void onProgressEnd() { - mDispatcher.unregister(this); - } - - @Override - public Notification getNotification(LoginState state) { - switch (state.getStep()) { - case AUTHENTICATING: - case SOCIAL_LOGIN: - case FETCHING_ACCOUNT: - case FETCHING_SETTINGS: - case FETCHING_SITES: - return LoginNotification.progress(this, state.getStep().progressPercent); - case SUCCESS: - return LoginNotification.success(this); - case FAILURE_EMAIL_WRONG_PASSWORD: - return LoginNotification.failure(this, R.string.notification_error_wrong_password); - case FAILURE_2FA: - return LoginNotification.failure(this, R.string.notification_2fa_needed); - case FAILURE_SOCIAL_2FA: - return LoginNotification.failure(this, R.string.notification_2fa_needed); - case FAILURE_USE_WPCOM_USERNAME_INSTEAD_OF_EMAIL: - return LoginNotification.failure(this, R.string.notification_wpcom_username_needed); - case SECURITY_KEY_NEEDED: - return LoginNotification.failure(this, R.string.notification_security_key_needed); - case FAILURE_FETCHING_ACCOUNT: - case FAILURE_CANNOT_ADD_DUPLICATE_SITE: - case FAILURE: - return LoginNotification.failure(this, R.string.notification_login_failed); - } - - return null; - } - - @Override - protected void trackStateUpdate(Map props) { - mAnalyticsListener.trackWpComBackgroundServiceUpdate(props); - } - - private void setState(LoginStep phase) { - setState(new LoginState(phase)); - } - - @Override - public void onCreate() { - AndroidInjection.inject(this); - super.onCreate(); - - AppLog.i(T.MAIN, "LoginWpcomService > Created"); - - // TODO: Recover any login attempts that were interrupted by the service being stopped? - } - - @Override - public void onDestroy() { - AppLog.i(T.MAIN, "LoginWpcomService > Destroyed"); - super.onDestroy(); - } - - @Override - public void onTimeout(int startId) { - super.onTimeout(startId); - setState(LoginStep.FAILURE); // This will cal stopSelf() - } - - @Override - public int onStartCommand(@Nullable Intent intent, int flags, int startId) { - if (intent == null) { - return START_NOT_STICKY; - } - - setState(LoginStep.AUTHENTICATING); - - String email = intent.getStringExtra(ARG_EMAIL); - String password = intent.getStringExtra(ARG_PASSWORD); - - mIdToken = intent.getStringExtra(ARG_SOCIAL_ID_TOKEN); - mService = intent.getStringExtra(ARG_SOCIAL_SERVICE); - mIsSocialLogin = intent.getBooleanExtra(ARG_SOCIAL_LOGIN, false); - mIsJetpackAppLogin = intent.getBooleanExtra(ARG_JETPACK_APP_LOGIN, false); - mIsWooAppLogin = intent.getBooleanExtra(ARG_WOO_APP_LOGIN, false); - - AuthenticatePayload payload = new AuthenticatePayload(email, password); - mDispatcher.dispatch(AuthenticationActionBuilder.newAuthenticateAction(payload)); - AppLog.i(T.NUX, "User tries to log in wpcom. Email: " + email); - - return START_REDELIVER_INTENT; - } - - private void handleAuthError(AuthenticationErrorType error, String errorMessage) { - if (error != AuthenticationErrorType.NEEDS_2FA) { - mAnalyticsListener.trackLoginFailed(error.getClass().getSimpleName(), - error.toString(), errorMessage); - - if (mIsSocialLogin) { - mAnalyticsListener.trackSocialFailure(error.getClass().getSimpleName(), - error.toString(), errorMessage); - } - } - - switch (error) { - case INCORRECT_USERNAME_OR_PASSWORD: - case NOT_AUTHENTICATED: // NOT_AUTHENTICATED is the generic error from XMLRPC response on first call. - setState(LoginStep.FAILURE_EMAIL_WRONG_PASSWORD); - break; - case NEEDS_2FA: - // login credentials were correct anyway so, offer to save to SmartLock - signalCredentialsOK(); - - if (mIsSocialLogin) { - setState(LoginStep.FAILURE_SOCIAL_2FA); - } else { - setState(LoginStep.FAILURE_2FA); - } - break; - case EMAIL_LOGIN_NOT_ALLOWED: - setState(LoginStep.FAILURE_USE_WPCOM_USERNAME_INSTEAD_OF_EMAIL); - break; - case INVALID_REQUEST: - // TODO: FluxC: could be specific? - default: - setState(LoginStep.FAILURE); - AppLog.e(T.NUX, "Server response: " + errorMessage); - - ToastUtils.showToast(this, errorMessage == null ? getString(R.string.error_generic) : errorMessage); - break; - } - } - - private void fetchAccount() { - setState(LoginStep.FETCHING_ACCOUNT); - mDispatcher.dispatch(AccountActionBuilder.newFetchAccountAction()); - } - - private void signalCredentialsOK() { - EventBus.getDefault().post(new OnCredentialsOK()); - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onTwoFactorAuthStarted(OnTwoFactorAuthStarted event) { - signalCredentialsOK(); - setState(LoginStep.SECURITY_KEY_NEEDED); - TwoFactorRequested twoFactorRequest = new TwoFactorRequested(event.userId, - event.webauthnNonce, event.mBackupNonce, event.authenticatorNonce, - event.pushNonce, event.mSupportedAuthTypes); - EventBus.getDefault().post(twoFactorRequest); - } - - // OnChanged events - - @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); - handleAuthError(event.error.type, event.error.message); - return; - } - - AppLog.i(T.NUX, "onAuthenticationChanged: " + event.toString()); - - if (mIsSocialLogin) { - setState(LoginStep.SOCIAL_LOGIN); - PushSocialPayload payload = new PushSocialPayload(mIdToken, mService); - mDispatcher.dispatch(AccountActionBuilder.newPushSocialConnectAction(payload)); - } else { - signalCredentialsOK(); - fetchAccount(); - } - } - - @SuppressWarnings("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSocialChanged(OnSocialChanged event) { - if (event.isError()) { - mAnalyticsListener.trackSocialConnectFailure(); - switch (event.error.type) { - case UNABLE_CONNECT: - AppLog.e(T.API, "Unable to connect WordPress.com account to social account."); - break; - case USER_ALREADY_ASSOCIATED: - AppLog.e(T.API, "This social account is already associated with a WordPress.com account."); - break; - // Ignore other error cases. The above are the only two we have chosen to log. - } - - fetchAccount(); - } else if (!event.requiresTwoStepAuth) { - mAnalyticsListener.trackSocialConnectSuccess(); - fetchAccount(); - } - } - - @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); - setState(LoginStep.FAILURE_FETCHING_ACCOUNT); - return; - } - - if (event.causeOfChange == AccountAction.FETCH_ACCOUNT) { - setState(LoginStep.FETCHING_SETTINGS); - // The user's account info has been fetched and stored - next, fetch the user's settings - mDispatcher.dispatch(AccountActionBuilder.newFetchSettingsAction()); - } else if (event.causeOfChange == AccountAction.FETCH_SETTINGS) { - setState(LoginStep.FETCHING_SITES); - // The user's account settings have also been fetched and stored - now we can fetch the user's sites - FetchSitesPayload payload = - SiteUtils.getFetchSitesPayload(mIsJetpackAppLogin, mIsWooAppLogin); - mDispatcher.dispatch(SiteActionBuilder.newFetchSitesAction(payload)); - } - } - - @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()); - if (event.error.type != SiteErrorType.DUPLICATE_SITE) { - setState(LoginStep.FAILURE); - return; - } - - 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 - setState(LoginStep.FAILURE_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(this, R.string.duplicate_site_detected); - } - } - - setState(LoginStep.SUCCESS); - } -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/di/LoginServiceModule.java b/libs/login/src/main/java/org/wordpress/android/login/di/LoginServiceModule.java deleted file mode 100644 index 77bf000091b2..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/di/LoginServiceModule.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.wordpress.android.login.di; - -import org.wordpress.android.login.LoginWpcomService; - -import dagger.Module; -import dagger.android.ContributesAndroidInjector; - -@Module -public abstract class LoginServiceModule { - @ContributesAndroidInjector - abstract LoginWpcomService loginWpcomService(); -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/webauthn/PasskeyRequest.kt b/libs/login/src/main/java/org/wordpress/android/login/webauthn/PasskeyRequest.kt deleted file mode 100644 index 16d0f0a8e0a2..000000000000 --- a/libs/login/src/main/java/org/wordpress/android/login/webauthn/PasskeyRequest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.wordpress.android.login.webauthn - -import android.annotation.SuppressLint -import android.content.Context -import android.os.CancellationSignal -import org.wordpress.android.util.AppLog -import androidx.credentials.CredentialManager -import androidx.credentials.CredentialManagerCallback -import androidx.credentials.GetCredentialRequest -import androidx.credentials.GetCredentialResponse -import androidx.credentials.GetPublicKeyCredentialOption -import androidx.credentials.PublicKeyCredential -import androidx.credentials.exceptions.GetCredentialException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.wordpress.android.fluxc.annotations.action.Action -import org.wordpress.android.fluxc.generated.AuthenticationActionBuilder -import org.wordpress.android.fluxc.store.AccountStore.FinishWebauthnChallengePayload -import java.util.concurrent.Executors - -@SuppressLint("CredentialManagerMisuse") -class PasskeyRequest private constructor( - context: Context, - requestData: PasskeyRequestData, - onSuccess: (Action) -> Unit, - onFailure: (Throwable) -> Unit -) { - init { - val executor = Executors.newSingleThreadExecutor() - val signal = CancellationSignal() - val getCredRequest = GetCredentialRequest( - listOf(GetPublicKeyCredentialOption(requestData.requestJson)) - ) - - val passkeyRequestCallback = object : CredentialManagerCallback { - override fun onError(e: GetCredentialException) { - CoroutineScope(Dispatchers.Main).launch { onFailure(e) } - } - - override fun onResult(result: GetCredentialResponse) { - FinishWebauthnChallengePayload().apply { - mUserId = requestData.userId - mTwoStepNonce = requestData.twoStepNonce - mClientData = result.toJson().orEmpty() - }.let { - AuthenticationActionBuilder.newFinishSecurityKeyChallengeAction(it) - }.let(onSuccess) - } - } - - try { - CredentialManager.create(context).getCredentialAsync( - request = getCredRequest, - context = context, - cancellationSignal = signal, - executor = executor, - callback = passkeyRequestCallback - ) - } catch (e: GetCredentialException) { - onFailure(e) - } - } - - private fun GetCredentialResponse.toJson(): String? { - return when (val credential = this.credential) { - is PublicKeyCredential -> credential.authenticationResponseJson - else -> { - AppLog.e(AppLog.T.API, "Unexpected type of credential") - null - } - } - } - - data class PasskeyRequestData( - val userId: String, - val twoStepNonce: String, - val requestJson: String - ) - - companion object { - @JvmStatic - fun create( - context: Context, - requestData: PasskeyRequestData, - onSuccess: (Action) -> Unit, - onFailure: (Throwable) -> Unit - ) { - PasskeyRequest(context, requestData, onSuccess, onFailure) - } - } -} diff --git a/libs/login/src/main/res/layout/login_alert_http_auth.xml b/libs/login/src/main/res/layout/login_alert_http_auth.xml deleted file mode 100644 index b9ba3cfa2994..000000000000 --- a/libs/login/src/main/res/layout/login_alert_http_auth.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/layout/login_include_email_header.xml b/libs/login/src/main/res/layout/login_include_email_header.xml deleted file mode 100644 index 544cc2ea24f0..000000000000 --- a/libs/login/src/main/res/layout/login_include_email_header.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - diff --git a/libs/login/src/main/res/layout/login_or_layout.xml b/libs/login/src/main/res/layout/login_or_layout.xml deleted file mode 100644 index c0652dd8fdec..000000000000 --- a/libs/login/src/main/res/layout/login_or_layout.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - From a309cf34804c72c2b98066187ec97d0bdd2e519d Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:22:21 -0700 Subject: [PATCH 09/71] =?UTF-8?q?Remove=20the=20=E2=80=9CFind=20your=20sit?= =?UTF-8?q?e=20address=E2=80=9D=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wordpress/android/modules/LoginModule.kt | 10 --- .../android/ui/accounts/LoginActivity.java | 19 ----- .../LoginSiteApplicationPasswordFragment.kt | 13 ---- .../android/login/LoginListener.java | 2 - .../LoginSiteAddressHelpDialogFragment.java | 78 ------------------- .../android/login/di/LoginFragmentModule.java | 12 --- .../layout/login_alert_site_address_help.xml | 26 ------- .../res/layout/login_site_address_screen.xml | 9 --- 8 files changed, 169 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/modules/LoginModule.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginSiteAddressHelpDialogFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/di/LoginFragmentModule.java delete mode 100644 libs/login/src/main/res/layout/login_alert_site_address_help.xml diff --git a/WordPress/src/main/java/org/wordpress/android/modules/LoginModule.kt b/WordPress/src/main/java/org/wordpress/android/modules/LoginModule.kt deleted file mode 100644 index 5597ed8118e0..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/modules/LoginModule.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.wordpress.android.modules - -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import org.wordpress.android.login.di.LoginFragmentModule - -@InstallIn(SingletonComponent::class) -@Module(includes = [LoginFragmentModule::class]) -interface LoginModule diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index cd1d58906fa1..9a094d9358d3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -585,25 +585,6 @@ public void helpSiteAddress(String url) { viewHelp(Origin.LOGIN_SITE_ADDRESS); } - @Override - public void helpFindingSiteAddress(String username, SiteStore siteStore) { - mUnifiedLoginTracker.trackClick(Click.HELP_FINDING_SITE_ADDRESS); - if (!mBuildConfigWrapper.isJetpackApp()) { - viewHelp(Origin.LOGIN_SITE_ADDRESS); - } else { - if (mContactSupportFeatureConfig.isEnabled()) { - Intent intent = SupportWebViewActivity.createIntent( - this, - Origin.LOGIN_SITE_ADDRESS, - null, - null); - startActivity(intent); - } else { - mZendeskHelper.createNewTicket(this, Origin.LOGIN_SITE_ADDRESS, null); - } - } - } - @Override public void startPostLoginServices() { // Get reader tags so they're available as soon as the Reader is accessed - done for diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt index 813b08cb1935..1f461ae3b2da 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt @@ -23,7 +23,6 @@ import org.wordpress.android.WordPress import org.wordpress.android.fluxc.store.AccountStore import org.wordpress.android.login.LoginBaseFormFragment import org.wordpress.android.login.LoginListener -import org.wordpress.android.login.LoginSiteAddressHelpDialogFragment import org.wordpress.android.login.LoginSiteAddressValidator import org.wordpress.android.login.R import org.wordpress.android.login.widgets.WPLoginInputRow @@ -94,11 +93,6 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment(R.id.login_site_address_help_button).setOnClickListener { - mAnalyticsListener.trackShowHelpClick() - showSiteAddressHelp() - } - loginSiteAddressValidator.isValid.observe(viewLifecycleOwner) { enabled -> bottomButton.isEnabled = enabled } @@ -174,13 +168,6 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment - - - - - - - diff --git a/libs/login/src/main/res/layout/login_site_address_screen.xml b/libs/login/src/main/res/layout/login_site_address_screen.xml index fc5c3dd0ac28..98c311d14b95 100644 --- a/libs/login/src/main/res/layout/login_site_address_screen.xml +++ b/libs/login/src/main/res/layout/login_site_address_screen.xml @@ -22,13 +22,4 @@ android:hint="@string/login_site_address" android:imeOptions="actionNext" android:inputType="textUri" /> - - From d7ba8c58108c6ae7983d3ce3fae8953d18eb984e Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 18:50:16 -0700 Subject: [PATCH 10/71] Remove SignupEpilogueFragment --- WordPress/src/main/AndroidManifest.xml | 5 - .../android/modules/AppComponent.java | 3 - .../android/ui/ActivityLauncher.java | 35 - .../LoginMagicLinkInterceptActivity.java | 23 +- .../ui/accounts/SignupEpilogueActivity.java | 67 -- .../accounts/signup/SignupEpilogueFragment.kt | 727 ------------------ .../signup/SignupEpilogueListener.java | 5 - .../android/ui/accounts/signup/SignupUtils.kt | 42 - ...UsernameChangerFullScreenDialogFragment.kt | 41 - .../android/ui/main/WPMainActivity.java | 53 +- .../wordpress/android/ui/prefs/AppPrefs.java | 15 - .../src/main/res/layout/signup_epilogue.xml | 101 --- .../res/layout/signup_epilogue_activity.xml | 14 - .../ui/accounts/signup/SignupUtilsTest.kt | 92 --- 14 files changed, 7 insertions(+), 1216 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/SignupEpilogueActivity.java delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueFragment.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueListener.java delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupUtils.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/UsernameChangerFullScreenDialogFragment.kt delete mode 100644 WordPress/src/main/res/layout/signup_epilogue.xml delete mode 100644 WordPress/src/main/res/layout/signup_epilogue_activity.xml delete mode 100644 WordPress/src/test/java/org/wordpress/android/ui/accounts/signup/SignupUtilsTest.kt diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 59bd2dca2ff2..002573c4c3cb 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -187,11 +187,6 @@ - - (), - FullScreenDialogFragment.OnConfirmListener, FullScreenDialogFragment.OnDismissListener, - OnShownListener { - private lateinit var mEditTextDisplayName: EditText - private lateinit var mEditTextUsername: EditText - private var mDialog: FullScreenDialogFragment? = null - private var mSignupEpilogueListener: SignupEpilogueListener? = null - - private lateinit var mHeaderAvatarAdd: ImageView - private var mDisplayName: String? = null - private lateinit var mEmailAddress: String - private lateinit var mPhotoUrl: String - private var mUsername: String? = null - private lateinit var mInputPassword: WPLoginInputRow - private lateinit var mHeaderAvatar: ImageView - private lateinit var mHeaderDisplayName: WPTextView - private lateinit var mHeaderEmailAddress: WPTextView - private var mBottomShadow: View? = null - private lateinit var mScrollView: NestedScrollView - private var mIsAvatarAdded: Boolean = false - private var mIsEmailSignup: Boolean = false - - private var mIsUpdatingDisplayName = false - private var mIsUpdatingPassword = false - private var mHasUpdatedPassword = false - private var mHasMadeUpdates = false - - @Inject - lateinit var mAccount: AccountStore - - @Inject - lateinit var dispatcher: Dispatcher - - @Inject - lateinit var mImageManager: ImageManager - - @Inject - lateinit var mAppPrefsWrapper: AppPrefsWrapper - - @Inject - lateinit var mUnifiedLoginTracker: UnifiedLoginTracker - - @Inject - lateinit var mSignupUtils: SignupUtils - - @Inject - lateinit var mAvatarService: AvatarService - - @LayoutRes - override fun getContentLayout(): Int { - return 0 // no content layout; entire view is inflated in createMainView - } - - @LayoutRes - override fun getProgressBarText(): Int { - return R.string.signup_updating_account - } - - override fun setupLabel(label: TextView) { - // no label in this screen - } - - override fun createMainView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): ViewGroup { - return inflater.inflate(R.layout.signup_epilogue, container, false) as ViewGroup - } - - @Suppress("LongMethod") - override fun setupContent(rootView: ViewGroup) { - val headerAvatarLayout = - rootView.findViewById(R.id.login_epilogue_header_avatar_layout) - headerAvatarLayout.isEnabled = mIsEmailSignup - headerAvatarLayout.setOnClickListener { - mUnifiedLoginTracker.trackClick(UnifiedLoginTracker.Click.SELECT_AVATAR) - GravatarQuickEditor.show( - fragment = this, - gravatarQuickEditorParams = GravatarQuickEditorParams { - email = Email(mEmailAddress) - avatarPickerContentLayout = AvatarPickerContentLayout.Horizontal - }, - authenticationMethod = AuthenticationMethod.Bearer(mAccount.accessToken.orEmpty()), - updateHandler = { event -> - when (event) { - AvatarPickerResult -> { - mPhotoUrl = AvatarUrl( - email = Email(mEmailAddress), - avatarQueryOptions = AvatarQueryOptions { - preferredSize = resources.getDimensionPixelSize(R.dimen.avatar_sz_large) - } - ).url(cacheBuster = System.currentTimeMillis().toString()).toString() - mImageManager.loadIntoCircle( - mHeaderAvatar, - ImageType.AVATAR_WITHOUT_BACKGROUND, - mPhotoUrl - ) - mHeaderAvatarAdd.visibility = View.GONE - mIsAvatarAdded = true - } - - else -> Unit - } - }, - ) - } - headerAvatarLayout.setOnLongClickListener { - ToastUtils.showToast( - activity, getString(R.string.content_description_add_avatar), - ToastUtils.Duration.SHORT - ) - true - } - headerAvatarLayout.redirectContextClickToLongPressListener() - mHeaderAvatarAdd = rootView.findViewById(R.id.login_epilogue_header_avatar_add) - mHeaderAvatarAdd.setVisibility(if (mIsEmailSignup) View.VISIBLE else View.GONE) - mHeaderAvatar = rootView.findViewById(R.id.login_epilogue_header_avatar) - mHeaderDisplayName = rootView.findViewById(R.id.login_epilogue_header_title) - mHeaderDisplayName.text = mDisplayName - mHeaderEmailAddress = rootView.findViewById(R.id.login_epilogue_header_subtitle) - mHeaderEmailAddress.text = mEmailAddress - val inputDisplayName = - rootView.findViewById(R.id.signup_epilogue_input_display) - mEditTextDisplayName = inputDisplayName.editText - mEditTextDisplayName.setText(mDisplayName) - mEditTextDisplayName.addTextChangedListener(object : TextWatcher { - @Suppress("EmptyFunctionBlock") - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - } - - @Suppress("EmptyFunctionBlock") - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - } - - override fun afterTextChanged(s: Editable) { - mDisplayName = s.toString() - mHeaderDisplayName.text = mDisplayName - } - }) - val inputUsername = - rootView.findViewById(R.id.signup_epilogue_input_username) - mEditTextUsername = inputUsername.editText - mEditTextUsername.setText(mUsername) - mEditTextUsername.setOnClickListener { - mUnifiedLoginTracker.trackClick(UnifiedLoginTracker.Click.EDIT_USERNAME) - launchDialog() - } - mEditTextUsername.onFocusChangeListener = OnFocusChangeListener { _, hasFocus -> - if (hasFocus) { - launchDialog() - } - } - mEditTextUsername.setOnKeyListener { _, keyCode, _ -> - // Consume keyboard events except for Enter (i.e. click/tap) and Tab (i.e. focus/navigation). - // The onKey method returns true if the listener has consumed the event and false otherwise - // allowing hardware keyboard users to tap and navigate, but not input text as expected. - // This allows the username changer to launch using the keyboard. - !(keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_TAB) - } - mInputPassword = rootView.findViewById(R.id.signup_epilogue_input_password) - mInputPassword.visibility = if (mIsEmailSignup) View.VISIBLE else View.GONE - val passwordDetail = - rootView.findViewById(R.id.signup_epilogue_input_password_detail) - passwordDetail.visibility = if (mIsEmailSignup) View.VISIBLE else View.GONE - - // Set focus on static text field to avoid showing keyboard on start. - mHeaderEmailAddress.requestFocus() - - mBottomShadow = rootView.findViewById(R.id.bottom_shadow) - mScrollView = rootView.findViewById(R.id.scroll_view) - mScrollView.setOnScrollChangeListener { _: NestedScrollView?, _: Int, _: Int, _: Int, _: Int -> - showBottomShadowIfNeeded() - } - // We must use onGlobalLayout here otherwise canScrollVertically will always return false - mScrollView.getViewTreeObserver() - .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this) - showBottomShadowIfNeeded() - } - }) - } - - private fun showBottomShadowIfNeeded() { - val canScrollDown = mScrollView.canScrollVertically(1) - if (mBottomShadow != null) { - mBottomShadow!!.visibility = - if (canScrollDown) View.VISIBLE else View.GONE - } - } - - override fun setupBottomButton(button: Button) { - button.setOnClickListener { - mUnifiedLoginTracker.trackClick(UnifiedLoginTracker.Click.CONTINUE) - updateAccountOrContinue() - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - (requireActivity().application as WordPress).component().inject(this) - dispatcher.dispatch(AccountActionBuilder.newFetchAccountAction()) - - mDisplayName = requireArguments().getString(ARG_DISPLAY_NAME) - mEmailAddress = StringUtils.notNullStr(requireArguments().getString(ARG_EMAIL_ADDRESS)) - mPhotoUrl = StringUtils.notNullStr(requireArguments().getString(ARG_PHOTO_URL)) - mUsername = requireArguments().getString(ARG_USERNAME) - mIsEmailSignup = requireArguments().getBoolean(ARG_IS_EMAIL_SIGNUP) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - if (savedInstanceState == null) { - // Start loading reader tags so they will be available asap - ReaderUpdateServiceStarter.startService( - WordPress.getContext(), - EnumSet.of(UpdateTask.TAGS) - ) - - mUnifiedLoginTracker.track(step = UnifiedLoginTracker.Step.SUCCESS) - if (mIsEmailSignup) { - AnalyticsTracker.track(Stat.SIGNUP_EMAIL_EPILOGUE_VIEWED) - - // Start progress and wait for account to be fetched before populating views when - // email does not exist in account store. - if (TextUtils.isEmpty(mAccountStore.account.email)) { - startProgress(false) - } else { - // Skip progress and populate views when email does exist in account store. - populateViews() - } - } else { - AnalyticsTracker.track(Stat.SIGNUP_SOCIAL_EPILOGUE_VIEWED) - mAccount.accessToken?.let { accessToken -> - DownloadAvatarAndUploadGravatarThread( - mPhotoUrl, - mEmailAddress, - accessToken - ).start() - mImageManager.loadIntoCircle( - mHeaderAvatar, - ImageType.AVATAR_WITHOUT_BACKGROUND, - (mPhotoUrl) - ) - } - } - } else { - mDialog = parentFragmentManager - .findFragmentByTag(FullScreenDialogFragment.TAG) as FullScreenDialogFragment? - - if (mDialog != null) { - mDialog!!.setOnConfirmListener(this) - mDialog!!.setOnDismissListener(this) - } - - mDisplayName = savedInstanceState.getString(KEY_DISPLAY_NAME) - mUsername = savedInstanceState.getString(KEY_USERNAME) - mIsAvatarAdded = savedInstanceState.getBoolean(KEY_IS_AVATAR_ADDED) - - if (mIsEmailSignup) { - mPhotoUrl = StringUtils.notNullStr(savedInstanceState.getString(KEY_PHOTO_URL)) - mEmailAddress = StringUtils.notNullStr(savedInstanceState.getString(KEY_EMAIL_ADDRESS)) - mHeaderEmailAddress.text = mEmailAddress - mHeaderAvatarAdd.visibility = if (mIsAvatarAdded) View.GONE else View.VISIBLE - } - mImageManager.loadIntoCircle( - mHeaderAvatar, - ImageType.AVATAR_WITHOUT_BACKGROUND, - mPhotoUrl - ) - - mIsUpdatingDisplayName = savedInstanceState.getBoolean(KEY_IS_UPDATING_DISPLAY_NAME) - mIsUpdatingPassword = savedInstanceState.getBoolean(KEY_IS_UPDATING_PASSWORD) - mHasUpdatedPassword = savedInstanceState.getBoolean(KEY_HAS_UPDATED_PASSWORD) - mHasMadeUpdates = savedInstanceState.getBoolean(KEY_HAS_MADE_UPDATES) - } - } - - @Suppress("TooGenericExceptionThrown") - override fun onAttach(context: Context) { - super.onAttach(context) - - if (context is SignupEpilogueListener) { - mSignupEpilogueListener = context - } else { - throw RuntimeException("$context must implement SignupEpilogueListener") - } - } - - override fun onConfirm(result: Bundle?) { - if (result != null) { - mUsername = result.getString(BaseUsernameChangerFullScreenDialogFragment.RESULT_USERNAME) - mEditTextUsername.setText(mUsername) - } - } - - override fun onDismiss() { - val props: MutableMap = HashMap() - props[SOURCE] = SOURCE_SIGNUP_EPILOGUE - AnalyticsTracker.track(Stat.CHANGE_USERNAME_DISMISSED, props) - mDialog = null - } - - override fun onShown() { - val props: MutableMap = HashMap() - props[SOURCE] = SOURCE_SIGNUP_EPILOGUE - AnalyticsTracker.track(Stat.CHANGE_USERNAME_DISPLAYED, props) - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putString(KEY_PHOTO_URL, mPhotoUrl) - outState.putString(KEY_DISPLAY_NAME, mDisplayName) - outState.putString(KEY_EMAIL_ADDRESS, mEmailAddress) - outState.putString(KEY_USERNAME, mUsername) - outState.putBoolean(KEY_IS_AVATAR_ADDED, mIsAvatarAdded) - outState.putBoolean(KEY_IS_UPDATING_DISPLAY_NAME, mIsUpdatingDisplayName) - outState.putBoolean(KEY_IS_UPDATING_PASSWORD, mIsUpdatingPassword) - outState.putBoolean(KEY_HAS_UPDATED_PASSWORD, mHasUpdatedPassword) - outState.putBoolean(KEY_HAS_MADE_UPDATES, mHasMadeUpdates) - } - - @Suppress("EmptyFunctionBlock") - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - } - - @Suppress("EmptyFunctionBlock") - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - } - - @Suppress("EmptyFunctionBlock") - override fun afterTextChanged(s: Editable) { - } - - @Suppress("EmptyFunctionBlock") - override fun onHelp() { - } - - override fun onLoginFinished() { - endProgress() - } - - @Suppress("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - override fun onAccountChanged(event: OnAccountChanged) { - if (event.isError) { - if (mIsUpdatingDisplayName) { - mIsUpdatingDisplayName = false - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED - else Stat.SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED - ) - } else if (mIsUpdatingPassword) { - mIsUpdatingPassword = false - } - - AppLog.e( - AppLog.T.API, ("SignupEpilogueFragment.onAccountChanged: " - + event.error.type + " - " + event.error.message) - ) - endProgress() - - if (isPasswordInErrorMessage(event.error.message)) { - showErrorDialogWithCloseButton(event.error.message) - } else { - showErrorDialog(getString(R.string.signup_epilogue_error_generic)) - } - // Wait to populate epilogue for email interface until account is fetched and email address - // is available since flow is coming from magic link with no instance argument values. - } else if (mIsEmailSignup && (event.causeOfChange == AccountAction.FETCH_ACCOUNT - ) && !TextUtils.isEmpty(mAccountStore.account.email) - ) { - endProgress() - populateViews() - } else if (event.causeOfChange == AccountAction.PUSH_SETTINGS) { - mHasMadeUpdates = true - - if (mIsUpdatingDisplayName) { - mIsUpdatingDisplayName = false - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED - else Stat.SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED - ) - } else if (mIsUpdatingPassword) { - mIsUpdatingPassword = false - mHasUpdatedPassword = true - } - - updateAccountOrContinue() - } - } - - @Suppress("unused") - @Subscribe(threadMode = ThreadMode.MAIN) - fun onUsernameChanged(event: OnUsernameChanged) { - if (event.isError) { - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_FAILED - else Stat.SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_FAILED - ) - AppLog.e( - AppLog.T.API, ("SignupEpilogueFragment.onUsernameChanged: " - + event.error.type + " - " + event.error.message) - ) - endProgress() - showErrorDialog(getString(R.string.signup_epilogue_error_generic)) - } else { - mHasMadeUpdates = true - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED - else Stat.SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED - ) - updateAccountOrContinue() - } - } - - private fun changedDisplayName(): Boolean { - return !TextUtils.equals(mAccount.account.displayName, mDisplayName) - } - - private fun changedPassword(): Boolean { - return !TextUtils.isEmpty(mInputPassword.editText.text.toString()) - } - - private fun changedUsername(): Boolean { - return !TextUtils.equals(mAccount.account.userName, mUsername) - } - - private fun isPasswordInErrorMessage(message: String): Boolean { - val lowercaseMessage = message.lowercase(Locale.getDefault()) - val lowercasePassword = getString(R.string.password).lowercase(Locale.getDefault()) - return lowercaseMessage.contains(lowercasePassword) - } - - private fun launchDialog() { - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_USERNAME_TAPPED - else Stat.SIGNUP_SOCIAL_EPILOGUE_USERNAME_TAPPED - ) - - val bundle: Bundle = BaseUsernameChangerFullScreenDialogFragment.newBundle( - mEditTextDisplayName.text.toString(), mEditTextUsername.text.toString() - ) - - mDialog = FullScreenDialogFragment.Builder(requireContext()) - .setTitle(R.string.username_changer_title) - .setAction(R.string.username_changer_action) - .setToolbarTheme(org.wordpress.android.login.R.style.ThemeOverlay_LoginFlow_Toolbar) - .setOnConfirmListener(this) - .setOnDismissListener(this) - .setOnShownListener(this) - .setContent(UsernameChangerFullScreenDialogFragment::class.java, bundle) - .build() - - mDialog?.show(requireActivity().supportFragmentManager, FullScreenDialogFragment.TAG) - } - - private fun populateViews() { - mEmailAddress = mAccountStore.account.email - mDisplayName = mSignupUtils.createDisplayNameFromEmail(mEmailAddress) - mUsername = if (!TextUtils.isEmpty(mAccountStore.account.userName) - ) mAccountStore.account.userName else mSignupUtils.createUsernameFromEmail(mEmailAddress) - mHeaderDisplayName.text = mDisplayName - mHeaderEmailAddress.text = mEmailAddress - mEditTextDisplayName.setText(mDisplayName) - mEditTextUsername.setText(mUsername) - // Set fragment arguments to know if account should be updated when values change. - val args = Bundle() - args.putString(ARG_DISPLAY_NAME, mDisplayName) - args.putString(ARG_EMAIL_ADDRESS, mEmailAddress) - args.putString(ARG_PHOTO_URL, mPhotoUrl) - args.putString(ARG_USERNAME, mUsername) - args.putBoolean(ARG_IS_EMAIL_SIGNUP, mIsEmailSignup) - arguments = args - } - - private fun showErrorDialog(message: String?) { - val dialogListener: DialogInterface.OnClickListener = - DialogInterface.OnClickListener { _, which -> - when (which) { - DialogInterface.BUTTON_NEGATIVE -> undoChanges() - DialogInterface.BUTTON_POSITIVE -> updateAccountOrContinue() - } - } - - val dialog = MaterialAlertDialogBuilder(requireActivity()) - .setMessage(message) - .setNeutralButton(R.string.login_error_button, dialogListener) - .setNegativeButton(R.string.signup_epilogue_error_button_negative, dialogListener) - .setPositiveButton(R.string.signup_epilogue_error_button_positive, dialogListener) - .create() - dialog.show() - } - - private fun showErrorDialogWithCloseButton(message: String?) { - val dialog = MaterialAlertDialogBuilder(requireActivity()) - .setMessage(message) - .setPositiveButton(R.string.login_error_button, null) - .create() - dialog.show() - } - - @SuppressLint("SetTextI18n") - private fun undoChanges() { - mDisplayName = if (!TextUtils.isEmpty(mAccountStore.account.displayName) - ) mAccountStore.account.displayName else requireArguments().getString(ARG_DISPLAY_NAME) - mEditTextDisplayName.setText(mDisplayName) - mUsername = if (!TextUtils.isEmpty(mAccountStore.account.userName) - ) mAccountStore.account.userName else requireArguments().getString(ARG_USERNAME) - mEditTextUsername.setText(mUsername) - mInputPassword.editText.setText("") - updateAccountOrContinue() - } - - private fun updateAccountOrContinue() { - if (changedUsername()) { - startProgressIfNeeded() - updateUsername() - } else if (changedDisplayName()) { - startProgressIfNeeded() - mIsUpdatingDisplayName = true - updateDisplayName() - } else if (changedPassword() && !mHasUpdatedPassword) { - startProgressIfNeeded() - mIsUpdatingPassword = true - updatePassword() - } else if (mSignupEpilogueListener != null) { - if (!mHasMadeUpdates) { - AnalyticsTracker.track( - if (mIsEmailSignup - ) Stat.SIGNUP_EMAIL_EPILOGUE_UNCHANGED - else Stat.SIGNUP_SOCIAL_EPILOGUE_UNCHANGED - ) - } - endProgressIfNeeded() - mSignupEpilogueListener!!.onContinue() - } - } - - private fun updateDisplayName() { - val payload = PushAccountSettingsPayload() - payload.params = HashMap() - payload.params["display_name"] = mDisplayName - dispatcher.dispatch(AccountActionBuilder.newPushSettingsAction(payload)) - } - - private fun updatePassword() { - val payload = PushAccountSettingsPayload() - payload.params = HashMap() - payload.params["password"] = mInputPassword.editText.text.toString() - dispatcher.dispatch(AccountActionBuilder.newPushSettingsAction(payload)) - } - - private fun updateUsername() { - mUsername?.let { - val payload = PushUsernamePayload(it, AccountUsernameActionType.KEEP_OLD_SITE_AND_ADDRESS) - dispatcher.dispatch(AccountActionBuilder.newPushUsernameAction(payload)) - } - } - - private inner class DownloadAvatarAndUploadGravatarThread( - private val mUrl: String, - private val mEmail: String, - private val mToken: String - ) : Thread() { - override fun run() { - @Suppress("TooGenericExceptionCaught") - try { - val uri = MediaUtils.downloadExternalMedia(context, mUrl.toUri()) - val file = File(URI(uri.toString())) - lifecycleScope.launch { - when (val result = mAvatarService.uploadCatching(file, mToken, Email(mEmail).hash(), true)) { - is GravatarResult.Success -> { - AppLog.i( - AppLog.T.NUX, - "Google avatar download and Gravatar upload succeeded." - ) - AnalyticsTracker.track(Stat.ME_GRAVATAR_UPLOADED) - } - - is GravatarResult.Failure -> { - AppLog.i( - AppLog.T.NUX, - "Google avatar download and Gravatar upload failed." - ) - val properties: MutableMap = HashMap() - properties["error_type"] = result.error - AnalyticsTracker.track(Stat.ME_GRAVATAR_UPLOAD_EXCEPTION, properties) - } - } - } - } catch (exception: NullPointerException) { - AppLog.e( - AppLog.T.NUX, ("Google avatar download and Gravatar upload failed - " - + exception.toString() + " - " + exception.message) - ) - } catch (exception: URISyntaxException) { - AppLog.e( - AppLog.T.NUX, ("Google avatar download and Gravatar upload failed - " - + exception.toString() + " - " + exception.message) - ) - } - } - } - - companion object { - private const val ARG_DISPLAY_NAME = "ARG_DISPLAY_NAME" - private const val ARG_EMAIL_ADDRESS = "ARG_EMAIL_ADDRESS" - private const val ARG_IS_EMAIL_SIGNUP = "ARG_IS_EMAIL_SIGNUP" - private const val ARG_PHOTO_URL = "ARG_PHOTO_URL" - private const val ARG_USERNAME = "ARG_USERNAME" - private const val KEY_DISPLAY_NAME = "KEY_DISPLAY_NAME" - private const val KEY_EMAIL_ADDRESS = "KEY_EMAIL_ADDRESS" - private const val KEY_IS_AVATAR_ADDED = "KEY_IS_AVATAR_ADDED" - private const val KEY_PHOTO_URL = "KEY_PHOTO_URL" - private const val KEY_USERNAME = "KEY_USERNAME" - private const val KEY_IS_UPDATING_DISPLAY_NAME = "KEY_IS_UPDATING_DISPLAY_NAME" - private const val KEY_IS_UPDATING_PASSWORD = "KEY_IS_UPDATING_PASSWORD" - private const val KEY_HAS_UPDATED_PASSWORD = "KEY_HAS_UPDATED_PASSWORD" - private const val KEY_HAS_MADE_UPDATES = "KEY_HAS_MADE_UPDATES" - - private const val SOURCE = "source" - private const val SOURCE_SIGNUP_EPILOGUE = "signup_epilogue" - - const val TAG: String = "signup_epilogue_fragment_tag" - - fun newInstance( - displayName: String?, emailAddress: String?, - photoUrl: String?, username: String?, - isEmailSignup: Boolean - ): SignupEpilogueFragment { - val signupEpilogueFragment = SignupEpilogueFragment() - val args = Bundle() - args.putString(ARG_DISPLAY_NAME, displayName) - args.putString(ARG_EMAIL_ADDRESS, emailAddress) - args.putString(ARG_PHOTO_URL, photoUrl) - args.putString(ARG_USERNAME, username) - args.putBoolean(ARG_IS_EMAIL_SIGNUP, isEmailSignup) - signupEpilogueFragment.arguments = args - return signupEpilogueFragment - } - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueListener.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueListener.java deleted file mode 100644 index aa83251c8a0f..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupEpilogueListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.wordpress.android.ui.accounts.signup; - -public interface SignupEpilogueListener { - void onContinue(); -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupUtils.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupUtils.kt deleted file mode 100644 index afe9eb6ecb8a..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/SignupUtils.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.wordpress.android.ui.accounts.signup - -import java.util.Locale -import javax.inject.Inject - -class SignupUtils @Inject constructor() { - /** - * Create a display name from the email address by taking everything before the "@" symbol, - * removing all non-letters and non-periods, replacing periods with spaces, and capitalizing - * the first letter of each word. - * - * @return [String] to be the display name - */ - fun createDisplayNameFromEmail(emailAddress: String): String? { - val username = emailAddress.split("@").firstOrNull() ?: return null - val usernameArray = username.replace("[^A-Za-z/.]".toRegex(), "").split(".") - val builder = StringBuilder() - for (item in usernameArray) { - if (item.isNotEmpty()) { - builder.append(item.substring(0, 1).uppercase(Locale.ROOT)) - if (item.length > 1) { - builder.append(item.substring(1)) - } - builder.append(" ") - } - } - val displayName = builder.toString().trim() - return if (displayName.isNotEmpty()) displayName else null - } - - /** - * Create a username from the email address by taking everything before the "@" symbol and - * removing all non-alphanumeric characters. - * - * @return [String] to be the username - */ - fun createUsernameFromEmail(emailAddress: String): String? { - return emailAddress.split("@".toRegex())[0] - .replace("[^A-Za-z0-9]".toRegex(), "") - .lowercase(Locale.ROOT) - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/UsernameChangerFullScreenDialogFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/UsernameChangerFullScreenDialogFragment.kt deleted file mode 100644 index c0f5da2259e4..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/signup/UsernameChangerFullScreenDialogFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.wordpress.android.ui.accounts.signup - -import android.os.Bundle -import android.text.Spanned -import androidx.core.text.HtmlCompat -import dagger.hilt.android.AndroidEntryPoint -import org.wordpress.android.R -import org.wordpress.android.analytics.AnalyticsTracker.Stat.SIGNUP_SOCIAL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED -import org.wordpress.android.ui.FullScreenDialogFragment.FullScreenDialogController - -/** - * Implements functionality specific to the Username Changer functionality in the sign-up flow. - */ -@AndroidEntryPoint -class UsernameChangerFullScreenDialogFragment : BaseUsernameChangerFullScreenDialogFragment() { - override fun getSuggestionsFailedStat() = SIGNUP_SOCIAL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED - override fun canHeaderTextLiveUpdate() = true - override fun getHeaderText(username: String?, display: String?): Spanned = HtmlCompat.fromHtml( - String.format( - getString(R.string.username_changer_header), - "", - username, - "", - "", - display, - "" - ), - HtmlCompat.FROM_HTML_MODE_LEGACY - ) - - override fun getTrackEventSource() = SOURCE - - override fun onUsernameConfirmed(controller: FullScreenDialogController, usernameSelected: String) { - val result = Bundle().apply { putString(RESULT_USERNAME, usernameSelected) } - controller.confirm(result) - } - - companion object { - const val SOURCE = "signup_epilogue" - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 0e040cde4f00..c53bf0af9a54 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -80,7 +80,6 @@ import org.wordpress.android.ui.Shortcut; import org.wordpress.android.ui.ShortcutsNavigator; import org.wordpress.android.ui.accounts.LoginActivity; -import org.wordpress.android.ui.accounts.SignupEpilogueActivity; import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingDialogFragment; import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsOnboardingDialogFragment.DialogType; import org.wordpress.android.ui.bloggingprompts.onboarding.BloggingPromptsReminderSchedulerListener; @@ -177,7 +176,6 @@ import static androidx.lifecycle.Lifecycle.State.STARTED; import static org.wordpress.android.WordPress.SITE; -import static org.wordpress.android.login.LoginAnalyticsListener.CreatedAccountSource.EMAIL; import static org.wordpress.android.push.NotificationsProcessingService.ARG_NOTIFICATION_TYPE; import static org.wordpress.android.ui.JetpackConnectionSource.NOTIFICATIONS; @@ -195,10 +193,8 @@ public class WPMainActivity extends BaseAppCompatActivity implements public static final String ARG_CONTINUE_JETPACK_CONNECT = "ARG_CONTINUE_JETPACK_CONNECT"; public static final String ARG_CREATE_SITE = "ARG_CREATE_SITE"; public static final String ARG_IS_MAGIC_LINK_LOGIN = "ARG_IS_MAGIC_LINK_LOGIN"; - public static final String ARG_IS_MAGIC_LINK_SIGNUP = "ARG_IS_MAGIC_LINK_SIGNUP"; public static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; public static final String ARG_OPENED_FROM_PUSH = "opened_from_push"; - public static final String ARG_SHOW_SIGNUP_EPILOGUE = "show_signup_epilogue"; public static final String ARG_SHOW_SITE_CREATION = "show_site_creation"; public static final String ARG_SITE_CREATION_SOURCE = "ARG_SITE_CREATION_SOURCE"; public static final String ARG_WP_COM_SIGN_UP = "sign_up"; @@ -231,7 +227,6 @@ public class WPMainActivity extends BaseAppCompatActivity implements private TextView mConnectionBar; private JetpackConnectionSource mJetpackConnectSource; private boolean mIsMagicLinkLogin; - private boolean mIsMagicLinkSignup; private WPMainActivityViewModel mViewModel; private ModalLayoutPickerViewModel mMLPViewModel; @@ -323,7 +318,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { }); mIsMagicLinkLogin = getIntent().getBooleanExtra(ARG_IS_MAGIC_LINK_LOGIN, false); - mIsMagicLinkSignup = getIntent().getBooleanExtra(ARG_IS_MAGIC_LINK_SIGNUP, false); mJetpackConnectSource = (JetpackConnectionSource) getIntent().getSerializableExtra(ARG_JETPACK_CONNECT_SOURCE); String authTokenToSet = null; @@ -424,12 +418,6 @@ && getIntent().getExtras().getBoolean(ARG_CONTINUE_JETPACK_CONNECT, false)) { // Save Token to the AccountStore. This will trigger a onAuthenticationChanged. UpdateTokenPayload payload = new UpdateTokenPayload(authTokenToSet); mDispatcher.dispatch(AccountActionBuilder.newUpdateAccessTokenAction(payload)); - } else if (getIntent().getBooleanExtra(ARG_SHOW_SIGNUP_EPILOGUE, false) && savedInstanceState == null) { - ActivityLauncher.showSignupEpilogue(this, - getIntent().getStringExtra(SignupEpilogueActivity.EXTRA_SIGNUP_DISPLAY_NAME), - getIntent().getStringExtra(SignupEpilogueActivity.EXTRA_SIGNUP_EMAIL_ADDRESS), - getIntent().getStringExtra(SignupEpilogueActivity.EXTRA_SIGNUP_PHOTO_URL), - getIntent().getStringExtra(SignupEpilogueActivity.EXTRA_SIGNUP_USERNAME), false); } else if (getIntent().getBooleanExtra(ARG_SHOW_SITE_CREATION, false) && savedInstanceState == null) { ActivityLauncher.newBlogForResult(this, SiteCreationSource.fromString(getIntent().getStringExtra(ARG_SITE_CREATION_SOURCE))); @@ -1450,25 +1438,12 @@ public void onAuthenticationChanged(OnAuthenticationChanged event) { if (mAccountStore.hasAccessToken()) { if (mIsMagicLinkLogin) { - if (mIsMagicLinkSignup) { - // Sets a flag that we need to track a magic link sign up. - // We'll handle it in onAccountChanged so we know we have - // updated account info. - AppPrefs.setShouldTrackMagicLinkSignup(true); - mDispatcher.dispatch(AccountActionBuilder.newFetchAccountAction()); - if (mJetpackConnectSource != null) { - ActivityLauncher.continueJetpackConnect(this, mJetpackConnectSource, getSelectedSite()); - } else { - ActivityLauncher.showSignupEpilogue(this, null, null, null, null, true); - } - } else { - mLoginAnalyticsListener.trackLoginMagicLinkSucceeded(); + mLoginAnalyticsListener.trackLoginMagicLinkSucceeded(); - if (mJetpackConnectSource != null) { - ActivityLauncher.continueJetpackConnect(this, mJetpackConnectSource, getSelectedSite()); - } else { - initSelectedSite(); - } + if (mJetpackConnectSource != null) { + ActivityLauncher.continueJetpackConnect(this, mJetpackConnectSource, getSelectedSite()); + } else { + initSelectedSite(); } } } @@ -1480,27 +1455,9 @@ public void onAccountChanged(OnAccountChanged event) { // Sign-out is handled in `handleSiteRemoved`, no need to show the signup flow here if (mAccountStore.hasAccessToken()) { if (mBottomNav != null) mBottomNav.showNoteBadge(mAccountStore.getAccount().getHasUnseenNotes()); - if (AppPrefs.getShouldTrackMagicLinkSignup()) { - trackMagicLinkSignupIfNeeded(); - } } } - /** - * Bumps stats related to a magic link sign up provided the account has been updated with - * the username and email address needed to refresh analytics meta data. - */ - private void trackMagicLinkSignupIfNeeded() { - AccountModel account = mAccountStore.getAccount(); - if (!TextUtils.isEmpty(account.getUserName()) && !TextUtils.isEmpty(account.getEmail())) { - mLoginAnalyticsListener.trackCreatedAccount(account.getUserName(), account.getEmail(), EMAIL); - mLoginAnalyticsListener.trackSignupMagicLinkSucceeded(); - mLoginAnalyticsListener.trackAnalyticsSignIn(true); - AppPrefs.removeShouldTrackMagicLinkSignup(); - } - } - - @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(NotificationEvents.NotificationsChanged event) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java index 967107aec596..2c2f766eff36 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java @@ -109,9 +109,6 @@ public enum DeletablePrefKey implements PrefKey { // Used to flag whether the app should strip geolocation from images STRIP_IMAGE_LOCATION, - // Used to flag the account created stat needs to be bumped after account information is synced. - SHOULD_TRACK_MAGIC_LINK_SIGNUP, - // Support email address and name that's independent of any account or site SUPPORT_EMAIL, SUPPORT_NAME, @@ -987,18 +984,6 @@ public static void setLastWpComThemeSync(long time) { setLong(UndeletablePrefKey.LAST_WP_COM_THEMES_SYNC, time); } - public static void setShouldTrackMagicLinkSignup(Boolean shouldTrack) { - setBoolean(DeletablePrefKey.SHOULD_TRACK_MAGIC_LINK_SIGNUP, shouldTrack); - } - - public static boolean getShouldTrackMagicLinkSignup() { - return getBoolean(DeletablePrefKey.SHOULD_TRACK_MAGIC_LINK_SIGNUP, false); - } - - public static void removeShouldTrackMagicLinkSignup() { - remove(DeletablePrefKey.SHOULD_TRACK_MAGIC_LINK_SIGNUP); - } - public static void setMainFabTooltipDisabled(Boolean disable) { setBoolean(UndeletablePrefKey.IS_MAIN_FAB_TOOLTIP_DISABLED, disable); } diff --git a/WordPress/src/main/res/layout/signup_epilogue.xml b/WordPress/src/main/res/layout/signup_epilogue.xml deleted file mode 100644 index 25d4291af80a..000000000000 --- a/WordPress/src/main/res/layout/signup_epilogue.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/src/main/res/layout/signup_epilogue_activity.xml b/WordPress/src/main/res/layout/signup_epilogue_activity.xml deleted file mode 100644 index c1ff2bc0b3cd..000000000000 --- a/WordPress/src/main/res/layout/signup_epilogue_activity.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/signup/SignupUtilsTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/signup/SignupUtilsTest.kt deleted file mode 100644 index 97fd7a0c2dd6..000000000000 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/signup/SignupUtilsTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.wordpress.android.ui.accounts.signup - -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner - -@RunWith(MockitoJUnitRunner::class) -class SignupUtilsTest { - private lateinit var signupUtils: SignupUtils - - @Before - fun setUp() { - this.signupUtils = SignupUtils() - } - - @Test - fun `maps simple email to display name`() { - assertEmailMappedToDisplayName("displayname@email.com", "Displayname") - } - - @Test - fun `maps longer email to display name`() { - assertEmailMappedToDisplayName("firstname.lastname@email.com", "Firstname Lastname") - } - - @Test - fun `maps single character short email to display name`() { - assertEmailMappedToDisplayName("a@email.com", "A") - } - - @Test - fun `maps single character longer email to display name`() { - assertEmailMappedToDisplayName("a.b.c@email.com", "A B C") - } - - @Test - fun `maps invalid email only with domain to null`() { - assertEmailMappedToDisplayName("@email.com", null) - } - - @Test - fun `maps invalid email ending with numbers in the middle`() { - assertEmailMappedToDisplayName("username.12.a@email.com", "Username A") - } - - @Test - fun `maps invalid email without domain to just display name`() { - assertEmailMappedToDisplayName("displayname", "Displayname") - } - - @Test - fun `creates username from simple email`() { - assertEmailMappedToUsername("username@email.com", "username") - } - - @Test - fun `creates username from longer email`() { - assertEmailMappedToUsername("firstname.lastname@email.com", "firstnamelastname") - } - - @Test - fun `creates username from single character short email`() { - assertEmailMappedToUsername("a@email.com", "a") - } - - @Test - fun `creates username from single character longer email`() { - assertEmailMappedToUsername("a.b.c@email.com", "abc") - } - - @Test - fun `returns null username from invalid email only with domain`() { - assertEmailMappedToUsername("@email.com", "") - } - - @Test - fun `creates username from invalid email without domain`() { - assertEmailMappedToUsername("username", "username") - } - - private fun assertEmailMappedToDisplayName(email: String, expectedDisplayName: String? = null) { - val displayName = signupUtils.createDisplayNameFromEmail(email) - assertThat(displayName).isEqualTo(expectedDisplayName) - } - - private fun assertEmailMappedToUsername(email: String, expectedUsername: String? = null) { - val username = signupUtils.createUsernameFromEmail(email) - assertThat(username).isEqualTo(expectedUsername) - } -} From c484a9142a6fc88a56e2a6077339902bc7dc0221 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:05:57 -0700 Subject: [PATCH 11/71] Remove native account signup code --- WordPress/build.gradle | 2 - .../wordpress/android/ui/RequestCodes.java | 1 - .../android/ui/accounts/HelpActivity.kt | 4 - .../android/ui/accounts/LoginActivity.java | 22 -- .../accounts/login/LoginAnalyticsTracker.java | 309 +----------------- .../android/util/BuildConfigWrapper.kt | 2 - .../android/analytics/AnalyticsTracker.java | 31 -- .../analytics/AnalyticsTrackerNosara.java | 24 -- .../android/login/LoginAnalyticsListener.kt | 71 ---- .../android/login/LoginListener.java | 3 - 10 files changed, 1 insertion(+), 468 deletions(-) diff --git a/WordPress/build.gradle b/WordPress/build.gradle index cd4cb1c3e5eb..36700501aa7b 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -168,7 +168,6 @@ android { // Override these constants in jetpack product flavor to enable/ disable features buildConfigField "boolean", "ENABLE_SITE_CREATION", "true" buildConfigField "boolean", "ENABLE_ADD_SELF_HOSTED_SITE", "true" - buildConfigField "boolean", "ENABLE_SIGNUP", "true" buildConfigField "boolean", "ENABLE_READER", "true" buildConfigField "boolean", "ENABLE_CREATE_FAB", "true" buildConfigField "boolean", "ENABLE_FOLLOWED_SITES_SETTINGS", "true" @@ -221,7 +220,6 @@ android { buildConfigField "boolean", "IS_JETPACK_APP", "true" buildConfigField "boolean", "ENABLE_SITE_CREATION", "true" buildConfigField "boolean", "ENABLE_ADD_SELF_HOSTED_SITE", "true" - buildConfigField "boolean", "ENABLE_SIGNUP", "true" buildConfigField "boolean", "ENABLE_READER", "true" buildConfigField "boolean", "ENABLE_CREATE_FAB", "true" buildConfigField "boolean", "ENABLE_FOLLOWED_SITES_SETTINGS", "true" diff --git a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java index 5a6f20fc0634..f6a181cf0e29 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java @@ -21,7 +21,6 @@ public class RequestCodes { public static final int STOCK_MEDIA_PICKER_SINGLE_SELECT = 1202; public static final int STOCK_MEDIA_PICKER_SINGLE_SELECT_FOR_GUTENBERG_BLOCK = 1203; public static final int SITE_ICON_PICKER = 1205; - public static final int SHOW_SIGNUP_EPILOGUE_AND_RETURN = 1301; public static final int SMART_LOCK_SAVE = 1400; public static final int SMART_LOCK_READ = 1500; public static final int NOTIFICATION_SETTINGS = 1600; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt index af0eb05068f1..c6692a6fe3ef 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/HelpActivity.kt @@ -497,7 +497,6 @@ class HelpActivity : BaseAppCompatActivity() { LOGIN_SCREEN_WPCOM("origin:wpcom-login-screen"), LOGIN_SCREEN_SELF_HOSTED("origin:wporg-login-screen"), LOGIN_SCREEN_JETPACK("origin:jetpack-login-screen"), - SIGNUP_SCREEN("origin:signup-screen"), ME_SCREEN_HELP("origin:me-screen-help"), DELETE_SITE("origin:delete-site"), DISCARD_CHANGES("origin:discard-changes"), @@ -509,9 +508,6 @@ class HelpActivity : BaseAppCompatActivity() { LOGIN_SITE_ADDRESS("origin:login-site-address"), LOGIN_SOCIAL("origin:login-social"), LOGIN_USERNAME_PASSWORD("origin:login-username-password"), - SIGNUP_EMAIL("origin:signup-email"), - SIGNUP_MAGIC_LINK("origin:signup-magic-link"), - SIGNUP_CONFIRMATION("origin:signup-confirmation"), SITE_CREATION_CREATING("origin:site-create-creating"), SITE_CREATION_SEGMENTS("origin:site-create-site-segments"), SITE_CREATION_VERTICALS("origin:site-create-site-verticals"), diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 9a094d9358d3..c8f7641da8d1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -106,7 +106,6 @@ public class LoginActivity extends BaseAppCompatActivity implements ConnectionCa public static final String TOKEN_PARAMETER = "token"; private static final String KEY_SMARTLOCK_HELPER_STATE = "KEY_SMARTLOCK_HELPER_STATE"; - private static final String KEY_SIGNUP_FROM_LOGIN_ENABLED = "KEY_SIGNUP_FROM_LOGIN_ENABLED"; private static final String KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE = "KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE"; private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; @@ -123,7 +122,6 @@ private enum SmartLockHelperState { private JetpackConnectionSource mJetpackConnectSource; private boolean mIsJetpackConnect; - private boolean mIsSignupFromLoginEnabled; private boolean mIsSmartLockTriggeredFromPrologue; private boolean mIsSiteLoginAvailableFromPrologue; @@ -190,13 +188,11 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { case FULL: case JETPACK_LOGIN_ONLY: mUnifiedLoginTracker.setSource(Source.DEFAULT); - mIsSignupFromLoginEnabled = mBuildConfigWrapper.isSignupEnabled(); loginFromPrologue(); break; case WPCOM_LOGIN_ONLY: case JETPACK_REST_CONNECT: mUnifiedLoginTracker.setSource(Source.ADD_WORDPRESS_COM_ACCOUNT); - mIsSignupFromLoginEnabled = mBuildConfigWrapper.isSignupEnabled(); checkSmartLockPasswordAndStartLogin(); break; case JETPACK_SELFHOSTED: @@ -206,7 +202,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { break; case JETPACK_STATS: mUnifiedLoginTracker.setSource(Source.JETPACK); - mIsSignupFromLoginEnabled = mBuildConfigWrapper.isSignupEnabled(); checkSmartLockPasswordAndStartLogin(); break; case WPCOM_LOGIN_DEEPLINK: @@ -233,7 +228,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { initSmartLockHelperConnection(); } - mIsSignupFromLoginEnabled = savedInstanceState.getBoolean(KEY_SIGNUP_FROM_LOGIN_ENABLED); mIsSiteLoginAvailableFromPrologue = savedInstanceState.getBoolean(KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE); String source = savedInstanceState.getString(KEY_UNIFIED_TRACKER_SOURCE); if (source != null) { @@ -270,7 +264,6 @@ private void loginFromPrologue() { public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_SMARTLOCK_HELPER_STATE, mSmartLockHelperState.name()); - outState.putBoolean(KEY_SIGNUP_FROM_LOGIN_ENABLED, mIsSignupFromLoginEnabled); outState.putBoolean(KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE, mIsSiteLoginAvailableFromPrologue); outState.putString(KEY_UNIFIED_TRACKER_SOURCE, mUnifiedLoginTracker.getSource().getValue()); Flow flow = mUnifiedLoginTracker.getFlow(); @@ -454,12 +447,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { - case RequestCodes.SHOW_SIGNUP_EPILOGUE_AND_RETURN: - // we showed the epilogue screen as informational and sites got loaded so, just - // return to login caller now - setResult(RESULT_OK); - finish(); - break; case RequestCodes.SMART_LOCK_SAVE: if (resultCode == RESULT_OK) { mLoginAnalyticsListener.trackLoginAutofillCredentialsUpdated(); @@ -650,15 +637,6 @@ public void onConnectionSuspended(int i) { AppLog.d(AppLog.T.NUX, "Google API client connection suspended"); } - @Override - public void showSignupToLoginMessage() { - WPSnackbar.make( - findViewById(R.id.main_view), - R.string.signup_user_exists, - Snackbar.LENGTH_LONG - ).show(); - } - @Override public void onPositiveClicked(@NonNull String instanceTag) { // No dialog tags currently handled diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java index d85022a7a85a..eafcf21aa4b7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java @@ -35,253 +35,32 @@ public void trackAnalyticsSignIn(boolean isWpcom) { AnalyticsUtils.trackAnalyticsSignIn(mAccountStore, mSiteStore, isWpcom); } - @Override - public void trackCreatedAccount(String username, String email, CreatedAccountSource source) { - AnalyticsUtils.trackAnalyticsAccountCreated(username, email, source.asPropertyMap()); - } - - @Override - public void trackEmailFormViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_EMAIL_FORM_VIEWED); - mUnifiedLoginTracker.track(Flow.WORDPRESS_COM, Step.START); - } - - @Override - public void trackInsertedInvalidUrl() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_INSERTED_INVALID_URL); - } - @Override public void trackLoginAccessed() { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_ACCESSED); } - @Override - public void trackLoginAutofillCredentialsFilled() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_AUTOFILL_CREDENTIALS_FILLED); - mUnifiedLoginTracker.track(Flow.SMART_LOCK_LOGIN, Step.START); - } - @Override public void trackLoginAutofillCredentialsUpdated() { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_AUTOFILL_CREDENTIALS_UPDATED); } - @Override - public void trackLoginFailed(String errorContext, String errorType, String errorDescription) { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_FAILED, errorContext, errorType, errorDescription); - } - - @Override - public void trackLoginForgotPasswordClicked() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_FORGOT_PASSWORD_CLICKED); - mUnifiedLoginTracker.trackClick(Click.FORGOTTEN_PASSWORD); - } - - @Override - public void trackLoginMagicLinkExited() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_EXITED); - } - @Override public void trackLoginMagicLinkOpened() { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_OPENED); } - @Override - public void trackLoginMagicLinkOpenEmailClientClicked() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_OPEN_EMAIL_CLIENT_CLICKED); - mUnifiedLoginTracker.track(Flow.LOGIN_MAGIC_LINK, Step.EMAIL_OPENED); - } - @Override public void trackLoginMagicLinkSucceeded() { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_SUCCEEDED); } - @Override - public void trackLoginSocial2faNeeded() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_2FA_NEEDED); - } - - @Override - public void trackLoginSocialSuccess() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_SUCCESS); - } - - @Override - public void trackMagicLinkFailed(Map properties) { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_FAILED, properties); - } - - @Override - public void trackSignupMagicLinkOpenEmailClientViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_OPEN_EMAIL_CLIENT_VIEWED); - mUnifiedLoginTracker.track(Flow.SIGNUP, Step.MAGIC_LINK_REQUESTED); - } - - @Override - public void trackLoginMagicLinkOpenEmailClientViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_OPEN_EMAIL_CLIENT_VIEWED); - mUnifiedLoginTracker.track(Flow.LOGIN_MAGIC_LINK, Step.MAGIC_LINK_REQUESTED); - } - - @Override - public void trackMagicLinkRequested() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_REQUESTED); - } - - @Override - public void trackMagicLinkRequestFormViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_MAGIC_LINK_REQUEST_FORM_VIEWED); - mUnifiedLoginTracker.track(Flow.LOGIN_MAGIC_LINK, Step.START); - } - - @Override - public void trackPasswordFormViewed(boolean isSocialChallenge) { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_PASSWORD_FORM_VIEWED); - if (isSocialChallenge) { - mUnifiedLoginTracker.track(Flow.GOOGLE_LOGIN, Step.PASSWORD_CHALLENGE); - } else { - mUnifiedLoginTracker.track(Flow.LOGIN_PASSWORD, Step.START); - } - } - - @Override - public void trackSignupCanceled() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_CANCELED); - } - - @Override - public void trackSignupEmailButtonTapped() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_EMAIL_BUTTON_TAPPED); - } - - @Override - public void trackSignupEmailToLogin() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_EMAIL_TO_LOGIN); - } - - @Override - public void trackSignupGoogleButtonTapped() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_SOCIAL_BUTTON_TAPPED); - } - - @Override - public void trackSignupMagicLinkFailed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_MAGIC_LINK_FAILED); - } - - @Override - public void trackSignupMagicLinkOpened() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_MAGIC_LINK_OPENED); - } - - @Override - public void trackSignupMagicLinkOpenEmailClientClicked() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_MAGIC_LINK_OPEN_EMAIL_CLIENT_CLICKED); - mUnifiedLoginTracker.track(Flow.SIGNUP, Step.EMAIL_OPENED); - } - - @Override - public void trackSignupMagicLinkSent() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_MAGIC_LINK_SENT); - } - - @Override - public void trackSignupMagicLinkSucceeded() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_MAGIC_LINK_SUCCEEDED); - } - - @Override - public void trackSignupSocialAccountsNeedConnecting() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_SOCIAL_ACCOUNTS_NEED_CONNECTING); - } - - @Override - public void trackSignupSocialButtonFailure() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_SOCIAL_BUTTON_FAILURE); - } - - @Override - public void trackSignupSocialToLogin() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_SOCIAL_TO_LOGIN); - } - - @Override - public void trackSignupTermsOfServiceTapped() { - AnalyticsTracker.track(AnalyticsTracker.Stat.SIGNUP_TERMS_OF_SERVICE_TAPPED); - } - - @Override - public void trackSocialButtonStart() { - mUnifiedLoginTracker.track(Flow.GOOGLE_LOGIN, Step.START); - } - - @Override - public void trackSocialAccountsNeedConnecting() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_ACCOUNTS_NEED_CONNECTING); - } - - @Override - public void trackSocialButtonClick() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_BUTTON_CLICK); - mUnifiedLoginTracker.trackClick(Click.LOGIN_WITH_GOOGLE); - } - - @Override - public void trackSocialButtonFailure() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_BUTTON_FAILURE); - } - - @Override - public void trackSocialConnectFailure() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_CONNECT_FAILURE); - } - - @Override - public void trackSocialConnectSuccess() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_CONNECT_SUCCESS); - } - - @Override - public void trackSocialErrorUnknownUser() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_ERROR_UNKNOWN_USER); - } - - @Override - public void trackSocialFailure(String errorContext, String errorType, String errorDescription) { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SOCIAL_FAILURE, errorContext, errorType, errorDescription); - } - - @Override - public void trackTwoFactorFormViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_TWO_FACTOR_FORM_VIEWED); - mUnifiedLoginTracker.track(Step.TWO_FACTOR_AUTHENTICATION); - } - @Override public void trackUrlFormViewed() { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_URL_FORM_VIEWED); mUnifiedLoginTracker.track(Flow.LOGIN_SITE_ADDRESS, Step.START); } - @Override - public void trackUrlHelpScreenViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_URL_HELP_SCREEN_VIEWED); - } - - @Override - public void trackUsernamePasswordFormViewed() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_USERNAME_PASSWORD_FORM_VIEWED); - mUnifiedLoginTracker.track(Step.USERNAME_PASSWORD); - } - - @Override - public void trackWpComBackgroundServiceUpdate(Map properties) { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_WPCOM_BACKGROUND_SERVICE_UPDATE, properties); - } - @Override public void trackConnectedSiteInfoRequested(String url) { Map properties = new HashMap<>(); @@ -306,31 +85,11 @@ public void trackFailure(String message) { mUnifiedLoginTracker.trackFailure(message); } - @Override - public void trackSendCodeWithTextClicked() { - mUnifiedLoginTracker.trackClick(Click.SEND_CODE_WITH_TEXT); - } - - @Override - public void trackSubmit2faCodeClicked() { - mUnifiedLoginTracker.trackClick(Click.SUBMIT_2FA_CODE); - } - @Override public void trackSubmitClicked() { mUnifiedLoginTracker.trackClick(Click.SUBMIT); } - @Override - public void trackRequestMagicLinkClick() { - mUnifiedLoginTracker.trackClick(Click.REQUEST_MAGIC_LINK); - } - - @Override - public void trackLoginWithPasswordClick() { - mUnifiedLoginTracker.trackClick(Click.LOGIN_WITH_PASSWORD); - } - @Override public void trackShowHelpClick() { mUnifiedLoginTracker.trackClick(Click.SHOW_HELP); @@ -338,73 +97,7 @@ public void trackShowHelpClick() { } @Override - public void trackDismissDialog() { - mUnifiedLoginTracker.trackClick(Click.DISMISS); - } - - @Override - public void trackSelectEmailField() { - mUnifiedLoginTracker.trackClick(Click.SELECT_EMAIL_FIELD); - } - - @Override - public void trackPickEmailFromHint() { - mUnifiedLoginTracker.trackClick(Click.PICK_EMAIL_FROM_HINT); - } - - @Override - public void trackShowEmailHints() { - mUnifiedLoginTracker.track(Step.SHOW_EMAIL_HINTS); - } - - @Override - public void emailFormScreenResumed() { - mUnifiedLoginTracker.setFlowAndStep(Flow.WORDPRESS_COM, Step.START); - } - - @Override - public void trackSocialSignupConfirmationViewed() { - mUnifiedLoginTracker.track(Flow.GOOGLE_SIGNUP, Step.START); - } - - @Override - public void trackCreateAccountClick() { - mUnifiedLoginTracker.trackClick(Click.CREATE_ACCOUNT); - } - - @Override public void emailPasswordFormScreenResumed() { - mUnifiedLoginTracker.setStep(Step.START); - } - - @Override public void siteAddressFormScreenResumed() { + public void siteAddressFormScreenResumed() { mUnifiedLoginTracker.setStep(Step.START); } - - @Override public void magicLinkRequestScreenResumed() { - mUnifiedLoginTracker.setStep(Step.START); - } - - @Override public void magicLinkSentScreenResumed() { - mUnifiedLoginTracker.setStep(Step.MAGIC_LINK_REQUESTED); - } - - @Override public void usernamePasswordScreenResumed() { - mUnifiedLoginTracker.setStep(Step.USERNAME_PASSWORD); - } - - @Override public void trackLoginSecurityKeyFailure() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SECURITY_KEY_FAILURE); - } - - @Override public void trackLoginSecurityKeySuccess() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SECURITY_KEY_SUCCESS); - } - - @Override public void trackUseSecurityKeyClicked() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_SECURITY_KEY_CLICKED); - } - - @Override public void trackLogin2faNeeded() { - AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_2FA_NEEDED); - } } diff --git a/WordPress/src/main/java/org/wordpress/android/util/BuildConfigWrapper.kt b/WordPress/src/main/java/org/wordpress/android/util/BuildConfigWrapper.kt index 5911cf39ca0b..a4d455b8b890 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/BuildConfigWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/BuildConfigWrapper.kt @@ -27,8 +27,6 @@ class BuildConfigWrapper @Inject constructor() { val isSiteCreationEnabled = BuildConfig.ENABLE_SITE_CREATION - val isSignupEnabled = BuildConfig.ENABLE_SIGNUP - val isCreateFabEnabled = BuildConfig.ENABLE_CREATE_FAB val isFollowedSitesSettingsEnabled = BuildConfig.ENABLE_FOLLOWED_SITES_SETTINGS diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java index 730eb995fff8..7443625cb96a 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java @@ -479,37 +479,6 @@ public enum Stat { PAGES_SEARCH_ACCESSED("site_pages_search_accessed"), PAGES_EDIT_HOMEPAGE_INFO_PRESSED("site_pages_edit_homepage_info_pressed"), PAGES_EDIT_HOMEPAGE_ITEM_PRESSED("site_pages_edit_homepage_item_pressed"), - SIGNUP_BUTTON_TAPPED, - SIGNUP_EMAIL_BUTTON_TAPPED, - SIGNUP_EMAIL_EPILOGUE_UNCHANGED("signup_epilogue_unchanged"), - SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED("signup_epilogue_update_display_name_failed"), - SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED("signup_epilogue_update_display_name_succeeded"), - SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_FAILED("signup_epilogue_update_username_failed"), - SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED("signup_epilogue_update_username_succeeded"), - SIGNUP_EMAIL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED("signup_epilogue_username_suggestions_failed"), - SIGNUP_EMAIL_EPILOGUE_USERNAME_TAPPED("signup_epilogue_username_tapped"), - SIGNUP_EMAIL_EPILOGUE_VIEWED("signup_epilogue_viewed"), - SIGNUP_SOCIAL_BUTTON_TAPPED, - SIGNUP_TERMS_OF_SERVICE_TAPPED, - SIGNUP_CANCELED, - SIGNUP_EMAIL_TO_LOGIN, - SIGNUP_MAGIC_LINK_FAILED, - SIGNUP_MAGIC_LINK_OPENED, - SIGNUP_MAGIC_LINK_OPEN_EMAIL_CLIENT_CLICKED, - SIGNUP_MAGIC_LINK_SENT, - SIGNUP_MAGIC_LINK_SUCCEEDED, - SIGNUP_SOCIAL_ACCOUNTS_NEED_CONNECTING, - SIGNUP_SOCIAL_BUTTON_FAILURE, - SIGNUP_SOCIAL_EPILOGUE_UNCHANGED("signup_epilogue_unchanged"), - SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED("signup_epilogue_update_display_name_failed"), - SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED("signup_epilogue_update_display_name_succeeded"), - SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_FAILED("signup_epilogue_update_username_failed"), - SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED("signup_epilogue_update_username_succeeded"), - SIGNUP_SOCIAL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED("signup_epilogue_username_suggestions_failed"), - SIGNUP_SOCIAL_EPILOGUE_USERNAME_TAPPED("signup_epilogue_username_tapped"), - SIGNUP_SOCIAL_EPILOGUE_VIEWED("signup_epilogue_viewed"), - SIGNUP_SOCIAL_SUCCESS, - SIGNUP_SOCIAL_TO_LOGIN, ENHANCED_SITE_CREATION_ACCESSED, ENHANCED_SITE_CREATION_DOMAINS_ACCESSED, ENHANCED_SITE_CREATION_DOMAINS_SELECTED, diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java index 3ae85a14e06e..26cf275669c1 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTrackerNosara.java @@ -232,30 +232,6 @@ public void track(AnalyticsTracker.Stat stat, Map properties) { case COMMENT_QUICK_ACTION_APPROVED: predefinedEventProperties.put("is_quick_action", true); break; - case SIGNUP_EMAIL_EPILOGUE_UNCHANGED: - case SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED: - case SIGNUP_EMAIL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED: - case SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_FAILED: - case SIGNUP_EMAIL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED: - case SIGNUP_EMAIL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED: - case SIGNUP_EMAIL_EPILOGUE_USERNAME_TAPPED: - case SIGNUP_EMAIL_EPILOGUE_VIEWED: - predefinedEventProperties.put("source", "email"); - break; - case SIGNUP_SOCIAL_EPILOGUE_UNCHANGED: - case SIGNUP_SOCIAL_BUTTON_FAILURE: - case SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_FAILED: - case SIGNUP_SOCIAL_EPILOGUE_UPDATE_DISPLAY_NAME_SUCCEEDED: - case SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_FAILED: - case SIGNUP_SOCIAL_EPILOGUE_UPDATE_USERNAME_SUCCEEDED: - case SIGNUP_SOCIAL_EPILOGUE_USERNAME_SUGGESTIONS_FAILED: - case SIGNUP_SOCIAL_EPILOGUE_USERNAME_TAPPED: - case SIGNUP_SOCIAL_EPILOGUE_VIEWED: - predefinedEventProperties.put("source", "social"); - break; - case SIGNUP_SOCIAL_BUTTON_TAPPED: - predefinedEventProperties.put("source", "google"); - break; case READER_POST_SAVED_FROM_OTHER_POST_LIST: predefinedEventProperties.put("source", "other_post_list"); break; diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt b/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt index 954cae5bd21d..e1fc9584fbd5 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt @@ -1,88 +1,17 @@ package org.wordpress.android.login -import java.util.Locale - interface LoginAnalyticsListener { fun trackAnalyticsSignIn(isWpcomLogin: Boolean) - fun trackCreatedAccount(username: String?, email: String?, source: CreatedAccountSource) - fun trackEmailFormViewed() - fun trackInsertedInvalidUrl() fun trackLoginAccessed() - fun trackLoginAutofillCredentialsFilled() fun trackLoginAutofillCredentialsUpdated() - fun trackLoginFailed(errorContext: String?, errorType: String?, errorDescription: String?) - fun trackLoginForgotPasswordClicked() - fun trackLoginMagicLinkExited() fun trackLoginMagicLinkOpened() - fun trackLoginMagicLinkOpenEmailClientClicked() fun trackLoginMagicLinkSucceeded() - fun trackLoginSocial2faNeeded() - fun trackLoginSocialSuccess() - fun trackMagicLinkFailed(properties: Map) - fun trackSignupMagicLinkOpenEmailClientViewed() - fun trackLoginMagicLinkOpenEmailClientViewed() - fun trackMagicLinkRequested() - fun trackMagicLinkRequestFormViewed() - fun trackPasswordFormViewed(isSocialChallenge: Boolean) - fun trackSignupCanceled() - fun trackSignupEmailButtonTapped() - fun trackSignupEmailToLogin() - fun trackSignupGoogleButtonTapped() - fun trackSignupMagicLinkFailed() - fun trackSignupMagicLinkOpened() - fun trackSignupMagicLinkOpenEmailClientClicked() - fun trackSignupMagicLinkSent() - fun trackSignupMagicLinkSucceeded() - fun trackSignupSocialAccountsNeedConnecting() - fun trackSignupSocialButtonFailure() - fun trackSignupSocialToLogin() - fun trackSignupTermsOfServiceTapped() - fun trackSocialButtonStart() - fun trackSocialAccountsNeedConnecting() - fun trackSocialButtonClick() - fun trackSocialButtonFailure() - fun trackSocialConnectFailure() - fun trackSocialConnectSuccess() - fun trackSocialErrorUnknownUser() - fun trackSocialFailure(errorContext: String?, errorType: String?, errorDescription: String?) - fun trackTwoFactorFormViewed() fun trackUrlFormViewed() - fun trackUrlHelpScreenViewed() - fun trackUsernamePasswordFormViewed() - fun trackWpComBackgroundServiceUpdate(properties: Map) fun trackConnectedSiteInfoRequested(url: String?) fun trackConnectedSiteInfoFailed(url: String?, errorContext: String?, errorType: String?, errorDescription: String?) fun trackConnectedSiteInfoSucceeded(properties: Map) fun trackFailure(message: String?) - fun trackSendCodeWithTextClicked() - fun trackSubmit2faCodeClicked() fun trackSubmitClicked() - fun trackRequestMagicLinkClick() - fun trackLoginWithPasswordClick() fun trackShowHelpClick() - fun trackDismissDialog() - fun trackSelectEmailField() - fun trackPickEmailFromHint() - fun trackShowEmailHints() - fun emailFormScreenResumed() - fun trackSocialSignupConfirmationViewed() - fun trackCreateAccountClick() - fun emailPasswordFormScreenResumed() fun siteAddressFormScreenResumed() - fun magicLinkRequestScreenResumed() - fun magicLinkSentScreenResumed() - fun usernamePasswordScreenResumed() - fun trackLogin2faNeeded() - fun trackLoginSecurityKeySuccess() - fun trackLoginSecurityKeyFailure() - fun trackUseSecurityKeyClicked() - - enum class CreatedAccountSource { - EMAIL, - GOOGLE; - - fun asPropertyMap() = hashMapOf( - "source" to name.lowercase(Locale.ROOT) - ) - } } diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java index 06fc436ad0cb..a59f7e79b868 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginListener.java @@ -28,7 +28,4 @@ interface SelfSignedSSLCallback { // General post-login callbacks void startPostLoginServices(); - - // Signup - void showSignupToLoginMessage(); } From 5abf4610ccec20e8c2d0e5d1887eef5c477e3596 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:15:11 -0700 Subject: [PATCH 12/71] Remove `PostSignupInterstitialActivity` --- WordPress/src/main/AndroidManifest.xml | 6 - .../android/modules/ViewModelModule.java | 6 - .../android/ui/ActivityLauncher.java | 9 -- .../android/ui/accounts/LoginActivity.java | 7 +- .../PostSignupInterstitialActivity.kt | 91 ------------ .../ApplicationPasswordLoginActivity.kt | 3 - .../ApplicationPasswordLoginViewModel.kt | 8 - .../accounts/login/LoginCompletionUseCase.kt | 28 +--- .../wordpress/android/ui/prefs/AppPrefs.java | 11 -- .../android/ui/prefs/AppPrefsWrapper.kt | 4 - .../PostSignupInterstitialViewModel.kt | 93 ------------ .../post_signup_interstitial_activity.xml | 11 -- .../post_signup_interstitial_activity.xml | 11 -- .../post_signup_interstitial_activity.xml | 11 -- .../ApplicationPasswordLoginViewModelTest.kt | 76 +++------- .../login/LoginCompletionUseCaseTest.kt | 73 ++------- .../PostSignupInterstitialViewModelTest.kt | 140 ------------------ 17 files changed, 38 insertions(+), 550 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/PostSignupInterstitialActivity.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModel.kt delete mode 100644 WordPress/src/main/res/layout-land/post_signup_interstitial_activity.xml delete mode 100644 WordPress/src/main/res/layout-sw600dp/post_signup_interstitial_activity.xml delete mode 100644 WordPress/src/main/res/layout/post_signup_interstitial_activity.xml delete mode 100644 WordPress/src/test/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModelTest.kt diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 002573c4c3cb..7f5509972047 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -187,12 +187,6 @@ - - (R.id.create_new_site_button) - - private fun PostSignupInterstitialActivityBinding.addSelfHostedSiteButton() = - root.findViewById(R.id.add_self_hosted_site_button) - - private fun PostSignupInterstitialActivityBinding.dismissButton() = - root.findViewById(R.id.dismiss_button) - - private fun executeAction(navigationAction: NavigationAction) = when (navigationAction) { - START_SITE_CREATION_FLOW -> startSiteCreationFlow() - START_SITE_CONNECTION_FLOW -> startSiteConnectionFlow() - SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY -> showJetpackIndividualPluginOverlay() - DISMISS -> dismiss() - DISMISS_FOR_JETPACK_REMOVAL -> dismissForJetpackRemoval() - } - - private fun startSiteCreationFlow() { - ActivityLauncher.showMainActivityAndSiteCreationActivity(this, SiteCreationSource.SIGNUP_EPILOGUE) - finish() - } - - private fun startSiteConnectionFlow() { - ActivityLauncher.addSelfHostedSiteForResult(this) - finish() - } - - private fun dismiss() { - ActivityLauncher.viewReader(this) - finish() - } - - private fun dismissForJetpackRemoval() { - ActivityLauncher.viewMySite(this) - finish() - } - - private fun showJetpackIndividualPluginOverlay() { - WPJetpackIndividualPluginFragment.show(supportFragmentManager) - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt index 63bd1383f272..4aac9caa6791 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt @@ -75,9 +75,6 @@ class ApplicationPasswordLoginActivity: BaseAppCompatActivity() { if (navigationActionData.isError) { ActivityLauncher.showMainActivity(this) - } else if (navigationActionData.showPostSignupInterstitial) { - unifiedLoginTracker.setFlow(UnifiedLoginTracker.Flow.APPLICATION_PASSWORD.value) - ActivityLauncher.showPostSignupInterstitial(this) } else { unifiedLoginTracker.setFlow(UnifiedLoginTracker.Flow.APPLICATION_PASSWORD.value) val mainActivityIntent = Intent(this, WPMainActivity::class.java) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt index b529272367c4..a66928a4e455 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt @@ -19,7 +19,6 @@ import org.wordpress.android.login.util.SiteUtils import org.wordpress.android.modules.IO_THREAD import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.UriLogin -import org.wordpress.android.ui.prefs.AppPrefsWrapper import org.wordpress.android.util.AppLog import org.wordpress.android.util.UrlUtils import javax.inject.Inject @@ -32,7 +31,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( private val applicationPasswordLoginHelper: ApplicationPasswordLoginHelper, private val selfHostedEndpointFinder: SelfHostedEndpointFinder, private val siteStore: SiteStore, - private val appPrefsWrapper: AppPrefsWrapper, private val appLogWrapper: AppLogWrapper, ) : ViewModel() { private val _onFinishedEvent = MutableSharedFlow() @@ -66,7 +64,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( _onFinishedEvent.emit( NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = "", oldSitesIDs = oldSitesIDs, isError = true @@ -136,7 +133,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( private suspend fun emitErrorFetching(siteUrl: String) = _onFinishedEvent.emit( NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = siteUrl, oldSitesIDs = oldSitesIDs, isError = true @@ -154,7 +150,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( _onFinishedEvent.emit( NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = currentUrlLogin?.siteUrl, oldSitesIDs = oldSitesIDs, isError = true @@ -165,8 +160,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( NavigationActionData( showSiteSelector = siteStore.hasSite() && oldSitesIDs?.contains(site.id) != true, // null or false - showPostSignupInterstitial = !siteStore.hasSite() - && appPrefsWrapper.shouldShowPostSignupInterstitial, siteUrl = currentUrlLogin?.siteUrl, oldSitesIDs = oldSitesIDs, isError = false, @@ -179,7 +172,6 @@ class ApplicationPasswordLoginViewModel @Inject constructor( data class NavigationActionData( val showSiteSelector: Boolean, - val showPostSignupInterstitial: Boolean, val siteUrl: String?, val oldSitesIDs: ArrayList?, val isError: Boolean, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt index c34267789dc2..c0c107016964 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt @@ -1,16 +1,13 @@ package org.wordpress.android.ui.accounts.login import org.wordpress.android.login.LoginMode -import org.wordpress.android.ui.prefs.AppPrefs import javax.inject.Inject /** * Use case for determining the appropriate action after login completion. * This extracts testable logic from LoginActivity. */ -class LoginCompletionUseCase @Inject constructor( - private val appPrefsWrapper: AppPrefsWrapper -) { +class LoginCompletionUseCase @Inject constructor() { /** * Determines if we should wait for sites to load after login. * @@ -44,23 +41,13 @@ class LoginCompletionUseCase @Inject constructor( * Determines the navigation destination for the main activity flow. * * @param loginMode The current login mode - * @param hasSites Whether the user has any sites * @return The navigation destination */ - fun getMainNavigationDestination( - loginMode: LoginMode, - hasSites: Boolean - ): MainNavigationDestination { + fun getMainNavigationDestination(loginMode: LoginMode): MainNavigationDestination { return when (loginMode) { LoginMode.FULL, LoginMode.JETPACK_LOGIN_ONLY, - LoginMode.WPCOM_LOGIN_ONLY -> { - if (!hasSites && appPrefsWrapper.shouldShowPostSignupInterstitial()) { - MainNavigationDestination.POST_SIGNUP_INTERSTITIAL - } else { - MainNavigationDestination.MAIN_ACTIVITY - } - } + LoginMode.WPCOM_LOGIN_ONLY -> MainNavigationDestination.MAIN_ACTIVITY else -> MainNavigationDestination.FINISH_ONLY } } @@ -81,18 +68,9 @@ class LoginCompletionUseCase @Inject constructor( * Enum representing the specific navigation destination. */ enum class MainNavigationDestination { - /** Show the post-signup interstitial screen */ - POST_SIGNUP_INTERSTITIAL, /** Show the main activity */ MAIN_ACTIVITY, /** Just finish the activity */ FINISH_ONLY } } - -/** - * Wrapper around AppPrefs to allow for testing. - */ -class AppPrefsWrapper @Inject constructor() { - fun shouldShowPostSignupInterstitial(): Boolean = AppPrefs.shouldShowPostSignupInterstitial() -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java index 2c2f766eff36..8592d32737aa 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefs.java @@ -252,9 +252,6 @@ public enum UndeletablePrefKey implements PrefKey { // indicates whether the system notifications are enabled for the app SYSTEM_NOTIFICATIONS_ENABLED, - // Used to indicate whether or not the the post-signup interstitial must be shown - SHOULD_SHOW_POST_SIGNUP_INTERSTITIAL, - // used to indicate that we do not need to show the main FAB tooltip IS_MAIN_FAB_TOOLTIP_DISABLED, @@ -1098,14 +1095,6 @@ public static boolean getSystemNotificationsEnabled() { return getBoolean(UndeletablePrefKey.SYSTEM_NOTIFICATIONS_ENABLED, true); } - public static void setShouldShowPostSignupInterstitial(boolean shouldShow) { - setBoolean(UndeletablePrefKey.SHOULD_SHOW_POST_SIGNUP_INTERSTITIAL, shouldShow); - } - - public static boolean shouldShowPostSignupInterstitial() { - return getBoolean(UndeletablePrefKey.SHOULD_SHOW_POST_SIGNUP_INTERSTITIAL, true); - } - private static List getPostWithHWAccelerationOff() { String idsAsString = getString(DeletablePrefKey.AZTEC_EDITOR_DISABLE_HW_ACC_KEYS, ""); return Arrays.asList(idsAsString.split(",")); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt index 2706800222e8..9d1e5d6d7dbc 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/AppPrefsWrapper.kt @@ -56,10 +56,6 @@ class AppPrefsWrapper @Inject constructor(val buildConfigWrapper: BuildConfigWra get() = AppPrefs.getSystemNotificationsEnabled() set(value) = AppPrefs.setSystemNotificationsEnabled(value) - var shouldShowPostSignupInterstitial: Boolean - get() = AppPrefs.shouldShowPostSignupInterstitial() - set(shouldShow) = AppPrefs.setShouldShowPostSignupInterstitial(shouldShow) - var readerTagsUpdatedTimestamp: Long get() = AppPrefs.getReaderTagsUpdatedTimestamp() set(timestamp) = AppPrefs.setReaderTagsUpdatedTimestamp(timestamp) diff --git a/WordPress/src/main/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModel.kt b/WordPress/src/main/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModel.kt deleted file mode 100644 index 8f02a73cbf49..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModel.kt +++ /dev/null @@ -1,93 +0,0 @@ -package org.wordpress.android.viewmodel.accounts - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_ADD_SELF_HOSTED_SITE_TAPPED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_CREATE_NEW_SITE_TAPPED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_DISMISSED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_SHOWN -import org.wordpress.android.ui.accounts.UnifiedLoginTracker -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Click -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Step.SUCCESS -import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper -import org.wordpress.android.ui.jetpackoverlay.individualplugin.WPJetpackIndividualPluginHelper -import org.wordpress.android.ui.prefs.AppPrefsWrapper -import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.viewmodel.SingleLiveEvent -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.DISMISS -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.DISMISS_FOR_JETPACK_REMOVAL -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.START_SITE_CONNECTION_FLOW -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.START_SITE_CREATION_FLOW -import javax.inject.Inject - -class PostSignupInterstitialViewModel -@Inject constructor( - private val appPrefs: AppPrefsWrapper, - private val unifiedLoginTracker: UnifiedLoginTracker, - private val analyticsTracker: AnalyticsTrackerWrapper, - private val wpJetpackIndividualPluginHelper: WPJetpackIndividualPluginHelper, - private val jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper -) : ViewModel() { - val navigationAction: SingleLiveEvent = SingleLiveEvent() - - fun onInterstitialShown() { - analyticsTracker.track(WELCOME_NO_SITES_INTERSTITIAL_SHOWN) - unifiedLoginTracker.track(step = SUCCESS) - appPrefs.shouldShowPostSignupInterstitial = false - checkJetpackIndividualPluginOverlayShouldShow() - } - - fun onCreateNewSiteButtonPressed() { - analyticsTracker.track(WELCOME_NO_SITES_INTERSTITIAL_CREATE_NEW_SITE_TAPPED) - unifiedLoginTracker.trackClick(Click.CREATE_NEW_SITE) - navigationAction.value = START_SITE_CREATION_FLOW - } - - fun onAddSelfHostedSiteButtonPressed() { - analyticsTracker.track(WELCOME_NO_SITES_INTERSTITIAL_ADD_SELF_HOSTED_SITE_TAPPED) - unifiedLoginTracker.trackClick(Click.ADD_SELF_HOSTED_SITE) - navigationAction.value = START_SITE_CONNECTION_FLOW - } - - fun onDismissButtonPressed() = onDismiss() - - fun onBackButtonPressed() = onDismiss() - - private fun onDismiss() { - unifiedLoginTracker.trackClick(Click.DISMISS) - analyticsTracker.track(WELCOME_NO_SITES_INTERSTITIAL_DISMISSED) - if (jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()) { - navigationAction.value = DISMISS_FOR_JETPACK_REMOVAL - } else { - navigationAction.value = DISMISS - } - } - - private fun checkJetpackIndividualPluginOverlayShouldShow() { - // don't check if already shown - if (navigationAction.value == SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY) return - - viewModelScope.launch { - val showOverlay = wpJetpackIndividualPluginHelper.shouldShowJetpackIndividualPluginOverlay() - if (showOverlay) { - delay(DELAY_BEFORE_SHOWING_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY) - navigationAction.postValue(SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY) - } - } - } - - enum class NavigationAction { - START_SITE_CREATION_FLOW, - START_SITE_CONNECTION_FLOW, - DISMISS, - SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY, - DISMISS_FOR_JETPACK_REMOVAL - } - - companion object { - private const val DELAY_BEFORE_SHOWING_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY = 500L - } -} diff --git a/WordPress/src/main/res/layout-land/post_signup_interstitial_activity.xml b/WordPress/src/main/res/layout-land/post_signup_interstitial_activity.xml deleted file mode 100644 index f0e486ca355c..000000000000 --- a/WordPress/src/main/res/layout-land/post_signup_interstitial_activity.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/WordPress/src/main/res/layout-sw600dp/post_signup_interstitial_activity.xml b/WordPress/src/main/res/layout-sw600dp/post_signup_interstitial_activity.xml deleted file mode 100644 index 5f6f9d759687..000000000000 --- a/WordPress/src/main/res/layout-sw600dp/post_signup_interstitial_activity.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/WordPress/src/main/res/layout/post_signup_interstitial_activity.xml b/WordPress/src/main/res/layout/post_signup_interstitial_activity.xml deleted file mode 100644 index 5f6f9d759687..000000000000 --- a/WordPress/src/main/res/layout/post_signup_interstitial_activity.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModelTest.kt index 1c431773dd22..58ce3198859b 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModelTest.kt @@ -19,7 +19,6 @@ import org.mockito.kotlin.eq import org.wordpress.android.fluxc.Dispatcher import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.utils.AppLogWrapper -import org.wordpress.android.ui.prefs.AppPrefsWrapper import kotlin.test.assertEquals @ExperimentalCoroutinesApi @@ -37,16 +36,18 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { @Mock lateinit var siteStore: SiteStore - @Mock - lateinit var appPrefsWrapper: AppPrefsWrapper - @Mock lateinit var appLogWrapper: AppLogWrapper private lateinit var viewModel: ApplicationPasswordLoginViewModel private val rawData = "url=callback?site_url=https://example.com&user_login=user&password=pass" - private val urlLogin = ApplicationPasswordLoginHelper.UriLogin("https://example.com", "user", "pass", "https://example.com/json") + private val urlLogin = ApplicationPasswordLoginHelper.UriLogin( + "https://example.com", + "user", + "pass", + "https://example.com/json" + ) private val testSite = SiteModel().apply { apiRestUsernamePlain = urlLogin.user apiRestPasswordPlain = urlLogin.password @@ -62,7 +63,6 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { applicationPasswordLoginHelper, selfHostedEndpointFinder, siteStore, - appPrefsWrapper, appLogWrapper ) whenever(applicationPasswordLoginHelper.getSiteUrlLoginFromRawData(rawData)).thenReturn(urlLogin) @@ -74,7 +74,6 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { val emptyRawData = "" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = "", oldSitesIDs = null, isError = true @@ -101,7 +100,6 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { val malformedRawData = "malformed ray data" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = "", oldSitesIDs = null, isError = true @@ -129,12 +127,12 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { // Given val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = urlLogin.siteUrl, oldSitesIDs = null, isError = true ) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) + whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))) + .thenReturn(false) whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(any())).thenThrow(RuntimeException()) // When @@ -156,12 +154,12 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { val xmlRpcEndpoint = "https://example.com/xmlrpc.php" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = urlLogin.siteUrl, oldSitesIDs = null, isError = true ) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) + whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))) + .thenReturn(false) whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!)) .thenReturn(xmlRpcEndpoint) @@ -191,7 +189,6 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { val xmlRpcEndpoint = "https://example.com/xmlrpc.php" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = true, - showPostSignupInterstitial = false, siteUrl = urlLogin.siteUrl, oldSitesIDs = null, isError = false, @@ -199,7 +196,8 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { ) whenever(siteStore.hasSite()).thenReturn(true) whenever(siteStore.sites).thenReturn(listOf(testSite)) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) + whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))) + .thenReturn(false) whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!)) .thenReturn(xmlRpcEndpoint) @@ -223,13 +221,12 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { } @Test - fun `given intent rawData, when setup site and not able to store credentials but store fetch, then emit ok with no site selector nor interstitial`() = + fun `given intent rawData, when setup site and not able to store credentials but store fetch and no sites, then emit ok without site selector`() = runTest { // Given val xmlRpcEndpoint = "https://example.com/xmlrpc.php" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = false, - showPostSignupInterstitial = false, siteUrl = urlLogin.siteUrl, oldSitesIDs = null, isError = false, @@ -237,7 +234,8 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { ) whenever(siteStore.hasSite()).thenReturn(false) whenever(siteStore.sites).thenReturn(listOf(testSite)) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) + whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))) + .thenReturn(false) whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!)) .thenReturn(xmlRpcEndpoint) @@ -261,13 +259,12 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { } @Test - fun `given intent rawData, when setup site and not able to store credentials but store fetch, then emit ok with no interstitial by sites`() = + fun `given intent rawData, when setup site and not able to store credentials but store fetch with sites, then emit ok with site selector`() = runTest { // Given val xmlRpcEndpoint = "https://example.com/xmlrpc.php" val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( showSiteSelector = true, - showPostSignupInterstitial = false, siteUrl = urlLogin.siteUrl, oldSitesIDs = null, isError = false, @@ -275,7 +272,8 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { ) whenever(siteStore.hasSite()).thenReturn(true) whenever(siteStore.sites).thenReturn(listOf(testSite)) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) + whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))) + .thenReturn(false) whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!)) .thenReturn(xmlRpcEndpoint) @@ -290,44 +288,6 @@ class ApplicationPasswordLoginViewModelTest : BaseUnitTest() { ) ) - // Then - val finishedEvent = awaitItem() - assertEquals(expectedResult, finishedEvent) - verify(selfHostedEndpointFinder, times(1)).verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `given intent rawData, when setup site and not able to store credentials but store fetch, then emit ok with no interstitial by preferences`() = - runTest { - // Given - val xmlRpcEndpoint = "https://example.com/xmlrpc.php" - val expectedResult = ApplicationPasswordLoginViewModel.NavigationActionData( - showSiteSelector = false, - showPostSignupInterstitial = false, - siteUrl = urlLogin.siteUrl, - oldSitesIDs = null, - isError = false, - newSiteLocalId = testSite.id - ) - whenever(siteStore.sites).thenReturn(listOf(testSite)) - whenever(appPrefsWrapper.shouldShowPostSignupInterstitial).thenReturn(false) - whenever(applicationPasswordLoginHelper.storeApplicationPasswordCredentialsFrom(eq(urlLogin))).thenReturn(false) - whenever(selfHostedEndpointFinder.verifyOrDiscoverXMLRPCEndpoint(urlLogin.siteUrl!!)) - .thenReturn(xmlRpcEndpoint) - - // When - viewModel.onFinishedEvent.test { - viewModel.setupSite(rawData) - // Mock onSiteChanged event - viewModel.onSiteChanged( - SiteStore.OnSiteChanged( - rowsAffected = 1, - updatedSites = listOf(testSite) - ) - ) - // Then val finishedEvent = awaitItem() assertEquals(expectedResult, finishedEvent) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt index fca06d82214c..894d6b88ebcb 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt @@ -4,9 +4,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import org.assertj.core.api.Assertions.assertThat import org.junit.Before import org.junit.Test -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.kotlin.whenever import org.wordpress.android.BaseUnitTest import org.wordpress.android.login.LoginMode import org.wordpress.android.ui.accounts.login.LoginCompletionUseCase.LoginCompletionAction @@ -14,15 +11,11 @@ import org.wordpress.android.ui.accounts.login.LoginCompletionUseCase.MainNaviga @ExperimentalCoroutinesApi class LoginCompletionUseCaseTest : BaseUnitTest() { - @Mock - lateinit var appPrefsWrapper: AppPrefsWrapper - private lateinit var useCase: LoginCompletionUseCase @Before fun setUp() { - MockitoAnnotations.openMocks(this) - useCase = LoginCompletionUseCase(appPrefsWrapper) + useCase = LoginCompletionUseCase() } // region shouldWaitForSitesToLoad tests @@ -141,114 +134,78 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { // region getMainNavigationDestination tests @Test - fun `given FULL mode with sites, then destination is MAIN_ACTIVITY`() { - val result = useCase.getMainNavigationDestination(LoginMode.FULL, hasSites = true) - - assertThat(result).isEqualTo(MainNavigationDestination.MAIN_ACTIVITY) - } - - @Test - fun `given FULL mode without sites and should show interstitial, then destination is POST_SIGNUP_INTERSTITIAL`() { - whenever(appPrefsWrapper.shouldShowPostSignupInterstitial()).thenReturn(true) - - val result = useCase.getMainNavigationDestination(LoginMode.FULL, hasSites = false) - - assertThat(result).isEqualTo(MainNavigationDestination.POST_SIGNUP_INTERSTITIAL) - } - - @Test - fun `given FULL mode without sites and should not show interstitial, then destination is MAIN_ACTIVITY`() { - whenever(appPrefsWrapper.shouldShowPostSignupInterstitial()).thenReturn(false) - - val result = useCase.getMainNavigationDestination(LoginMode.FULL, hasSites = false) + fun `given FULL mode, then destination is MAIN_ACTIVITY`() { + val result = useCase.getMainNavigationDestination(LoginMode.FULL) assertThat(result).isEqualTo(MainNavigationDestination.MAIN_ACTIVITY) } @Test - fun `given JETPACK_LOGIN_ONLY mode with sites, then destination is MAIN_ACTIVITY`() { - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_LOGIN_ONLY, hasSites = true) + fun `given JETPACK_LOGIN_ONLY mode, then destination is MAIN_ACTIVITY`() { + val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_LOGIN_ONLY) assertThat(result).isEqualTo(MainNavigationDestination.MAIN_ACTIVITY) } @Test - fun `given JETPACK_LOGIN_ONLY without sites and should show interstitial, then POST_SIGNUP_INTERSTITIAL`() { - whenever(appPrefsWrapper.shouldShowPostSignupInterstitial()).thenReturn(true) - - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_LOGIN_ONLY, hasSites = false) - - assertThat(result).isEqualTo(MainNavigationDestination.POST_SIGNUP_INTERSTITIAL) - } - - @Test - fun `given WPCOM_LOGIN_ONLY mode with sites, then destination is MAIN_ACTIVITY`() { - val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_LOGIN_ONLY, hasSites = true) + fun `given WPCOM_LOGIN_ONLY mode, then destination is MAIN_ACTIVITY`() { + val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_LOGIN_ONLY) assertThat(result).isEqualTo(MainNavigationDestination.MAIN_ACTIVITY) } - @Test - fun `given WPCOM_LOGIN_ONLY without sites and should show interstitial, then POST_SIGNUP_INTERSTITIAL`() { - whenever(appPrefsWrapper.shouldShowPostSignupInterstitial()).thenReturn(true) - - val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_LOGIN_ONLY, hasSites = false) - - assertThat(result).isEqualTo(MainNavigationDestination.POST_SIGNUP_INTERSTITIAL) - } - @Test fun `given SELFHOSTED_ONLY mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.SELFHOSTED_ONLY, hasSites = false) + val result = useCase.getMainNavigationDestination(LoginMode.SELFHOSTED_ONLY) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given JETPACK_SELFHOSTED mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_SELFHOSTED, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_SELFHOSTED) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given SHARE_INTENT mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.SHARE_INTENT, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.SHARE_INTENT) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given WOO_LOGIN_MODE, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.WOO_LOGIN_MODE, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.WOO_LOGIN_MODE) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given JETPACK_STATS mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_STATS, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_STATS) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given JETPACK_REST_CONNECT mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_REST_CONNECT, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_REST_CONNECT) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given WPCOM_LOGIN_DEEPLINK mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_LOGIN_DEEPLINK, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_LOGIN_DEEPLINK) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } @Test fun `given WPCOM_REAUTHENTICATE mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_REAUTHENTICATE, hasSites = true) + val result = useCase.getMainNavigationDestination(LoginMode.WPCOM_REAUTHENTICATE) assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } diff --git a/WordPress/src/test/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModelTest.kt deleted file mode 100644 index d61e834df55f..000000000000 --- a/WordPress/src/test/java/org/wordpress/android/viewmodel/accounts/PostSignupInterstitialViewModelTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -package org.wordpress.android.viewmodel.accounts - -import androidx.lifecycle.Observer -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.assertj.core.api.Assertions.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import org.wordpress.android.BaseUnitTest -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_ADD_SELF_HOSTED_SITE_TAPPED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_CREATE_NEW_SITE_TAPPED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_DISMISSED -import org.wordpress.android.analytics.AnalyticsTracker.Stat.WELCOME_NO_SITES_INTERSTITIAL_SHOWN -import org.wordpress.android.ui.accounts.UnifiedLoginTracker -import org.wordpress.android.ui.jetpackoverlay.JetpackFeatureRemovalPhaseHelper -import org.wordpress.android.ui.jetpackoverlay.individualplugin.WPJetpackIndividualPluginHelper -import org.wordpress.android.ui.prefs.AppPrefsWrapper -import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.DISMISS -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.DISMISS_FOR_JETPACK_REMOVAL -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.START_SITE_CONNECTION_FLOW -import org.wordpress.android.viewmodel.accounts.PostSignupInterstitialViewModel.NavigationAction.START_SITE_CREATION_FLOW - -@ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) -class PostSignupInterstitialViewModelTest : BaseUnitTest() { - private val appPrefs: AppPrefsWrapper = mock() - private val unifiedLoginTracker: UnifiedLoginTracker = mock() - private val analyticsTracker: AnalyticsTrackerWrapper = mock() - private val wpJetpackIndividualPluginHelper: WPJetpackIndividualPluginHelper = mock() - private val observer: Observer = mock() - private val jetpackFeatureRemovalPhaseHelper: JetpackFeatureRemovalPhaseHelper = mock() - - private lateinit var viewModel: PostSignupInterstitialViewModel - - @Before - fun setUp() { - viewModel = PostSignupInterstitialViewModel( - appPrefs, - unifiedLoginTracker, - analyticsTracker, - wpJetpackIndividualPluginHelper, - jetpackFeatureRemovalPhaseHelper - ) - viewModel.navigationAction.observeForever(observer) - whenever(jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()).thenReturn(false) - } - - @Test - fun `when interstitial is shown should update preference value`() = test { - whenever(wpJetpackIndividualPluginHelper.shouldShowJetpackIndividualPluginOverlay()).thenReturn(false) - - viewModel.onInterstitialShown() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_SHOWN) - verify(appPrefs).shouldShowPostSignupInterstitial = false - } - - @Test - fun `given overlay should show when interstitial is shown then show jetpack individual plugin overlay`() = test { - whenever(wpJetpackIndividualPluginHelper.shouldShowJetpackIndividualPluginOverlay()).thenReturn(true) - - viewModel.onInterstitialShown() - advanceUntilIdle() - - assertThat(viewModel.navigationAction.value).isEqualTo(NavigationAction.SHOW_JETPACK_INDIVIDUAL_PLUGIN_OVERLAY) - } - - @Test - fun `given overlay should not show when interstitial is shown then don't show jetpack individual plugin overlay`() = - test { - whenever(wpJetpackIndividualPluginHelper.shouldShowJetpackIndividualPluginOverlay()).thenReturn(false) - - viewModel.onInterstitialShown() - advanceUntilIdle() - - assertThat(viewModel.navigationAction.value).isNull() - } - - @Test - fun `when create new site button is pressed should start site creation flow`() { - viewModel.onCreateNewSiteButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_CREATE_NEW_SITE_TAPPED) - verify(observer).onChanged(START_SITE_CREATION_FLOW) - } - - @Test - fun `when add self hosted site button is pressed should start site connection flow`() { - viewModel.onAddSelfHostedSiteButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_ADD_SELF_HOSTED_SITE_TAPPED) - verify(observer).onChanged(START_SITE_CONNECTION_FLOW) - } - - @Test - fun `given showJPFeatures should not show, when dismissal button is pressed should dismiss`() { - whenever(jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()).thenReturn(false) - - viewModel.onDismissButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_DISMISSED) - verify(observer).onChanged(DISMISS) - } - - @Test - fun `given showJPFeatures should not show, when back button is pressed should dismiss`() { - whenever(jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()).thenReturn(false) - - viewModel.onBackButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_DISMISSED) - verify(observer).onChanged(DISMISS) - } - - @Test - fun `given showJPFeatures should show, when dismissal button is pressed should dismiss for jetpack removal`() { - whenever(jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()).thenReturn(true) - - viewModel.onDismissButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_DISMISSED) - verify(observer).onChanged(DISMISS_FOR_JETPACK_REMOVAL) - } - - @Test - fun `given showJPFeatures should show, when back button is pressed should dismiss for jetpack removal`() { - whenever(jetpackFeatureRemovalPhaseHelper.shouldRemoveJetpackFeatures()).thenReturn(true) - - viewModel.onBackButtonPressed() - - verify(analyticsTracker).track(WELCOME_NO_SITES_INTERSTITIAL_DISMISSED) - verify(observer).onChanged(DISMISS_FOR_JETPACK_REMOVAL) - } -} From 0b24d0663d48981904bf8c17219bd099558a42f0 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 3 Feb 2026 19:55:29 -0700 Subject: [PATCH 13/71] Remove SmartLock --- .../wordpress/android/ui/RequestCodes.java | 2 - .../android/ui/accounts/LoginActivity.java | 203 +----------------- .../android/ui/accounts/SmartLockHelper.java | 155 ------------- 3 files changed, 7 insertions(+), 353 deletions(-) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java diff --git a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java index f6a181cf0e29..f37eec2e73a6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java @@ -21,8 +21,6 @@ public class RequestCodes { public static final int STOCK_MEDIA_PICKER_SINGLE_SELECT = 1202; public static final int STOCK_MEDIA_PICKER_SINGLE_SELECT_FOR_GUTENBERG_BLOCK = 1203; public static final int SITE_ICON_PICKER = 1205; - public static final int SMART_LOCK_SAVE = 1400; - public static final int SMART_LOCK_READ = 1500; public static final int NOTIFICATION_SETTINGS = 1600; public static final int ACTIVITY_LOG_DETAIL = 1700; public static final int BACKUP_DOWNLOAD = 1710; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 21d459c2ce4b..ffe72fe0cfc0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -15,12 +15,6 @@ import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.ViewModelProvider; -import com.google.android.gms.auth.api.credentials.Credential; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; -import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; -import com.google.android.material.snackbar.Snackbar; - import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.R; @@ -41,17 +35,11 @@ import org.wordpress.android.login.LoginListener; import org.wordpress.android.login.LoginMode; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; -import org.wordpress.android.support.SupportWebViewActivity; import org.wordpress.android.support.ZendeskExtraTags; -import org.wordpress.android.support.ZendeskHelper; import org.wordpress.android.ui.ActivityLauncher; -import org.wordpress.android.ui.JetpackConnectionSource; -import org.wordpress.android.ui.RequestCodes; import org.wordpress.android.ui.accounts.HelpActivity.Origin; import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowNoJetpackSites; import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowSiteAddressError; -import org.wordpress.android.ui.accounts.SmartLockHelper.Callback; -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Click; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Flow; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Source; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Step; @@ -66,7 +54,6 @@ import org.wordpress.android.ui.main.BaseAppCompatActivity; import org.wordpress.android.ui.main.ChooseSiteActivity; import org.wordpress.android.ui.notifications.services.NotificationsUpdateServiceStarter; -import org.wordpress.android.ui.posts.BasicFragmentDialog; import org.wordpress.android.ui.posts.BasicFragmentDialog.BasicDialogPositiveClickInterface; import org.wordpress.android.ui.prefs.AppPrefs; import org.wordpress.android.ui.prefs.experimentalfeatures.ExperimentalFeatures; @@ -74,14 +61,9 @@ import org.wordpress.android.ui.reader.services.update.ReaderUpdateServiceStarter; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.AppLog.T; -import org.wordpress.android.util.BuildConfigWrapper; import org.wordpress.android.util.SelfSignedSSLUtils; import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.ToastUtils.Duration; -import org.wordpress.android.util.WPActivityUtils; -import org.wordpress.android.util.WPUrlUtils; -import org.wordpress.android.util.config.ContactSupportFeatureConfig; -import org.wordpress.android.widgets.WPSnackbar; import java.util.ArrayList; import java.util.Collections; @@ -98,40 +80,21 @@ import static org.wordpress.android.util.ActivityUtils.hideKeyboard; @AndroidEntryPoint -public class LoginActivity extends BaseAppCompatActivity implements ConnectionCallbacks, OnConnectionFailedListener, - Callback, LoginListener, LoginPrologueListener, +public class LoginActivity extends BaseAppCompatActivity implements LoginListener, LoginPrologueListener, HasAndroidInjector, BasicDialogPositiveClickInterface { public static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; public static final String MAGIC_LOGIN = "magic-login"; public static final String TOKEN_PARAMETER = "token"; - private static final String KEY_SMARTLOCK_HELPER_STATE = "KEY_SMARTLOCK_HELPER_STATE"; - private static final String KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE = "KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE"; private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; - private enum SmartLockHelperState { - NOT_TRIGGERED, - TRIGGER_FILL_IN_ON_CONNECT, - FINISH_ON_CONNECT, - FINISHED - } - - private SmartLockHelper mSmartLockHelper; - private SmartLockHelperState mSmartLockHelperState = SmartLockHelperState.NOT_TRIGGERED; - private JetpackConnectionSource mJetpackConnectSource; - private boolean mIsJetpackConnect; - - private boolean mIsSmartLockTriggeredFromPrologue; - private boolean mIsSiteLoginAvailableFromPrologue; - private LoginMode mLoginMode; private LoginViewModel mViewModel; @Inject protected WPcomLoginHelper mLoginHelper; @Inject DispatchingAndroidInjector mDispatchingAndroidInjector; @Inject protected LoginAnalyticsListener mLoginAnalyticsListener; - @Inject ZendeskHelper mZendeskHelper; @Inject UnifiedLoginTracker mUnifiedLoginTracker; @Inject protected SiteStore mSiteStore; @Inject protected AccountStore mAccountStore; @@ -141,8 +104,6 @@ private enum SmartLockHelperState { // Flag to track when we're waiting for account/sites to load after OAuth login private boolean mIsWaitingForSitesToLoad = false; private ArrayList mOldSitesIdsForLoginUpdate; - @Inject BuildConfigWrapper mBuildConfigWrapper; - @Inject ContactSupportFeatureConfig mContactSupportFeatureConfig; @Inject ExperimentalFeatures mExperimentalFeatures; @Inject LoginCompletionUseCase mLoginCompletionUseCase; @@ -177,23 +138,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(R.layout.login_activity); if (savedInstanceState == null) { - if (getIntent() != null) { - mJetpackConnectSource = - (JetpackConnectionSource) getIntent().getSerializableExtra(ARG_JETPACK_CONNECT_SOURCE); - } - mLoginAnalyticsListener.trackLoginAccessed(); switch (loginMode) { case FULL: case JETPACK_LOGIN_ONLY: mUnifiedLoginTracker.setSource(Source.DEFAULT); - loginFromPrologue(); + showFragment(new LoginPrologueRevampedFragment(), LoginPrologueRevampedFragment.TAG); break; case WPCOM_LOGIN_ONLY: case JETPACK_REST_CONNECT: mUnifiedLoginTracker.setSource(Source.ADD_WORDPRESS_COM_ACCOUNT); - checkSmartLockPasswordAndStartLogin(); + showWPcomLoginScreen(this); break; case JETPACK_SELFHOSTED: case SELFHOSTED_ONLY: @@ -202,33 +158,24 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { break; case JETPACK_STATS: mUnifiedLoginTracker.setSource(Source.JETPACK); - checkSmartLockPasswordAndStartLogin(); + showWPcomLoginScreen(this); break; case WPCOM_LOGIN_DEEPLINK: mUnifiedLoginTracker.setSource(Source.DEEPLINK); - checkSmartLockPasswordAndStartLogin(); + showWPcomLoginScreen(this); break; case WPCOM_REAUTHENTICATE: mUnifiedLoginTracker.setSource(Source.REAUTHENTICATION); - showWPcomLoginScreen(getBaseContext()); + showWPcomLoginScreen(this); break; case SHARE_INTENT: mUnifiedLoginTracker.setSource(Source.SHARE); - checkSmartLockPasswordAndStartLogin(); + showWPcomLoginScreen(this); break; case WOO_LOGIN_MODE: break; } } else { - mSmartLockHelperState = SmartLockHelperState.valueOf( - savedInstanceState.getString(KEY_SMARTLOCK_HELPER_STATE)); - - if (mSmartLockHelperState != SmartLockHelperState.NOT_TRIGGERED) { - // reconnect SmartLockHelper - initSmartLockHelperConnection(); - } - - mIsSiteLoginAvailableFromPrologue = savedInstanceState.getBoolean(KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE); String source = savedInstanceState.getString(KEY_UNIFIED_TRACKER_SOURCE); if (source != null) { mUnifiedLoginTracker.setSource(source); @@ -253,18 +200,9 @@ private void initViewModel() { }); } - private void loginFromPrologue() { - showFragment(new LoginPrologueRevampedFragment(), LoginPrologueRevampedFragment.TAG); - mIsSmartLockTriggeredFromPrologue = true; - mIsSiteLoginAvailableFromPrologue = true; - initSmartLockIfNotFinished(true); - } - @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(KEY_SMARTLOCK_HELPER_STATE, mSmartLockHelperState.name()); - outState.putBoolean(KEY_SITE_LOGIN_AVAILABLE_FROM_PROLOGUE, mIsSiteLoginAvailableFromPrologue); outState.putString(KEY_UNIFIED_TRACKER_SOURCE, mUnifiedLoginTracker.getSource().getValue()); Flow flow = mUnifiedLoginTracker.getFlow(); if (flow != null) { @@ -356,11 +294,6 @@ private void slideInFragment(Fragment fragment, boolean shouldAddToBackStack, St fragmentTransaction.commitAllowingStateLoss(); } - private LoginPrologueRevampedFragment getLoginPrologueRevampedFragment() { - Fragment fragment = getSupportFragmentManager().findFragmentByTag(LoginPrologueRevampedFragment.TAG); - return fragment == null ? null : (LoginPrologueRevampedFragment) fragment; - } - @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { if (item.getItemId() == android.R.id.home) { @@ -438,74 +371,6 @@ private void finishWithNewlyAddedSiteId(ArrayList oldSitesIds) { finish(); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - AppLog.d(T.MAIN, "LoginActivity: onActivity Result - requestCode" + requestCode); - super.onActivityResult(requestCode, resultCode, data); - - switch (requestCode) { - case RequestCodes.SMART_LOCK_SAVE: - if (resultCode == RESULT_OK) { - mLoginAnalyticsListener.trackLoginAutofillCredentialsUpdated(); - AppLog.d(AppLog.T.NUX, "Credentials saved"); - } else { - AppLog.d(AppLog.T.NUX, "Credentials save cancelled"); - } - break; - case RequestCodes.SMART_LOCK_READ: - if (resultCode == RESULT_OK) { - AppLog.d(AppLog.T.NUX, "Credentials retrieved"); - Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY); - onCredentialRetrieved(credential); - } else { - AppLog.e(AppLog.T.NUX, "Credential read failed"); - onCredentialsUnavailable(); - } - break; - } - } - - private boolean initSmartLockHelperConnection() { - mSmartLockHelper = new SmartLockHelper(this); - return mSmartLockHelper.initSmartLockForPasswords(); - } - - private void checkSmartLockPasswordAndStartLogin() { - initSmartLockIfNotFinished(true); - - if (mSmartLockHelperState == SmartLockHelperState.FINISHED) { - startLogin(); - } - } - - /** - * @param triggerFillInOnConnect set to true, if you want to show an account chooser dialog when the user has - * stored their credentials in the past. Set to false, if you just want to - * initialize SmartLock eg. when you want to use it just to save users credentials. - */ - private void initSmartLockIfNotFinished(boolean triggerFillInOnConnect) { - if (mSmartLockHelperState == SmartLockHelperState.NOT_TRIGGERED) { - if (initSmartLockHelperConnection()) { - if (triggerFillInOnConnect) { - mSmartLockHelperState = SmartLockHelperState.TRIGGER_FILL_IN_ON_CONNECT; - } else { - mSmartLockHelperState = SmartLockHelperState.FINISH_ON_CONNECT; - } - } else { - // just shortcircuit the attempt to use SmartLockHelper - mSmartLockHelperState = SmartLockHelperState.FINISHED; - } - } - } - - private void startLogin() { - if (getLoginMode() == LoginMode.JETPACK_STATS) { - mIsJetpackConnect = true; - } - // Use web-based WP.com login - showWPcomLoginScreen(this); - } - // LoginPrologueListener implementation methods public void showWPcomLoginScreen(@NonNull Context context) { @@ -580,60 +445,6 @@ public void startPostLoginServices() { NotificationsUpdateServiceStarter.startService(getApplicationContext()); } - // SmartLock - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - AppLog.d(AppLog.T.NUX, "Connection result: " + connectionResult); - mSmartLockHelperState = SmartLockHelperState.FINISHED; - } - - @Override - public void onConnected(Bundle bundle) { - AppLog.d(AppLog.T.NUX, "Google API client connected"); - - switch (mSmartLockHelperState) { - case NOT_TRIGGERED: - // should not reach this state here! - throw new RuntimeException("Internal inconsistency error!"); - case TRIGGER_FILL_IN_ON_CONNECT: - mSmartLockHelperState = SmartLockHelperState.FINISHED; - - // force account chooser - mSmartLockHelper.disableAutoSignIn(); - - mSmartLockHelper.smartLockAutoFill(this); - break; - case FINISH_ON_CONNECT: - mSmartLockHelperState = SmartLockHelperState.FINISHED; - break; - case FINISHED: - // don't do anything special. We're reconnecting the GoogleApiClient on rotation. - break; - } - } - - @Override - public void onCredentialRetrieved(Credential credential) { - // Smart Lock credentials can no longer be used for WP.com login (now web-based) - mSmartLockHelperState = SmartLockHelperState.FINISHED; - startLogin(); - } - - @Override - public void onCredentialsUnavailable() { - mSmartLockHelperState = SmartLockHelperState.FINISHED; - if (mIsSmartLockTriggeredFromPrologue) { - return; - } - startLogin(); - } - - @Override - public void onConnectionSuspended(int i) { - AppLog.d(AppLog.T.NUX, "Google API client connection suspended"); - } - @Override public void onPositiveClicked(@NonNull String instanceTag) { // No dialog tags currently handled diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java deleted file mode 100644 index e0d0b0fe1278..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/SmartLockHelper.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.wordpress.android.ui.accounts; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.IntentSender; -import android.net.Uri; -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.FragmentActivity; - -import com.google.android.gms.auth.api.Auth; -import com.google.android.gms.auth.api.credentials.Credential; -import com.google.android.gms.auth.api.credentials.CredentialRequest; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.api.CommonStatusCodes; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; -import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; - -import org.wordpress.android.ui.RequestCodes; -import org.wordpress.android.util.AppLog; -import org.wordpress.android.util.AppLog.T; - -import java.lang.ref.WeakReference; - -public class SmartLockHelper { - private GoogleApiClient mCredentialsClient; - private WeakReference mActivity; - - public interface Callback { - void onCredentialRetrieved(Credential credential); - - void onCredentialsUnavailable(); - } - - public SmartLockHelper(@NonNull FragmentActivity activity) { - if (activity instanceof OnConnectionFailedListener && activity instanceof ConnectionCallbacks) { - mActivity = new WeakReference<>(activity); - } else { - throw new RuntimeException("SmartLockHelper constructor needs an activity that " - + "implements OnConnectionFailedListener and ConnectionCallbacks"); - } - } - - private FragmentActivity getActivityAndCheckAvailability() { - FragmentActivity activity = mActivity.get(); - if (activity == null) { - return null; - } - int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(activity); - if (status == ConnectionResult.SUCCESS) { - return activity; - } - return null; - } - - public boolean initSmartLockForPasswords() { - FragmentActivity activity = getActivityAndCheckAvailability(); - if (activity == null) { - return false; - } - mCredentialsClient = new GoogleApiClient.Builder(activity) - .addConnectionCallbacks((ConnectionCallbacks) activity) - .enableAutoManage(activity, (OnConnectionFailedListener) activity) - .addApi(Auth.CREDENTIALS_API) - .build(); - return true; - } - - public void smartLockAutoFill(@NonNull final Callback callback) { - Activity activity = getActivityAndCheckAvailability(); - if (activity == null || mCredentialsClient == null || !mCredentialsClient.isConnected()) { - return; - } - CredentialRequest credentialRequest = new CredentialRequest.Builder() - .setPasswordLoginSupported(true) - .build(); - Auth.CredentialsApi.request(mCredentialsClient, credentialRequest).setResultCallback( - result -> { - Activity currentActivity = getActivityAndCheckAvailability(); - if (currentActivity == null) { - return; - } - Status status = result.getStatus(); - if (status.isSuccess()) { - Credential credential = result.getCredential(); - callback.onCredentialRetrieved(credential); - } else { - if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) { - try { - // Prompt the user to choose a saved credential - status.startResolutionForResult(currentActivity, RequestCodes.SMART_LOCK_READ); - } catch (IntentSender.SendIntentException e) { - AppLog.d(T.NUX, "SmartLock: Failed to send resolution for credential request"); - - callback.onCredentialsUnavailable(); - } - } else { - // The user must create an account or log in manually. - AppLog.d(T.NUX, "SmartLock: Unsuccessful credential request."); - - callback.onCredentialsUnavailable(); - } - } - }); - } - - - public void saveCredentialsInSmartLock(@NonNull final String username, @NonNull final String password, - @NonNull final String displayName, @Nullable final Uri profilePicture) { - // need username and password fields for Smart Lock - // https://github.com/wordpress-mobile/WordPress-Android/issues/5850 - if (TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) { - AppLog.i(T.MAIN, String.format( - "Cannot save Smart Lock credentials, username (%s) or password (%s) is empty", username, password)); - return; - } - - Activity activity = getActivityAndCheckAvailability(); - if (activity == null || mCredentialsClient == null || !mCredentialsClient.isConnected()) { - return; - } - Credential credential = new Credential.Builder(username).setPassword(password) - .setName(displayName) - .setProfilePictureUri(profilePicture).build(); - Auth.CredentialsApi.save(mCredentialsClient, credential).setResultCallback( - new ResultCallback() { - @Override - @SuppressLint("VisibleForTests") - public void onResult(@NonNull Status status) { - if (!status.isSuccess() && status.hasResolution()) { - try { - Activity activity = getActivityAndCheckAvailability(); - if (activity == null) { - return; - } - // This prompt the user to resolve the save request - status.startResolutionForResult(activity, RequestCodes.SMART_LOCK_SAVE); - } catch (IntentSender.SendIntentException e) { - // Could not resolve the request - } - } - } - }); - } - - public void disableAutoSignIn() { - Auth.CredentialsApi.disableAutoSignIn(mCredentialsClient); - } -} From 0fbfb1dd47a7e640286dfe180819cd82d68f7290 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 11:11:54 -0700 Subject: [PATCH 14/71] Remove `WOO_LOGIN_MODE` --- .../android/ui/accounts/LoginActivity.java | 2 -- .../ui/accounts/login/LoginCompletionUseCase.kt | 2 -- .../accounts/login/LoginCompletionUseCaseTest.kt | 14 -------------- .../android/login/LoginBaseFormFragment.java | 6 +----- .../org/wordpress/android/login/LoginMode.java | 3 +-- .../wordpress/android/login/util/SiteUtils.java | 5 ++--- 6 files changed, 4 insertions(+), 28 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index ffe72fe0cfc0..9fd650cc9b17 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -172,8 +172,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mUnifiedLoginTracker.setSource(Source.SHARE); showWPcomLoginScreen(this); break; - case WOO_LOGIN_MODE: - break; } } else { String source = savedInstanceState.getString(KEY_UNIFIED_TRACKER_SOURCE); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt index c0c107016964..42dc65f0180c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt @@ -31,8 +31,6 @@ class LoginCompletionUseCase @Inject constructor() { LoginMode.JETPACK_SELFHOSTED, LoginMode.SELFHOSTED_ONLY -> LoginCompletionAction.FINISH_WITH_NEW_SITE - LoginMode.WOO_LOGIN_MODE -> LoginCompletionAction.FINISH_ONLY - else -> LoginCompletionAction.NAVIGATE_TO_MAIN } } diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt index 894d6b88ebcb..31fe3571ccee 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt @@ -94,13 +94,6 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { assertThat(result).isEqualTo(LoginCompletionAction.FINISH_WITH_NEW_SITE) } - @Test - fun `given WOO_LOGIN_MODE, then action is FINISH_ONLY`() { - val result = useCase.getLoginCompletionAction(LoginMode.WOO_LOGIN_MODE) - - assertThat(result).isEqualTo(LoginCompletionAction.FINISH_ONLY) - } - @Test fun `given JETPACK_STATS mode, then action is NAVIGATE_TO_MAIN`() { val result = useCase.getLoginCompletionAction(LoginMode.JETPACK_STATS) @@ -175,13 +168,6 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } - @Test - fun `given WOO_LOGIN_MODE, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.WOO_LOGIN_MODE) - - assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) - } - @Test fun `given JETPACK_STATS mode, then destination is FINISH_ONLY`() { val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_STATS) diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java index b154f45a46b9..b19d816473b7 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java @@ -327,7 +327,7 @@ public void onAccountChanged(OnAccountChanged event) { } else if (event.causeOfChange == AccountAction.FETCH_SETTINGS) { // The user's account settings have also been fetched and stored - now we can fetch the user's sites FetchSitesPayload payload = - SiteUtils.getFetchSitesPayload(isJetpackAppLogin(), isWooAppLogin()); + SiteUtils.getFetchSitesPayload(isJetpackAppLogin()); mDispatcher.dispatch(SiteActionBuilder.newFetchSitesAction(payload)); } } @@ -339,10 +339,6 @@ protected boolean isJetpackAppLogin() { return mode == LoginMode.JETPACK_LOGIN_ONLY || mode == LoginMode.JETPACK_SELFHOSTED; } - protected boolean isWooAppLogin() { - return (mLoginListener instanceof LoginListener) - && ((LoginListener) mLoginListener).getLoginMode() == LoginMode.WOO_LOGIN_MODE; - } @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java index 7be20419fd88..10c8e41bc7f4 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java @@ -12,8 +12,7 @@ public enum LoginMode { JETPACK_REST_CONNECT, WPCOM_LOGIN_DEEPLINK, WPCOM_REAUTHENTICATE, - SHARE_INTENT, - WOO_LOGIN_MODE; + SHARE_INTENT; private static final String ARG_LOGIN_MODE = "ARG_LOGIN_MODE"; diff --git a/libs/login/src/main/java/org/wordpress/android/login/util/SiteUtils.java b/libs/login/src/main/java/org/wordpress/android/login/util/SiteUtils.java index 5eff40a34ba7..95dd26e0574d 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/util/SiteUtils.java +++ b/libs/login/src/main/java/org/wordpress/android/login/util/SiteUtils.java @@ -48,11 +48,10 @@ private static SiteModel getSiteByMatchingUrl(List siteModelList, Str } @NonNull - public static FetchSitesPayload getFetchSitesPayload(boolean isJetpackAppLogin, - boolean isWooAppLogin) { + public static FetchSitesPayload getFetchSitesPayload(boolean isJetpackAppLogin) { ArrayList siteFilters = new ArrayList<>(); return new FetchSitesPayload( - siteFilters, !isJetpackAppLogin && !isWooAppLogin + siteFilters, !isJetpackAppLogin ); } } From 2a5e08072b74fe4573f2c8996964ece1aeab64ca Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:34:38 -0700 Subject: [PATCH 15/71] =?UTF-8?q?Don=E2=80=99t=20show=20the=20username/pas?= =?UTF-8?q?sword=20fields=20for=20Application=20Password=20sites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wordpress/android/ui/prefs/SiteSettingsFragment.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java index 454c8e008f2c..5657f3a850b6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/prefs/SiteSettingsFragment.java @@ -2099,8 +2099,13 @@ private void removeEmptyCategories() { } private void removeNonSelfHostedPreferences() { - mUsernamePref.setEnabled(true); - mPasswordPref.setEnabled(true); + // Hide username/password preferences for sites using application passwords + if (mSite.isUsingSelfHostedRestApi()) { + WPPrefUtils.removePreference(this, R.string.pref_key_site_screen, R.string.pref_key_site_account); + } else { + mUsernamePref.setEnabled(true); + mPasswordPref.setEnabled(true); + } PreferenceGroup group = (PreferenceGroup) findPreference(getString(R.string.pref_key_site_general)); if (group != null) { group.removeAll(); From a6792f2e17b42c7139a2104c057f498d7a170665 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:39:35 -0700 Subject: [PATCH 16/71] In the Me tab, start WP.com login directly --- .../org/wordpress/android/ui/main/MeFragment.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt index f46f1bf92ba4..95f17d0d9449 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/MeFragment.kt @@ -46,6 +46,8 @@ import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.models.JetpackPoweredScreen import org.wordpress.android.ui.ActivityLauncher +import org.wordpress.android.ui.ActivityNavigator +import org.wordpress.android.ui.accounts.login.WPcomLoginHelper import org.wordpress.android.ui.about.UnifiedAboutActivity import org.wordpress.android.ui.accounts.HelpActivity.Origin.ME_SCREEN_HELP import org.wordpress.android.ui.debug.DebugSettingsActivity @@ -139,6 +141,12 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener { @Inject lateinit var appLogWrapper: AppLogWrapper + @Inject + lateinit var activityNavigator: ActivityNavigator + + @Inject + lateinit var wpcomLoginHelper: WPcomLoginHelper + private val viewModel: MeViewModel by viewModels() private val emailVerificationViewModel: EmailVerificationViewModel by viewModels() @@ -222,10 +230,8 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener { if (accountStore.hasAccessToken()) { signOutWordPressComWithConfirmation() } else { - if (BuildConfig.IS_JETPACK_APP) { - ActivityLauncher.showSignInForResultJetpackOnly(activity) - } else { - ActivityLauncher.showSignInForResultWpComOnly(activity) + activity?.let { + activityNavigator.openInCustomTab(it, wpcomLoginHelper.wpcomLoginUri.toString()) } } } From 679ffb0e74dbb2203ad6ec3f10934b3c8b339ed4 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:39:42 -0700 Subject: [PATCH 17/71] Fix strings --- WordPress/src/jetpack/res/values/strings.xml | 4 ++-- fastlane/jetpack_resources/values/strings.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/src/jetpack/res/values/strings.xml b/WordPress/src/jetpack/res/values/strings.xml index f5c6d242def6..38f535f387e7 100644 --- a/WordPress/src/jetpack/res/values/strings.xml +++ b/WordPress/src/jetpack/res/values/strings.xml @@ -36,8 +36,8 @@ Jetpack for Android Support - Log out of Jetpack - Log out of Jetpack? + Log out of WordPress.com + Log out of WordPress.com? About Jetpack diff --git a/fastlane/jetpack_resources/values/strings.xml b/fastlane/jetpack_resources/values/strings.xml index f5c6d242def6..38f535f387e7 100644 --- a/fastlane/jetpack_resources/values/strings.xml +++ b/fastlane/jetpack_resources/values/strings.xml @@ -36,8 +36,8 @@ Jetpack for Android Support - Log out of Jetpack - Log out of Jetpack? + Log out of WordPress.com + Log out of WordPress.com? About Jetpack From e775cd70455bd723dd828dc0bc09c7e3e3a460a7 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:51:58 -0700 Subject: [PATCH 18/71] Fix WP.com login UI --- .../android/ui/accounts/LoginActivity.java | 2 ++ .../src/main/res/layout/login_loading.xml | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 WordPress/src/main/res/layout/login_loading.xml diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 9fd650cc9b17..c86726fc508b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -129,6 +129,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // JETPACK_LOGIN_ONLY = JPAndroid LoginMode loginMode = getLoginMode(); if ((mLoginHelper.isLoggedIn()) && (loginMode == LoginMode.FULL || loginMode == LoginMode.JETPACK_LOGIN_ONLY)) { + // Show loading UI while we fetch account and sites in the background + setContentView(R.layout.login_loading); this.loggedInAndFinish(new ArrayList(), true); return; } diff --git a/WordPress/src/main/res/layout/login_loading.xml b/WordPress/src/main/res/layout/login_loading.xml new file mode 100644 index 000000000000..3d430fe630e8 --- /dev/null +++ b/WordPress/src/main/res/layout/login_loading.xml @@ -0,0 +1,30 @@ + + + + + + + + From 482617f1e3356cd2b0d9019bf7e94564af3abe84 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:05:57 -0700 Subject: [PATCH 19/71] Improve the WP.org login device name --- .../login/ApplicationPasswordLoginHelper.kt | 22 ++++++++++--------- WordPress/src/main/res/values/strings.xml | 2 ++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/ApplicationPasswordLoginHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/ApplicationPasswordLoginHelper.kt index 9fdbae901799..bbd4fda8e98c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/ApplicationPasswordLoginHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/ApplicationPasswordLoginHelper.kt @@ -1,6 +1,9 @@ package org.wordpress.android.ui.accounts.login +import android.content.Context import androidx.core.net.toUri +import org.wordpress.android.R +import org.wordpress.android.util.DeviceUtils import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import org.wordpress.android.analytics.AnalyticsTracker @@ -194,9 +197,10 @@ class ApplicationPasswordLoginHelper @Inject constructor( * This class is created to wrap the Uri calls and let us unit test the login helper */ class UriLoginWrapper @Inject constructor( + private val context: Context, private val apiRootUrlCache: ApiRootUrlCache, private val buildConfigWrapper: BuildConfigWrapper, - ) { + ) { fun parseUriLogin(url: String): UriLogin { val uri = url.toUri() val siteUrl = UrlUtils.normalizeUrl(uri.getQueryParameter("site_url")) @@ -210,15 +214,15 @@ class ApplicationPasswordLoginHelper @Inject constructor( return if (authorizationUrl.isNullOrEmpty()) { authorizationUrl.orEmpty() } else { - val appName: String - val successUrl: String - if (buildConfigWrapper.isJetpackApp) { - appName = ANDROID_JETPACK_CLIENT - successUrl = JETPACK_SUCCESS_URL + val userDeviceName = DeviceUtils.getInstance().getDeviceName(context) + val (appName, successUrl) = if (buildConfigWrapper.isJetpackApp) { + context.getString(R.string.application_password_app_name_jetpack, userDeviceName) to + JETPACK_SUCCESS_URL } else { - appName = ANDROID_WORDPRESS_CLIENT - successUrl = WORDPRESS_SUCCESS_URL + context.getString(R.string.application_password_app_name_wordpress, userDeviceName) to + WORDPRESS_SUCCESS_URL } + authorizationUrl.toUri().buildUpon().apply { appendQueryParameter("app_name", appName) appendQueryParameter("success_url", successUrl) @@ -228,8 +232,6 @@ class ApplicationPasswordLoginHelper @Inject constructor( } companion object { - const val ANDROID_JETPACK_CLIENT = "android-jetpack-client" - const val ANDROID_WORDPRESS_CLIENT = "android-wordpress-client" private const val JETPACK_SUCCESS_URL = "jetpack://app-pass-authorize" private const val WORDPRESS_SUCCESS_URL = "wordpress://app-pass-authorize" } diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index fb0fdc2e0cbd..b79485dc678c 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4520,6 +4520,8 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> Name Created Last used + Jetpack Android App on %s + WordPress Android App on %s There\'s nothing here From 2332de08c7e2d1251d560a79a6693dc0cb7a0222 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:29:33 -0700 Subject: [PATCH 20/71] Add self-hosted site reauthentication --- ...licationPasswordAutoAuthDialogViewModel.kt | 12 ++- .../ApplicationPasswordViewModelSlice.kt | 98 ++++++++++++++++++- WordPress/src/main/res/values/strings.xml | 1 + 3 files changed, 105 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt index 39689966d8f9..c0ad66c2b19f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt @@ -1,8 +1,10 @@ package org.wordpress.android.ui.accounts.login.applicationpassword +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -10,17 +12,17 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import org.wordpress.android.R import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper -import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.Companion.ANDROID_JETPACK_CLIENT -import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.Companion.ANDROID_WORDPRESS_CLIENT import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.UriLogin import org.wordpress.android.ui.prefs.experimentalfeatures.ExperimentalFeatures import org.wordpress.android.ui.prefs.experimentalfeatures.ExperimentalFeatures.Feature import org.wordpress.android.util.AppLog import org.wordpress.android.util.BuildConfigWrapper +import org.wordpress.android.util.DeviceUtils import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.ApplicationPasswordCreateParams import uniffi.wp_api.WpUuid @@ -31,6 +33,7 @@ import javax.inject.Inject @HiltViewModel class ApplicationPasswordAutoAuthDialogViewModel @Inject constructor( + @ApplicationContext private val context: Context, private val wpApiClientProvider: WpApiClientProvider, private val applicationPasswordLoginHelper: ApplicationPasswordLoginHelper, private val buildConfigWrapper: BuildConfigWrapper, @@ -57,10 +60,11 @@ class ApplicationPasswordAutoAuthDialogViewModel @Inject constructor( val client = wpApiClientProvider.getWpApiClientCookiesNonceAuthentication( site = site, ) + val deviceName = DeviceUtils.getInstance().getDeviceName(context) val appName = if (buildConfigWrapper.isJetpackApp) { - ANDROID_JETPACK_CLIENT + context.getString(R.string.application_password_app_name_jetpack, deviceName) } else { - ANDROID_WORDPRESS_CLIENT + context.getString(R.string.application_password_app_name_wordpress, deviceName) } val appId = WpUuid() val response = client.request { requestBuilder -> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt index 4769b3ccda67..ec1276ea9efa 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.wordpress.android.R import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.utils.AppLogWrapper import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper @@ -19,6 +20,7 @@ import org.wordpress.android.ui.utils.ListItemInteraction import org.wordpress.android.ui.utils.UiString.UiStringRes import org.wordpress.android.util.AppLog import org.wordpress.android.viewmodel.Event +import rs.wordpress.api.kotlin.WpRequestResult import javax.inject.Inject class ApplicationPasswordViewModelSlice @Inject constructor( @@ -26,6 +28,7 @@ class ApplicationPasswordViewModelSlice @Inject constructor( private val siteStore: SiteStore, private val experimentalFeatures: ExperimentalFeatures, private val appLogWrapper: AppLogWrapper, + private val wpApiClientProvider: WpApiClientProvider, ) { lateinit var scope: CoroutineScope @@ -39,10 +42,18 @@ class ApplicationPasswordViewModelSlice @Inject constructor( private val _onSnackbarMessage = MutableLiveData>() val onSnackbarMessage = _onSnackbarMessage - val uiModelMutable = MutableLiveData() - val uiModel: LiveData = uiModelMutable + val uiModelMutable = MutableLiveData() + val uiModel: LiveData = uiModelMutable fun buildCard(siteModel: SiteModel) { + val storedSite = siteStore.sites.firstOrNull { it.id == siteModel.id } + + // For sites using application passwords, validate credentials + if (storedSite != null && storedSite.isUsingSelfHostedRestApi) { + validateCredentialsAndBuildCard(storedSite) + return + } + if (shouldBuildCard()) { buildApplicationPasswordDiscovery(siteModel) } else { @@ -51,9 +62,92 @@ class ApplicationPasswordViewModelSlice @Inject constructor( } } + @Suppress("TooGenericExceptionCaught") + private fun validateCredentialsAndBuildCard(site: SiteModel) { + appLogWrapper.d(AppLog.T.MAIN, "A_P: Validating credentials for ${site.url}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: isUsingSelfHostedRestApi: ${site.isUsingSelfHostedRestApi}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: hasApiRestUsername: ${!site.apiRestUsernamePlain.isNullOrEmpty()}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: hasApiRestPassword: ${!site.apiRestPasswordPlain.isNullOrEmpty()}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: apiRestUsername: ${site.apiRestUsernamePlain}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: wpApiRestUrl: ${site.wpApiRestUrl}") + appLogWrapper.d(AppLog.T.MAIN, "A_P: origin: ${site.origin}") + + // If credentials are missing, show reauthentication banner immediately + if (applicationPasswordLoginHelper.siteHasBadCredentials(site)) { + appLogWrapper.d(AppLog.T.MAIN, "A_P: Credentials missing for ${site.url}, showing banner") + buildReauthenticationBanner(site) + return + } + + // Validate credentials by making a simple API call + scope.launch { + try { + appLogWrapper.d(AppLog.T.MAIN, "A_P: Making API call to validate credentials") + val client = wpApiClientProvider.getWpApiClient(site) + val response = client.request { requestBuilder -> + requestBuilder.users().retrieveMeWithViewContext() + } + appLogWrapper.d(AppLog.T.MAIN, "A_P: API response type: ${response::class.simpleName}") + when (response) { + is WpRequestResult.Success -> { + // Credentials are valid, hide the card + uiModelMutable.postValue(null) + appLogWrapper.d(AppLog.T.MAIN, "A_P: Credentials valid for ${site.url}") + } + is WpRequestResult.WpError -> { + appLogWrapper.d(AppLog.T.MAIN, "A_P: WpError for ${site.url}: ${response.response}") + buildReauthenticationBanner(site) + } + is WpRequestResult.UnknownError -> { + appLogWrapper.d( + AppLog.T.MAIN, + "A_P: UnknownError for ${site.url}: code=${response.statusCode}, msg=${response.response}" + ) + buildReauthenticationBanner(site) + } + else -> { + // Credentials are invalid, show reauthentication banner + appLogWrapper.d(AppLog.T.MAIN, "A_P: Other error for ${site.url}: $response") + buildReauthenticationBanner(site) + } + } + } catch (e: Exception) { + appLogWrapper.e( + AppLog.T.MAIN, + "A_P: Exception validating credentials for ${site.url}: ${e::class.simpleName}: ${e.message}" + ) + e.printStackTrace() + uiModelMutable.postValue(null) + } + } + } + private fun shouldBuildCard(): Boolean = experimentalFeatures.isEnabled(Feature.EXPERIMENTAL_APPLICATION_PASSWORD_FEATURE) + private fun buildReauthenticationBanner(site: SiteModel) { + scope.launch { + val authorizationUrlComplete = applicationPasswordLoginHelper.getAuthorizationUrlComplete(site.url) + if (authorizationUrlComplete.isEmpty()) { + uiModelMutable.postValue(null) + appLogWrapper.d(AppLog.T.MAIN, "A_P: Hiding reauthentication card for ${site.url} - bad discovery") + } else { + showReauthenticationCard(site, authorizationUrlComplete) + } + } + } + + private fun showReauthenticationCard(site: SiteModel, alternativeUrl: String) { + uiModelMutable.postValue( + MySiteCardAndItem.Item.SingleActionCard( + textResource = R.string.application_password_reauthentication_banner, + imageResource = R.drawable.ic_notice_white_24dp, + onActionClick = { onClick(site, alternativeUrl) } + ) + ) + appLogWrapper.d(AppLog.T.MAIN, "A_P: Showing reauthentication card for ${site.url}") + } + private fun buildApplicationPasswordDiscovery(site: SiteModel) { scope.launch { // If the site is already authorized, no need to run the discovery diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index b79485dc678c..9b851834a861 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -4522,6 +4522,7 @@ translators: %s: Select control option value e.g: "Auto, 25%". --> Last used Jetpack Android App on %s WordPress Android App on %s + Your site credentials are invalid. Tap to reauthenticate. There\'s nothing here From 656bffd917e007b20ce56a2eac0c86b763b1c38f Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 11:08:28 -0700 Subject: [PATCH 21/71] Display more detailed error messages --- .../ApplicationPasswordsViewModel.kt | 85 ++++++++++++++++++- ...licationPasswordAutoAuthDialogViewModel.kt | 28 +++++- .../ApplicationPasswordViewModelSlice.kt | 19 +++++ 3 files changed, 129 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordsViewModel.kt index d93285f31f45..0e1cd0d233f1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordsViewModel.kt @@ -27,6 +27,7 @@ import java.util.Locale import rs.wordpress.api.kotlin.WpApiClient import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.ApplicationPasswordWithViewContext +import uniffi.wp_api.RequestExecutionErrorReason import uniffi.wp_api.WpApiParamOrder import javax.inject.Inject import javax.inject.Named @@ -201,8 +202,43 @@ class ApplicationPasswordsViewModel @Inject constructor( userIdResponse.response.data.id } + is WpRequestResult.WpError -> { + val error = "Error getting current user Id: WpError - ${userIdResponse.errorMessage}" + appLogWrapper.e(AppLog.T.API, "$error (response: ${userIdResponse.response})") + onError(error) + null + } + + is WpRequestResult.UnknownError -> { + val error = "Error getting current user Id: UnknownError - " + + "statusCode=${userIdResponse.statusCode}, response=${userIdResponse.response}" + appLogWrapper.e(AppLog.T.API, error) + onError(error) + null + } + + is WpRequestResult.ResponseParsingError<*> -> { + val error = "Error getting current user Id: ResponseParsingError - $userIdResponse" + appLogWrapper.e(AppLog.T.API, error) + onError(error) + null + } + + is WpRequestResult.RequestExecutionFailed -> { + val isTimeout = userIdResponse.reason is RequestExecutionErrorReason.HttpTimeoutError + val error = if (isTimeout) { + "Error getting current user Id: Request timed out" + } else { + "Error getting current user Id: RequestExecutionFailed - " + + "reason=${userIdResponse.reason}, statusCode=${userIdResponse.statusCode}" + } + appLogWrapper.e(AppLog.T.API, error) + onError(error) + null + } + else -> { - val error = "Error getting current user Id" + val error = "Error getting current user Id: ${userIdResponse::class.simpleName} - $userIdResponse" appLogWrapper.e(AppLog.T.API, error) onError(error) null @@ -223,8 +259,53 @@ class ApplicationPasswordsViewModel @Inject constructor( currentApplicationPasswordResponse.response.data } + is WpRequestResult.WpError -> { + val error = "Error getting Application Password list: WpError - " + + currentApplicationPasswordResponse.errorMessage + appLogWrapper.e( + AppLog.T.API, + "$error (response: ${currentApplicationPasswordResponse.response})" + ) + onError(error) + emptyList() + } + + is WpRequestResult.UnknownError -> { + val error = "Error getting Application Password list: UnknownError - " + + "statusCode=${currentApplicationPasswordResponse.statusCode}, " + + "response=${currentApplicationPasswordResponse.response}" + appLogWrapper.e(AppLog.T.API, error) + onError(error) + emptyList() + } + + is WpRequestResult.ResponseParsingError<*> -> { + val error = "Error getting Application Password list: ResponseParsingError - " + + "$currentApplicationPasswordResponse" + appLogWrapper.e(AppLog.T.API, error) + onError(error) + emptyList() + } + + is WpRequestResult.RequestExecutionFailed -> { + val isTimeout = + currentApplicationPasswordResponse.reason is RequestExecutionErrorReason.HttpTimeoutError + val error = if (isTimeout) { + "Error getting Application Password list: Request timed out" + } else { + "Error getting Application Password list: RequestExecutionFailed - " + + "reason=${currentApplicationPasswordResponse.reason}, " + + "statusCode=${currentApplicationPasswordResponse.statusCode}" + } + appLogWrapper.e(AppLog.T.API, error) + onError(error) + emptyList() + } + else -> { - val error = "Error getting Application Password list" + val error = "Error getting Application Password list: " + + "${currentApplicationPasswordResponse::class.simpleName} - " + + "$currentApplicationPasswordResponse" appLogWrapper.e(AppLog.T.API, error) onError(error) emptyList() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt index c0ad66c2b19f..4423a79f9d65 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/ApplicationPasswordAutoAuthDialogViewModel.kt @@ -25,6 +25,7 @@ import org.wordpress.android.util.BuildConfigWrapper import org.wordpress.android.util.DeviceUtils import rs.wordpress.api.kotlin.WpRequestResult import uniffi.wp_api.ApplicationPasswordCreateParams +import uniffi.wp_api.RequestExecutionErrorReason import uniffi.wp_api.WpUuid import java.text.SimpleDateFormat import java.util.Date @@ -92,8 +93,33 @@ class ApplicationPasswordAutoAuthDialogViewModel @Inject constructor( _navigationEvent.emit(NavigationEvent.Success) } + is WpRequestResult.WpError -> { + appLogWrapper.e( + AppLog.T.API, + "Error creating application password: WpError - ${response.errorMessage}" + ) + fallbackToManualLogin(site.url) + } + + is WpRequestResult.RequestExecutionFailed -> { + val isTimeout = response.reason is RequestExecutionErrorReason.HttpTimeoutError + if (isTimeout) { + appLogWrapper.e(AppLog.T.API, "Error creating application password: Request timed out") + } else { + appLogWrapper.e( + AppLog.T.API, + "Error creating application password: RequestExecutionFailed - " + + "reason=${response.reason}, statusCode=${response.statusCode}" + ) + } + fallbackToManualLogin(site.url) + } + else -> { - appLogWrapper.e(AppLog.T.API, "Error creating application password") + appLogWrapper.e( + AppLog.T.API, + "Error creating application password: ${response::class.simpleName} - $response" + ) fallbackToManualLogin(site.url) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt index ec1276ea9efa..2263b03195f0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt @@ -21,6 +21,7 @@ import org.wordpress.android.ui.utils.UiString.UiStringRes import org.wordpress.android.util.AppLog import org.wordpress.android.viewmodel.Event import rs.wordpress.api.kotlin.WpRequestResult +import uniffi.wp_api.RequestExecutionErrorReason import javax.inject.Inject class ApplicationPasswordViewModelSlice @Inject constructor( @@ -105,6 +106,24 @@ class ApplicationPasswordViewModelSlice @Inject constructor( ) buildReauthenticationBanner(site) } + is WpRequestResult.RequestExecutionFailed -> { + val isTimeout = response.reason is RequestExecutionErrorReason.HttpTimeoutError + if (isTimeout) { + appLogWrapper.d(AppLog.T.MAIN, "A_P: Request timed out for ${site.url}") + } else { + appLogWrapper.d( + AppLog.T.MAIN, + "A_P: RequestExecutionFailed for ${site.url}: " + + "reason=${response.reason}, statusCode=${response.statusCode}" + ) + } + // Don't show reauthentication banner for timeouts - it's likely a network issue + if (!isTimeout) { + buildReauthenticationBanner(site) + } else { + uiModelMutable.postValue(null) + } + } else -> { // Credentials are invalid, show reauthentication banner appLogWrapper.d(AppLog.T.MAIN, "A_P: Other error for ${site.url}: $response") From 0548ab8388e529cecdb1618fb455c9000b305daf Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:55:17 -0700 Subject: [PATCH 22/71] Properly display timeout errors --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a81d16c86267..0e610bdfc995 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -102,7 +102,7 @@ wellsql = '2.0.0' wordpress-aztec = 'v2.1.4' wordpress-lint = '2.2.0' wordpress-persistent-edittext = '1.0.2' -wordpress-rs = 'trunk-ceb966c5b7f1af692aa9320b97a0cf2f316bb6c6' +wordpress-rs = '1131-710ccb468914942c951c2a0fe80edf85af00e7f8' wordpress-utils = '3.14.0' automattic-ucrop = '2.2.11' zendesk = '5.5.2' From e56c141998a4436ef58535fb15b74f80ec18c809 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:55:28 -0700 Subject: [PATCH 23/71] Fix WP.com sharing login flow --- WordPress/src/main/AndroidManifest.xml | 1 + .../android/ui/accounts/LoginActivity.java | 35 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 7f5509972047..8c208c58c084 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -129,6 +129,7 @@ android:label="@string/me_section_screen_title" /> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index c86726fc508b..2e93b207c448 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -89,6 +89,9 @@ public class LoginActivity extends BaseAppCompatActivity implements LoginListene private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; + // Static field to preserve login mode across OAuth flow (when callback creates new activity) + private static LoginMode sPendingLoginMode; + private LoginMode mLoginMode; private LoginViewModel mViewModel; @Inject protected WPcomLoginHelper mLoginHelper; @@ -118,6 +121,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (loginProcessed) { getIntent().setData(null); + // OAuth login successful - show loading UI and finish the login flow + setContentView(R.layout.login_loading); + this.loggedInAndFinish(new ArrayList(), true); + return; + } else { + // Not an OAuth callback - clear any pending login mode from a previous flow + sPendingLoginMode = null; } // Start preloading the WordPress.com login page if needed – this avoids visual hitches @@ -172,7 +182,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { break; case SHARE_INTENT: mUnifiedLoginTracker.setSource(Source.SHARE); - showWPcomLoginScreen(this); + showFragment(new LoginPrologueRevampedFragment(), LoginPrologueRevampedFragment.TAG); break; } } else { @@ -210,6 +220,20 @@ public void onSaveInstanceState(Bundle outState) { } } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + + // Handle OAuth callback when activity is reused (singleTop) + boolean loginProcessed = mLoginHelper.tryLoginWithDataString(intent.getDataString()); + if (loginProcessed) { + intent.setData(null); + setContentView(R.layout.login_loading); + this.loggedInAndFinish(new ArrayList(), true); + } + } + @Override protected void onStart() { super.onStart(); @@ -314,6 +338,12 @@ public LoginMode getLoginMode() { // compute and cache the Login mode mLoginMode = LoginMode.fromIntent(getIntent()); + // If the mode is FULL (default) but we have a pending mode from an OAuth flow, use that instead + if (mLoginMode == LoginMode.FULL && sPendingLoginMode != null) { + mLoginMode = sPendingLoginMode; + sPendingLoginMode = null; // Clear after use + } + return mLoginMode; } @@ -377,6 +407,9 @@ public void showWPcomLoginScreen(@NonNull Context context) { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_WPCOM_WEBVIEW); mUnifiedLoginTracker.setFlowAndStep(Flow.WORDPRESS_COM_WEB, Step.WPCOM_WEB_START); + // Save the current login mode so it survives the OAuth callback (which creates a new activity) + sPendingLoginMode = getLoginMode(); + CustomTabsIntent intent = getCustomTabsIntent(); Uri loginUri = mLoginHelper.getWpcomLoginUri(); From f294dcf654cb92ce7e8c27ce48552e8433e02183 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:27:17 -0700 Subject: [PATCH 24/71] Fix share flow login --- .../android/ui/ActivityLauncher.java | 9 ++++- .../android/ui/accounts/LoginActivity.java | 35 +++++++++++++++++-- .../ApplicationPasswordLoginActivity.kt | 9 +++++ .../accounts/login/LoginCompletionUseCase.kt | 11 ------ .../android/ui/main/WPMainActivity.java | 7 ++++ .../login/LoginCompletionUseCaseTest.kt | 32 ----------------- 6 files changed, 56 insertions(+), 47 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index e3129d105c7f..ccca3cb7c351 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -179,12 +179,19 @@ public class ActivityLauncher { public static final String CATEGORY_DETAIL_ID = "category_detail_key"; public static void showMainActivity(Context context) { - showMainActivity(context, false); + showMainActivity(context, false, false); } public static void showMainActivity(Context context, boolean bypassMigration) { + showMainActivity(context, bypassMigration, false); + } + + public static void showMainActivity(Context context, boolean bypassMigration, boolean selectPrimarySite) { Intent intent = getMainActivityInNewStack(context); intent.putExtra(ARG_BYPASS_MIGRATION, bypassMigration); + if (selectPrimarySite) { + intent.putExtra(WPMainActivity.ARG_SELECT_PRIMARY_SITE, true); + } context.startActivity(intent); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 2e93b207c448..f1da4648ca58 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -92,6 +92,19 @@ public class LoginActivity extends BaseAppCompatActivity implements LoginListene // Static field to preserve login mode across OAuth flow (when callback creates new activity) private static LoginMode sPendingLoginMode; + // Static field to track if we're in a share flow (for self-hosted login via ApplicationPasswordLoginActivity) + private static boolean sIsShareFlowPending; + + /** + * Check if there's a pending share flow. Used by ApplicationPasswordLoginActivity + * to determine whether to navigate to main activity or just finish. + */ + public static boolean consumeShareFlowPending() { + boolean result = sIsShareFlowPending; + sIsShareFlowPending = false; + return result; + } + private LoginMode mLoginMode; private LoginViewModel mViewModel; @Inject protected WPcomLoginHelper mLoginHelper; @@ -246,6 +259,17 @@ protected void onStop() { mDispatcher.unregister(this); } + @Override + protected void onResume() { + super.onResume(); + // Check if self-hosted login completed while in share flow + // ApplicationPasswordLoginActivity finishes back here after successful login + if (getLoginMode() == LoginMode.SHARE_INTENT && mSiteStore.hasSite()) { + setResult(Activity.RESULT_OK); + finish(); + } + } + @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) public void onAccountChanged(OnAccountChanged event) { @@ -289,7 +313,8 @@ private void navigateToMainActivityOrFinish() { mLoginCompletionUseCase.getMainNavigationDestination(getLoginMode()); switch (destination) { case MAIN_ACTIVITY: - ActivityLauncher.showMainActivity(this); + // Select the primary site after WP.com login + ActivityLauncher.showMainActivity(this, false, true); break; case FINISH_ONLY: default: @@ -353,7 +378,7 @@ private void loggedInAndFinish(ArrayList oldSitesIds, boolean doLoginUp // If doLoginUpdate is true, we need to fetch account and sites before navigating. // This happens after WordPress.com OAuth login where we only have the token. - if (mLoginCompletionUseCase.shouldWaitForSitesToLoad(doLoginUpdate, mSiteStore.hasSite())) { + if (doLoginUpdate) { AppLog.i(T.MAIN, "Fetching account and sites before proceeding"); mIsWaitingForSitesToLoad = true; mOldSitesIdsForLoginUpdate = oldSitesIds; @@ -367,7 +392,7 @@ private void loggedInAndFinish(ArrayList oldSitesIds, boolean doLoginUp // Handle self-hosted site login - find the newly added site and return its ID finishWithNewlyAddedSiteId(oldSitesIds); break; - case FINISH_ONLY: + case FINISH_ONLY: // WooCommerce handles its own navigation break; case NAVIGATE_TO_MAIN: @@ -436,6 +461,10 @@ public void showWPcomLoginScreen(@NonNull Context context) { @Override public void loginViaSiteAddress() { + // Track if we're in a share flow so ApplicationPasswordLoginActivity knows to just finish + if (getLoginMode() == LoginMode.SHARE_INTENT) { + sIsShareFlowPending = true; + } slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt index 4aac9caa6791..56db194e18ae 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.wordpress.android.R import org.wordpress.android.ui.ActivityLauncher +import org.wordpress.android.ui.accounts.LoginActivity import org.wordpress.android.ui.accounts.UnifiedLoginTracker import org.wordpress.android.ui.compose.theme.AppThemeM3 import org.wordpress.android.ui.main.BaseAppCompatActivity @@ -73,6 +74,14 @@ class ApplicationPasswordLoginActivity: BaseAppCompatActivity() { ) } + // Check if we're in a share flow - if so, just finish and return to LoginActivity + val isShareFlow = LoginActivity.consumeShareFlowPending() + if (isShareFlow && !navigationActionData.isError) { + setResult(RESULT_OK) + finish() + return + } + if (navigationActionData.isError) { ActivityLauncher.showMainActivity(this) } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt index 42dc65f0180c..ecc18d70a7a3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt @@ -8,17 +8,6 @@ import javax.inject.Inject * This extracts testable logic from LoginActivity. */ class LoginCompletionUseCase @Inject constructor() { - /** - * Determines if we should wait for sites to load after login. - * - * @param doLoginUpdate Whether the login requires fetching account/sites - * @param hasSites Whether the user already has sites loaded - * @return true if we should wait for sites to load before proceeding - */ - fun shouldWaitForSitesToLoad(doLoginUpdate: Boolean, hasSites: Boolean): Boolean { - return doLoginUpdate && !hasSites - } - /** * Determines the navigation action to take based on the login mode. * diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index c53bf0af9a54..3e680ff9b801 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -214,6 +214,7 @@ public class WPMainActivity extends BaseAppCompatActivity implements public static final String ARG_DISMISS_NOTIFICATION = "dismiss_notification"; public static final String ARG_OPEN_BLOGGING_REMINDERS = "show_blogging_reminders_flow"; public static final String ARG_SELECTED_SITE = "SELECTED_SITE_ID"; + public static final String ARG_SELECT_PRIMARY_SITE = "SELECT_PRIMARY_SITE"; public static final String ARG_STAT_TO_TRACK = "stat_to_track"; public static final String ARG_EDITOR_ORIGIN = "editor_origin"; public static final String ARG_IS_CHANGING_CONFIGURATION = "IS_CHANGING_CONFIGURATION"; @@ -440,6 +441,12 @@ && getIntent().getExtras().getBoolean(ARG_CONTINUE_JETPACK_CONNECT, false)) { onSetPromptReminderClick(getIntent().getIntExtra(ARG_OPEN_BLOGGING_REMINDERS, 0)); } + // Check if we should select the primary site (e.g., after WP.com login) + if (getIntent().getBooleanExtra(ARG_SELECT_PRIMARY_SITE, false)) { + getIntent().removeExtra(ARG_SELECT_PRIMARY_SITE); + mSelectedSiteRepository.removeSite(); + initSelectedSite(); + } // Check if a specific site should be selected (e.g., after adding a self-hosted site) int selectedSiteId = getIntent().getIntExtra(ARG_SELECTED_SITE, SelectedSiteRepository.UNAVAILABLE); if (selectedSiteId != SelectedSiteRepository.UNAVAILABLE) { diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt index 31fe3571ccee..af73c1a24f9e 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt @@ -18,38 +18,6 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { useCase = LoginCompletionUseCase() } - // region shouldWaitForSitesToLoad tests - - @Test - fun `given doLoginUpdate true and no sites, then should wait for sites`() { - val result = useCase.shouldWaitForSitesToLoad(doLoginUpdate = true, hasSites = false) - - assertThat(result).isTrue() - } - - @Test - fun `given doLoginUpdate true and has sites, then should not wait`() { - val result = useCase.shouldWaitForSitesToLoad(doLoginUpdate = true, hasSites = true) - - assertThat(result).isFalse() - } - - @Test - fun `given doLoginUpdate false and no sites, then should not wait`() { - val result = useCase.shouldWaitForSitesToLoad(doLoginUpdate = false, hasSites = false) - - assertThat(result).isFalse() - } - - @Test - fun `given doLoginUpdate false and has sites, then should not wait`() { - val result = useCase.shouldWaitForSitesToLoad(doLoginUpdate = false, hasSites = true) - - assertThat(result).isFalse() - } - - // endregion - // region getLoginCompletionAction tests @Test From c375955027379841d8511e0a9d201d5699fabfab Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 16:49:20 -0700 Subject: [PATCH 25/71] Remove `LoginMode.JETPACK_SELFHOSTED` --- .../android/modules/LoginAnalyticsModule.java | 7 +++++++ .../org/wordpress/android/ui/ActivityLauncher.java | 3 +-- .../android/ui/accounts/LoginActivity.java | 1 - .../ui/accounts/login/LoginCompletionUseCase.kt | 1 - .../accounts/login/LoginCompletionUseCaseTest.kt | 14 -------------- .../org/wordpress/android/login/AppLoginConfig.kt | 13 +++++++++++++ .../android/login/LoginBaseFormFragment.java | 11 ++--------- .../org/wordpress/android/login/LoginMode.java | 13 ++++++++++--- 8 files changed, 33 insertions(+), 30 deletions(-) create mode 100644 libs/login/src/main/java/org/wordpress/android/login/AppLoginConfig.kt diff --git a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java index 44b25c9431e6..b92134a05be1 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java @@ -1,7 +1,9 @@ package org.wordpress.android.modules; +import org.wordpress.android.BuildConfig; import org.wordpress.android.fluxc.store.AccountStore; import org.wordpress.android.fluxc.store.SiteStore; +import org.wordpress.android.login.AppLoginConfig; import org.wordpress.android.login.LoginAnalyticsListener; import org.wordpress.android.ui.accounts.UnifiedLoginTracker; import org.wordpress.android.ui.accounts.login.LoginAnalyticsTracker; @@ -19,4 +21,9 @@ public LoginAnalyticsListener provideAnalyticsListener(AccountStore accountStore UnifiedLoginTracker unifiedLoginTracker) { return new LoginAnalyticsTracker(accountStore, siteStore, unifiedLoginTracker); } + + @Provides + public AppLoginConfig provideAppLoginConfig() { + return () -> BuildConfig.IS_JETPACK_APP; + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index ccca3cb7c351..547f0fe86f69 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -1592,8 +1592,7 @@ public static void viewSuggestionsForResult(@NonNull Activity activity, public static void addSelfHostedSiteForResult(Activity activity) { Intent intent; intent = new Intent(activity, LoginActivity.class); - LoginMode mode = BuildConfig.IS_JETPACK_APP ? LoginMode.JETPACK_SELFHOSTED : LoginMode.SELFHOSTED_ONLY; - mode.putInto(intent); + LoginMode.SELFHOSTED_ONLY.putInto(intent); activity.startActivityForResult(intent, RequestCodes.ADD_ACCOUNT); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index f1da4648ca58..da368d1148e0 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -176,7 +176,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mUnifiedLoginTracker.setSource(Source.ADD_WORDPRESS_COM_ACCOUNT); showWPcomLoginScreen(this); break; - case JETPACK_SELFHOSTED: case SELFHOSTED_ONLY: mUnifiedLoginTracker.setSource(Source.SELF_HOSTED); showFragment(new LoginSiteApplicationPasswordFragment(), LoginSiteApplicationPasswordFragment.TAG); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt index ecc18d70a7a3..023e5d38bc08 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt @@ -17,7 +17,6 @@ class LoginCompletionUseCase @Inject constructor() { fun getLoginCompletionAction(loginMode: LoginMode): LoginCompletionAction { return when (loginMode) { LoginMode.SHARE_INTENT, - LoginMode.JETPACK_SELFHOSTED, LoginMode.SELFHOSTED_ONLY -> LoginCompletionAction.FINISH_WITH_NEW_SITE else -> LoginCompletionAction.NAVIGATE_TO_MAIN diff --git a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt index af73c1a24f9e..843fe72cfc9f 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt @@ -48,13 +48,6 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { assertThat(result).isEqualTo(LoginCompletionAction.FINISH_WITH_NEW_SITE) } - @Test - fun `given JETPACK_SELFHOSTED mode, then action is FINISH_WITH_NEW_SITE`() { - val result = useCase.getLoginCompletionAction(LoginMode.JETPACK_SELFHOSTED) - - assertThat(result).isEqualTo(LoginCompletionAction.FINISH_WITH_NEW_SITE) - } - @Test fun `given SHARE_INTENT mode, then action is FINISH_WITH_NEW_SITE`() { val result = useCase.getLoginCompletionAction(LoginMode.SHARE_INTENT) @@ -122,13 +115,6 @@ class LoginCompletionUseCaseTest : BaseUnitTest() { assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) } - @Test - fun `given JETPACK_SELFHOSTED mode, then destination is FINISH_ONLY`() { - val result = useCase.getMainNavigationDestination(LoginMode.JETPACK_SELFHOSTED) - - assertThat(result).isEqualTo(MainNavigationDestination.FINISH_ONLY) - } - @Test fun `given SHARE_INTENT mode, then destination is FINISH_ONLY`() { val result = useCase.getMainNavigationDestination(LoginMode.SHARE_INTENT) diff --git a/libs/login/src/main/java/org/wordpress/android/login/AppLoginConfig.kt b/libs/login/src/main/java/org/wordpress/android/login/AppLoginConfig.kt new file mode 100644 index 000000000000..502ab91b01e4 --- /dev/null +++ b/libs/login/src/main/java/org/wordpress/android/login/AppLoginConfig.kt @@ -0,0 +1,13 @@ +package org.wordpress.android.login + +/** + * Configuration interface for app-specific login behavior. + * Implemented by the app module to provide app-specific settings to the login library. + */ +interface AppLoginConfig { + /** + * Returns true if this is the Jetpack app, false for WordPress app. + * Used to determine site filtering behavior during login. + */ + val isJetpackApp: Boolean +} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java index b19d816473b7..8aebe5695ad5 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java @@ -64,6 +64,7 @@ public abstract class LoginBaseFormFragment extends Fragment @Inject protected AccountStore mAccountStore; @Inject protected LoginAnalyticsListener mAnalyticsListener; + @Inject protected AppLoginConfig mAppLoginConfig; protected abstract @LayoutRes int getContentLayout(); protected abstract void setupLabel(@NonNull TextView label); @@ -327,19 +328,11 @@ public void onAccountChanged(OnAccountChanged event) { } else if (event.causeOfChange == AccountAction.FETCH_SETTINGS) { // The user's account settings have also been fetched and stored - now we can fetch the user's sites FetchSitesPayload payload = - SiteUtils.getFetchSitesPayload(isJetpackAppLogin()); + SiteUtils.getFetchSitesPayload(mAppLoginConfig.isJetpackApp()); mDispatcher.dispatch(SiteActionBuilder.newFetchSitesAction(payload)); } } - protected boolean isJetpackAppLogin() { - if (!(mLoginListener instanceof LoginListener)) return false; - - LoginMode mode = ((LoginListener) mLoginListener).getLoginMode(); - return mode == LoginMode.JETPACK_LOGIN_ONLY || mode == LoginMode.JETPACK_SELFHOSTED; - } - - @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) public void onSiteChanged(OnSiteChanged event) { diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java b/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java index 10c8e41bc7f4..22e17ed6fe2f 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java +++ b/libs/login/src/main/java/org/wordpress/android/login/LoginMode.java @@ -2,21 +2,28 @@ import android.content.Intent; +import androidx.annotation.NonNull; + public enum LoginMode { FULL, SELFHOSTED_ONLY, - JETPACK_SELFHOSTED, WPCOM_LOGIN_ONLY, JETPACK_LOGIN_ONLY, JETPACK_STATS, JETPACK_REST_CONNECT, + + // The user has the Jetpack app installed, is not logged in, but tries to use it + // to open a link like https://wordpress.com/stats. WPCOM_LOGIN_DEEPLINK, WPCOM_REAUTHENTICATE, + + // The user has tried to share content from another app (like Google Photos) using the app, but they + // haven't logged in yet. SHARE_INTENT; private static final String ARG_LOGIN_MODE = "ARG_LOGIN_MODE"; - public static LoginMode fromIntent(Intent intent) { + @NonNull public static LoginMode fromIntent(@NonNull Intent intent) { if (intent.hasExtra(ARG_LOGIN_MODE)) { return LoginMode.valueOf(intent.getStringExtra(ARG_LOGIN_MODE)); } else { @@ -24,7 +31,7 @@ public static LoginMode fromIntent(Intent intent) { } } - public void putInto(Intent intent) { + public void putInto(@NonNull Intent intent) { intent.putExtra(ARG_LOGIN_MODE, this.name()); } } From 25b11e0e6b8672c2a69a04574eecbb064d1b2539 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:00:21 -0700 Subject: [PATCH 26/71] Move resources out of login module into app --- WordPress/build.gradle | 1 - .../login/LoginPrologueRevampedFragment.kt | 11 +- .../android/modules/LoginAnalyticsModule.java | 8 +- .../android/support/ZendeskHelper.kt | 2 +- .../android/ui/ActivityLauncher.java | 22 +- .../ui/JetpackConnectionResultActivity.java | 4 +- .../ui/JetpackConnectionWebViewActivity.java | 4 +- .../android/ui/accounts/LoginActivity.java | 162 +++----- .../android/ui/accounts/LoginFlow.kt | 109 ++++++ .../LoginMagicLinkInterceptActivity.java | 2 +- .../ApplicationPasswordLoginViewModel.kt | 2 +- .../accounts}/login/LoginAnalyticsListener.kt | 9 +- .../accounts/login/LoginAnalyticsTracker.java | 1 - .../accounts/login/LoginCompletionUseCase.kt | 62 --- .../accounts/login/LoginPrologueListener.kt | 9 - .../login/LoginSiteAddressValidator.java | 3 +- .../LoginSiteApplicationPasswordFragment.kt | 205 +++++++--- .../login/jetpack/LoginNoSitesFragment.kt | 34 +- .../jetpack/LoginSiteCheckErrorFragment.kt | 15 - .../JetpackRemoteInstallActivity.kt | 4 +- .../ui/layoutpicker/CategoryViewHolder.kt | 2 +- .../android/ui/main/WPMainActivity.java | 2 +- .../util/AuthenticationDialogUtils.java | 4 +- .../org/wordpress/android/util/SiteUtils.java | 10 + .../util/image/ImagePlaceholderManager.kt | 5 +- .../login_on_background_medium_selector.xml | 0 .../color/login_on_surface_high_selector.xml | 0 .../login_on_surface_medium_selector.xml | 0 .../main/res/drawable/login_toolbar_icon.xml | 0 .../src/main/res/layout/help_activity.xml | 2 +- .../jetpack_login_include_user_container.xml | 2 +- .../src/main/res/layout/login_form_screen.xml | 0 .../layout/login_include_epilogue_header.xml | 2 +- .../res/layout/login_site_address_screen.xml | 17 +- .../res/layout/reader_listitem_comment.xml | 2 +- .../src/main/res/layout/toolbar_login.xml | 0 .../src/main/res/menu/menu_login.xml | 0 .../main/res/values-night/styles_login.xml | 27 ++ .../src/main/res/values-v27/styles_login.xml | 0 WordPress/src/main/res/values/dimens.xml | 1 + WordPress/src/main/res/values/styles.xml | 2 +- .../src/main/res/values/styles_login.xml | 287 +++++++++++++- .../login/LoginCompletionUseCaseTest.kt | 154 -------- .../login/LoginSiteAddressValidatorTest.java | 2 +- .../accounts/login/LoginPrologueFragment.kt | 14 +- .../login/LoginPrologueRevampedFragment.kt | 11 +- config/gradle/code_coverage.gradle | 3 +- libs/login/build.gradle | 84 ---- libs/login/developer.properties-example | 4 - .../wordpress/android/login/AppLoginConfig.kt | 13 - .../android/login/LoginBaseFormFragment.java | 365 ------------------ .../android/login/LoginListener.java | 31 -- .../wordpress/android/login/LoginMode.java | 37 -- .../android/login/util/AvatarHelper.kt | 77 ---- .../android/login/util/ContextExtensions.kt | 29 -- .../android/login/util/SiteUtils.java | 57 --- .../login/widgets/WPLoginInputRow.java | 204 ---------- .../material_on_surface_emphasis_low.xml | 4 - .../drawable-hdpi/ic_password_visibility.png | Bin 470 -> 0 bytes .../ic_password_visibility_off.png | Bin 507 -> 0 bytes .../drawable-hdpi/login_notification_icon.png | Bin 719 -> 0 bytes .../drawable-mdpi/ic_password_visibility.png | Bin 309 -> 0 bytes .../ic_password_visibility_off.png | Bin 351 -> 0 bytes .../drawable-xhdpi/ic_password_visibility.png | Bin 593 -> 0 bytes .../ic_password_visibility_off.png | Bin 629 -> 0 bytes .../ic_password_visibility.png | Bin 868 -> 0 bytes .../ic_password_visibility_off.png | Bin 884 -> 0 bytes .../login_notification_icon.png | Bin 1477 -> 0 bytes .../ic_password_visibility.png | Bin 1155 -> 0 bytes .../ic_password_visibility_off.png | Bin 1201 -> 0 bytes .../main/res/drawable/ic_globe_grey_24dp.xml | 13 - .../src/main/res/drawable/ic_google_60dp.xml | 28 -- .../drawable/ic_help_outline_white_24dp.xml | 11 - .../ic_user_circle_no_padding_grey_24dp.xml | 9 - .../res/drawable/login_site_address_help.xml | 113 ------ .../drawable/selector_password_visibility.xml | 11 - .../src/main/res/layout/login_input_row.xml | 25 -- .../src/main/res/values-night/colors.xml | 6 - .../src/main/res/values-night/themes.xml | 27 -- libs/login/src/main/res/values/attrs.xml | 15 - libs/login/src/main/res/values/colors.xml | 27 -- libs/login/src/main/res/values/dimens.xml | 33 -- libs/login/src/main/res/values/shapes.xml | 11 - libs/login/src/main/res/values/strings.xml | 150 ------- libs/login/src/main/res/values/styles.xml | 117 ------ libs/login/src/main/res/values/themes.xml | 74 ---- libs/login/src/main/res/values/types.xml | 70 ---- settings.gradle | 2 - 88 files changed, 702 insertions(+), 2163 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt rename {libs/login/src/main/java/org/wordpress/android => WordPress/src/main/java/org/wordpress/android/ui/accounts}/login/LoginAnalyticsListener.kt (71%) delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginPrologueListener.kt rename {libs/login/src/main/java/org/wordpress/android => WordPress/src/main/java/org/wordpress/android/ui/accounts}/login/LoginSiteAddressValidator.java (96%) rename {libs/login => WordPress}/src/main/res/color/login_on_background_medium_selector.xml (100%) rename {libs/login => WordPress}/src/main/res/color/login_on_surface_high_selector.xml (100%) rename {libs/login => WordPress}/src/main/res/color/login_on_surface_medium_selector.xml (100%) rename {libs/login => WordPress}/src/main/res/drawable/login_toolbar_icon.xml (100%) rename {libs/login => WordPress}/src/main/res/layout/login_form_screen.xml (100%) rename {libs/login => WordPress}/src/main/res/layout/login_site_address_screen.xml (58%) rename {libs/login => WordPress}/src/main/res/layout/toolbar_login.xml (100%) rename {libs/login => WordPress}/src/main/res/menu/menu_login.xml (100%) create mode 100644 WordPress/src/main/res/values-night/styles_login.xml rename libs/login/src/main/res/values-v27/themes.xml => WordPress/src/main/res/values-v27/styles_login.xml (100%) delete mode 100644 WordPress/src/test/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCaseTest.kt rename {libs/login/src/test/java/org/wordpress/android => WordPress/src/test/java/org/wordpress/android/ui/accounts}/login/LoginSiteAddressValidatorTest.java (98%) delete mode 100644 libs/login/build.gradle delete mode 100644 libs/login/developer.properties-example delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/AppLoginConfig.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginBaseFormFragment.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginListener.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/LoginMode.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/util/AvatarHelper.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/util/ContextExtensions.kt delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/util/SiteUtils.java delete mode 100644 libs/login/src/main/java/org/wordpress/android/login/widgets/WPLoginInputRow.java delete mode 100644 libs/login/src/main/res/color/material_on_surface_emphasis_low.xml delete mode 100644 libs/login/src/main/res/drawable-hdpi/ic_password_visibility.png delete mode 100644 libs/login/src/main/res/drawable-hdpi/ic_password_visibility_off.png delete mode 100644 libs/login/src/main/res/drawable-hdpi/login_notification_icon.png delete mode 100644 libs/login/src/main/res/drawable-mdpi/ic_password_visibility.png delete mode 100644 libs/login/src/main/res/drawable-mdpi/ic_password_visibility_off.png delete mode 100644 libs/login/src/main/res/drawable-xhdpi/ic_password_visibility.png delete mode 100644 libs/login/src/main/res/drawable-xhdpi/ic_password_visibility_off.png delete mode 100644 libs/login/src/main/res/drawable-xxhdpi/ic_password_visibility.png delete mode 100644 libs/login/src/main/res/drawable-xxhdpi/ic_password_visibility_off.png delete mode 100644 libs/login/src/main/res/drawable-xxhdpi/login_notification_icon.png delete mode 100644 libs/login/src/main/res/drawable-xxxhdpi/ic_password_visibility.png delete mode 100644 libs/login/src/main/res/drawable-xxxhdpi/ic_password_visibility_off.png delete mode 100644 libs/login/src/main/res/drawable/ic_globe_grey_24dp.xml delete mode 100644 libs/login/src/main/res/drawable/ic_google_60dp.xml delete mode 100644 libs/login/src/main/res/drawable/ic_help_outline_white_24dp.xml delete mode 100644 libs/login/src/main/res/drawable/ic_user_circle_no_padding_grey_24dp.xml delete mode 100644 libs/login/src/main/res/drawable/login_site_address_help.xml delete mode 100644 libs/login/src/main/res/drawable/selector_password_visibility.xml delete mode 100644 libs/login/src/main/res/layout/login_input_row.xml delete mode 100644 libs/login/src/main/res/values-night/colors.xml delete mode 100644 libs/login/src/main/res/values-night/themes.xml delete mode 100644 libs/login/src/main/res/values/attrs.xml delete mode 100644 libs/login/src/main/res/values/colors.xml delete mode 100644 libs/login/src/main/res/values/dimens.xml delete mode 100644 libs/login/src/main/res/values/shapes.xml delete mode 100644 libs/login/src/main/res/values/strings.xml delete mode 100644 libs/login/src/main/res/values/styles.xml delete mode 100644 libs/login/src/main/res/values/themes.xml delete mode 100644 libs/login/src/main/res/values/types.xml diff --git a/WordPress/build.gradle b/WordPress/build.gradle index 36700501aa7b..19a0a644ebd6 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -392,7 +392,6 @@ dependencies { strictly libs.wordpress.utils.get().version } } - implementation project(":libs:login") implementation project(":libs:posttypes") implementation("$gradle.ext.aboutAutomatticBinaryPath:${libs.versions.automattic.about.get()}") diff --git a/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt b/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt index 520e23b4d4aa..9c4178afb865 100644 --- a/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt +++ b/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt @@ -28,6 +28,7 @@ import androidx.fragment.app.viewModels import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.isActive import org.wordpress.android.R +import org.wordpress.android.ui.accounts.LoginActivity import org.wordpress.android.ui.accounts.login.components.LoopingTextWithBackground import org.wordpress.android.ui.accounts.login.components.PrimaryButton import org.wordpress.android.ui.accounts.login.components.SecondaryButton @@ -41,7 +42,7 @@ val LocalPosition = compositionLocalOf { 0f } @AndroidEntryPoint class LoginPrologueRevampedFragment : Fragment() { - private lateinit var loginPrologueListener: LoginPrologueListener + private lateinit var loginActivity: LoginActivity private val viewModel by viewModels() override fun onCreateView( @@ -55,11 +56,11 @@ class LoginPrologueRevampedFragment : Fragment() { LoginScreenRevamped( onWpComLoginClicked = { viewModel.onWpComLoginClicked() - loginPrologueListener.showWPcomLoginScreen(this.context) + loginActivity.showWPcomLoginScreen(this.context) }, onSiteAddressLoginClicked = { viewModel.onSiteAddressLoginClicked() - loginPrologueListener.loginViaSiteAddress() + loginActivity.loginViaSiteAddress() }, ) } @@ -69,8 +70,8 @@ class LoginPrologueRevampedFragment : Fragment() { override fun onAttach(context: Context) { super.onAttach(context) - check(context is LoginPrologueListener) { "$context must implement LoginPrologueListener" } - loginPrologueListener = context + check(context is LoginActivity) { "$context must be LoginActivity" } + loginActivity = context } companion object { diff --git a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java index b92134a05be1..208b2b8e518a 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java @@ -3,8 +3,7 @@ import org.wordpress.android.BuildConfig; import org.wordpress.android.fluxc.store.AccountStore; import org.wordpress.android.fluxc.store.SiteStore; -import org.wordpress.android.login.AppLoginConfig; -import org.wordpress.android.login.LoginAnalyticsListener; +import org.wordpress.android.ui.accounts.login.LoginAnalyticsListener; import org.wordpress.android.ui.accounts.UnifiedLoginTracker; import org.wordpress.android.ui.accounts.login.LoginAnalyticsTracker; @@ -21,9 +20,4 @@ public LoginAnalyticsListener provideAnalyticsListener(AccountStore accountStore UnifiedLoginTracker unifiedLoginTracker) { return new LoginAnalyticsTracker(accountStore, siteStore, unifiedLoginTracker); } - - @Provides - public AppLoginConfig provideAppLoginConfig() { - return () -> BuildConfig.IS_JETPACK_APP; - } } diff --git a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt index 47a361ce22e9..d01ff647edfb 100644 --- a/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/support/ZendeskHelper.kt @@ -15,7 +15,7 @@ import org.wordpress.android.analytics.AnalyticsTracker.Stat import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.AccountStore import org.wordpress.android.fluxc.store.SiteStore -import org.wordpress.android.login.BuildConfig +import org.wordpress.android.BuildConfig import org.wordpress.android.ui.accounts.HelpActivity.Origin import org.wordpress.android.ui.notifications.utils.NotificationsUtils import org.wordpress.android.ui.prefs.AppPrefs diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index 547f0fe86f69..4b779ce8fde3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -30,7 +30,7 @@ import org.wordpress.android.fluxc.network.utils.StatsGranularity; import org.wordpress.android.imageeditor.EditImageActivity; import org.wordpress.android.imageeditor.preview.PreviewImageFragment.Companion.EditImageData; -import org.wordpress.android.login.LoginMode; +import org.wordpress.android.ui.accounts.LoginFlow; import org.wordpress.android.models.ReaderPost; import org.wordpress.android.networking.SSLCertsViewActivity; import org.wordpress.android.push.NotificationType; @@ -151,9 +151,9 @@ import static org.wordpress.android.analytics.AnalyticsTracker.Stat.READER_ARTICLE_REBLOGGED; import static org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_ACCESS_ERROR; import static org.wordpress.android.imageeditor.preview.PreviewImageFragment.ARG_EDIT_IMAGE_DATA; -import static org.wordpress.android.login.LoginMode.JETPACK_LOGIN_ONLY; -import static org.wordpress.android.login.LoginMode.JETPACK_REST_CONNECT; -import static org.wordpress.android.login.LoginMode.WPCOM_LOGIN_ONLY; +import static org.wordpress.android.ui.accounts.LoginFlow.PROLOGUE; +import static org.wordpress.android.ui.accounts.LoginFlow.JETPACK_REST_CONNECT; +import static org.wordpress.android.ui.accounts.LoginFlow.WPCOM_LOGIN; import static org.wordpress.android.push.NotificationsProcessingService.ARG_NOTIFICATION_TYPE; import static org.wordpress.android.ui.WPWebViewActivity.ENCODING_UTF8; import static org.wordpress.android.ui.blaze.blazepromote.BlazePromoteParentActivityKt.ARG_BLAZE_FLOW_SOURCE; @@ -1494,7 +1494,7 @@ public static void showSignInForResult(Activity activity, boolean clearTop) { public static void showSignInForResultWpComOnly(Activity activity) { Intent intent = new Intent(activity, LoginActivity.class); - WPCOM_LOGIN_ONLY.putInto(intent); + WPCOM_LOGIN.putInto(intent); activity.startActivityForResult(intent, RequestCodes.ADD_ACCOUNT); } @@ -1516,7 +1516,7 @@ public static void showSignInForResultJetpackOnly(Activity activity) { Intent intent = new Intent(activity, LoginActivity.class); intent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - JETPACK_LOGIN_ONLY.putInto(intent); + PROLOGUE.putInto(intent); activity.startActivityForResult(intent, RequestCodes.ADD_ACCOUNT); } @@ -1592,33 +1592,33 @@ public static void viewSuggestionsForResult(@NonNull Activity activity, public static void addSelfHostedSiteForResult(Activity activity) { Intent intent; intent = new Intent(activity, LoginActivity.class); - LoginMode.SELFHOSTED_ONLY.putInto(intent); + LoginFlow.SELFHOSTED_ONLY.putInto(intent); activity.startActivityForResult(intent, RequestCodes.ADD_ACCOUNT); } public static void loginForDeeplink(Activity activity) { Intent intent; intent = new Intent(activity, LoginActivity.class); - LoginMode.WPCOM_LOGIN_DEEPLINK.putInto(intent); + LoginFlow.WPCOM_LOGIN_DEEPLINK.putInto(intent); activity.startActivityForResult(intent, RequestCodes.DO_LOGIN); } public static void loginForShareIntent(Activity activity) { Intent intent = new Intent(activity, LoginActivity.class); - LoginMode.SHARE_INTENT.putInto(intent); + LoginFlow.SHARE_INTENT.putInto(intent); activity.startActivityForResult(intent, RequestCodes.DO_LOGIN); } public static void loginWithoutMagicLink(Activity activity) { Intent intent; intent = new Intent(activity, LoginActivity.class); - LoginMode.WPCOM_LOGIN_DEEPLINK.putInto(intent); + LoginFlow.WPCOM_LOGIN_DEEPLINK.putInto(intent); activity.startActivityForResult(intent, RequestCodes.DO_LOGIN); } public static void loginForJetpackStats(Fragment fragment) { Intent intent = new Intent(fragment.getActivity(), LoginActivity.class); - LoginMode.JETPACK_STATS.putInto(intent); + LoginFlow.JETPACK_STATS.putInto(intent); fragment.startActivityForResult(intent, RequestCodes.DO_LOGIN); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionResultActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionResultActivity.java index 51da38fab731..410c1b3705f8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionResultActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionResultActivity.java @@ -18,7 +18,7 @@ import org.wordpress.android.fluxc.model.SiteModel; import org.wordpress.android.fluxc.store.AccountStore; import org.wordpress.android.fluxc.store.SiteStore; -import org.wordpress.android.login.LoginMode; +import org.wordpress.android.ui.accounts.LoginFlow; import org.wordpress.android.ui.accounts.LoginActivity; import org.wordpress.android.ui.main.BaseAppCompatActivity; import org.wordpress.android.ui.stats.refresh.utils.StatsLaunchedFrom; @@ -103,7 +103,7 @@ public void handleOnBackPressed() { } else { // An edgecase when the user is logged out in the app but logged in in webview Intent loginIntent = new Intent(this, LoginActivity.class); - LoginMode.JETPACK_STATS.putInto(loginIntent); + LoginFlow.JETPACK_STATS.putInto(loginIntent); this.startActivityForResult(loginIntent, JETPACK_LOGIN); } } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionWebViewActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionWebViewActivity.java index 42f4bb3d8b23..d7b9f12f709f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionWebViewActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/JetpackConnectionWebViewActivity.java @@ -14,7 +14,7 @@ import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; import org.wordpress.android.fluxc.model.SiteModel; -import org.wordpress.android.login.LoginMode; +import org.wordpress.android.ui.accounts.LoginFlow; import org.wordpress.android.ui.JetpackConnectionWebViewClient.JetpackConnectionWebViewClientListener; import org.wordpress.android.ui.accounts.LoginActivity; @@ -146,7 +146,7 @@ public void onRequiresWPComLogin(WebView webView, String redirectPage) { @Override public void onRequiresJetpackLogin() { Intent loginIntent = new Intent(this, LoginActivity.class); - LoginMode.JETPACK_STATS.putInto(loginIntent); + LoginFlow.JETPACK_STATS.putInto(loginIntent); loginIntent.putExtra(LoginActivity.ARG_JETPACK_CONNECT_SOURCE, mSource); startActivityForResult(loginIntent, JETPACK_LOGIN); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index da368d1148e0..386e03187715 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -31,9 +31,8 @@ import org.wordpress.android.fluxc.store.SiteStore.ConnectSiteInfoPayload; import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged; import org.wordpress.android.util.SiteUtils; -import org.wordpress.android.login.LoginAnalyticsListener; -import org.wordpress.android.login.LoginListener; -import org.wordpress.android.login.LoginMode; +import org.wordpress.android.ui.accounts.login.LoginAnalyticsListener; +import org.wordpress.android.ui.accounts.LoginFlow; import org.wordpress.android.ui.accounts.login.applicationpassword.LoginSiteApplicationPasswordFragment; import org.wordpress.android.support.ZendeskExtraTags; import org.wordpress.android.ui.ActivityLauncher; @@ -41,12 +40,7 @@ import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowNoJetpackSites; import org.wordpress.android.ui.accounts.LoginNavigationEvents.ShowSiteAddressError; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Flow; -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Source; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Step; -import org.wordpress.android.ui.accounts.login.LoginCompletionUseCase; -import org.wordpress.android.ui.accounts.login.LoginCompletionUseCase.LoginCompletionAction; -import org.wordpress.android.ui.accounts.login.LoginCompletionUseCase.MainNavigationDestination; -import org.wordpress.android.ui.accounts.login.LoginPrologueListener; import org.wordpress.android.ui.accounts.login.LoginPrologueRevampedFragment; import org.wordpress.android.ui.accounts.login.WPcomLoginHelper; import org.wordpress.android.ui.accounts.login.jetpack.LoginNoSitesFragment; @@ -80,7 +74,7 @@ import static org.wordpress.android.util.ActivityUtils.hideKeyboard; @AndroidEntryPoint -public class LoginActivity extends BaseAppCompatActivity implements LoginListener, LoginPrologueListener, +public class LoginActivity extends BaseAppCompatActivity implements HasAndroidInjector, BasicDialogPositiveClickInterface { public static final String ARG_JETPACK_CONNECT_SOURCE = "ARG_JETPACK_CONNECT_SOURCE"; public static final String MAGIC_LOGIN = "magic-login"; @@ -89,8 +83,8 @@ public class LoginActivity extends BaseAppCompatActivity implements LoginListene private static final String KEY_UNIFIED_TRACKER_SOURCE = "KEY_UNIFIED_TRACKER_SOURCE"; private static final String KEY_UNIFIED_TRACKER_FLOW = "KEY_UNIFIED_TRACKER_FLOW"; - // Static field to preserve login mode across OAuth flow (when callback creates new activity) - private static LoginMode sPendingLoginMode; + // Static field to preserve login flow across OAuth flow (when callback creates new activity) + private static LoginFlow sPendingLoginFlow; // Static field to track if we're in a share flow (for self-hosted login via ApplicationPasswordLoginActivity) private static boolean sIsShareFlowPending; @@ -105,7 +99,7 @@ public static boolean consumeShareFlowPending() { return result; } - private LoginMode mLoginMode; + private LoginFlow mLoginFlow; private LoginViewModel mViewModel; @Inject protected WPcomLoginHelper mLoginHelper; @@ -122,7 +116,6 @@ public static boolean consumeShareFlowPending() { private ArrayList mOldSitesIdsForLoginUpdate; @Inject ExperimentalFeatures mExperimentalFeatures; - @Inject LoginCompletionUseCase mLoginCompletionUseCase; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -140,7 +133,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { return; } else { // Not an OAuth callback - clear any pending login mode from a previous flow - sPendingLoginMode = null; + sPendingLoginFlow = null; } // Start preloading the WordPress.com login page if needed – this avoids visual hitches @@ -148,10 +141,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { mLoginHelper.bindCustomTabsService(this); // go no further if the user is already logged in and this is the login screen shown at startup - // FULL = WPAndroid - // JETPACK_LOGIN_ONLY = JPAndroid - LoginMode loginMode = getLoginMode(); - if ((mLoginHelper.isLoggedIn()) && (loginMode == LoginMode.FULL || loginMode == LoginMode.JETPACK_LOGIN_ONLY)) { + LoginFlow loginFlow = getLoginFlow(); + if ((mLoginHelper.isLoggedIn()) && (loginFlow == LoginFlow.PROLOGUE)) { // Show loading UI while we fetch account and sites in the background setContentView(R.layout.login_loading); this.loggedInAndFinish(new ArrayList(), true); @@ -165,37 +156,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { mLoginAnalyticsListener.trackLoginAccessed(); - switch (loginMode) { - case FULL: - case JETPACK_LOGIN_ONLY: - mUnifiedLoginTracker.setSource(Source.DEFAULT); + mUnifiedLoginTracker.setSource(loginFlow.getAnalyticsSource()); + + switch (loginFlow.getInitialScreen()) { + case PROLOGUE: showFragment(new LoginPrologueRevampedFragment(), LoginPrologueRevampedFragment.TAG); break; - case WPCOM_LOGIN_ONLY: - case JETPACK_REST_CONNECT: - mUnifiedLoginTracker.setSource(Source.ADD_WORDPRESS_COM_ACCOUNT); + case WPCOM_OAUTH: showWPcomLoginScreen(this); break; - case SELFHOSTED_ONLY: - mUnifiedLoginTracker.setSource(Source.SELF_HOSTED); + case SELF_HOSTED: showFragment(new LoginSiteApplicationPasswordFragment(), LoginSiteApplicationPasswordFragment.TAG); break; - case JETPACK_STATS: - mUnifiedLoginTracker.setSource(Source.JETPACK); - showWPcomLoginScreen(this); - break; - case WPCOM_LOGIN_DEEPLINK: - mUnifiedLoginTracker.setSource(Source.DEEPLINK); - showWPcomLoginScreen(this); - break; - case WPCOM_REAUTHENTICATE: - mUnifiedLoginTracker.setSource(Source.REAUTHENTICATION); - showWPcomLoginScreen(this); - break; - case SHARE_INTENT: - mUnifiedLoginTracker.setSource(Source.SHARE); - showFragment(new LoginPrologueRevampedFragment(), LoginPrologueRevampedFragment.TAG); - break; } } else { String source = savedInstanceState.getString(KEY_UNIFIED_TRACKER_SOURCE); @@ -223,7 +195,7 @@ private void initViewModel() { } @Override - public void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putString(KEY_UNIFIED_TRACKER_SOURCE, mUnifiedLoginTracker.getSource().getValue()); Flow flow = mUnifiedLoginTracker.getFlow(); @@ -233,7 +205,7 @@ public void onSaveInstanceState(Bundle outState) { } @Override - protected void onNewIntent(Intent intent) { + protected void onNewIntent(@NonNull Intent intent) { super.onNewIntent(intent); setIntent(intent); @@ -263,7 +235,7 @@ protected void onResume() { super.onResume(); // Check if self-hosted login completed while in share flow // ApplicationPasswordLoginActivity finishes back here after successful login - if (getLoginMode() == LoginMode.SHARE_INTENT && mSiteStore.hasSite()) { + if (getLoginFlow() == LoginFlow.SHARE_INTENT && mSiteStore.hasSite()) { setResult(Activity.RESULT_OK); finish(); } @@ -308,30 +280,23 @@ private void finishLoginAfterSitesLoaded() { * This is the common exit point for successful logins. */ private void navigateToMainActivityOrFinish() { - MainNavigationDestination destination = - mLoginCompletionUseCase.getMainNavigationDestination(getLoginMode()); - switch (destination) { - case MAIN_ACTIVITY: - // Select the primary site after WP.com login - ActivityLauncher.showMainActivity(this, false, true); - break; - case FINISH_ONLY: - default: - // For other modes (JETPACK_STATS, JETPACK_REST_CONNECT, WPCOM_LOGIN_DEEPLINK, - // WPCOM_REAUTHENTICATE, etc.), just finish and let the caller handle navigation - break; + LoginFlow.CompletionBehavior behavior = getLoginFlow().getCompletionBehavior(); + if (behavior == LoginFlow.CompletionBehavior.MAIN_ACTIVITY) { + // Select the primary site after WP.com login + ActivityLauncher.showMainActivity(this, false, true); } + // For FINISH and FINISH_WITH_SITE, just finish and let the caller handle navigation setResult(Activity.RESULT_OK); finish(); } - private void showFragment(Fragment fragment, String tag) { + private void showFragment(@NonNull Fragment fragment, @NonNull String tag) { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.fragment_container, fragment, tag); fragmentTransaction.commit(); } - private void slideInFragment(Fragment fragment, boolean shouldAddToBackStack, String tag) { + private void slideInFragment(@NonNull Fragment fragment, boolean shouldAddToBackStack, @NonNull String tag) { FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.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); @@ -342,33 +307,22 @@ private void slideInFragment(Fragment fragment, boolean shouldAddToBackStack, St fragmentTransaction.commitAllowingStateLoss(); } - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == android.R.id.home) { - getOnBackPressedDispatcher().onBackPressed(); - return true; - } - - return false; - } - - @Override - public LoginMode getLoginMode() { - if (mLoginMode != null) { + public LoginFlow getLoginFlow() { + if (mLoginFlow != null) { // returned the cached value - return mLoginMode; + return mLoginFlow; } - // compute and cache the Login mode - mLoginMode = LoginMode.fromIntent(getIntent()); + // compute and cache the Login flow + mLoginFlow = LoginFlow.fromIntent(getIntent()); - // If the mode is FULL (default) but we have a pending mode from an OAuth flow, use that instead - if (mLoginMode == LoginMode.FULL && sPendingLoginMode != null) { - mLoginMode = sPendingLoginMode; - sPendingLoginMode = null; // Clear after use + // If the flow is PROLOGUE (default) but we have a pending flow from an OAuth callback, use that instead + if (mLoginFlow == LoginFlow.PROLOGUE && sPendingLoginFlow != null) { + mLoginFlow = sPendingLoginFlow; + sPendingLoginFlow = null; // Clear after use } - return mLoginMode; + return mLoginFlow; } private void loggedInAndFinish(ArrayList oldSitesIds, boolean doLoginUpdate) { @@ -385,16 +339,14 @@ private void loggedInAndFinish(ArrayList oldSitesIds, boolean doLoginUp return; // Wait for onAccountChanged -> onSiteChanged before navigating } - LoginCompletionAction action = mLoginCompletionUseCase.getLoginCompletionAction(getLoginMode()); - switch (action) { - case FINISH_WITH_NEW_SITE: + LoginFlow.CompletionBehavior behavior = getLoginFlow().getCompletionBehavior(); + switch (behavior) { + case FINISH_WITH_SITE: // Handle self-hosted site login - find the newly added site and return its ID finishWithNewlyAddedSiteId(oldSitesIds); break; - case FINISH_ONLY: - // WooCommerce handles its own navigation - break; - case NAVIGATE_TO_MAIN: + case MAIN_ACTIVITY: + case FINISH: default: // For all other modes, use the common navigation logic navigateToMainActivityOrFinish(); @@ -425,14 +377,12 @@ private void finishWithNewlyAddedSiteId(ArrayList oldSitesIds) { finish(); } - // LoginPrologueListener implementation methods - public void showWPcomLoginScreen(@NonNull Context context) { AnalyticsTracker.track(AnalyticsTracker.Stat.LOGIN_WPCOM_WEBVIEW); mUnifiedLoginTracker.setFlowAndStep(Flow.WORDPRESS_COM_WEB, Step.WPCOM_WEB_START); // Save the current login mode so it survives the OAuth callback (which creates a new activity) - sPendingLoginMode = getLoginMode(); + sPendingLoginFlow = getLoginFlow(); CustomTabsIntent intent = getCustomTabsIntent(); @@ -456,46 +406,24 @@ public void showWPcomLoginScreen(@NonNull Context context) { .build(); } - // LoginListener implementation methods - - @Override public void loginViaSiteAddress() { // Track if we're in a share flow so ApplicationPasswordLoginActivity knows to just finish - if (getLoginMode() == LoginMode.SHARE_INTENT) { + if (getLoginFlow() == LoginFlow.SHARE_INTENT) { sIsShareFlowPending = true; } slideInFragment(new LoginSiteApplicationPasswordFragment(), true, LoginSiteApplicationPasswordFragment.TAG); } - @Override - public void alreadyLoggedInWpcom(ArrayList oldSitesIds) { - ToastUtils.showToast(this, R.string.already_logged_in_wpcom, ToastUtils.Duration.LONG); - loggedInAndFinish(oldSitesIds, false); - } - - @Override - public void handleSslCertificateError(MemorizingTrustManager memorizingTrustManager, - final SelfSignedSSLCallback callback) { - SelfSignedSSLUtils.showSSLWarningDialog(this, memorizingTrustManager, new SelfSignedSSLUtils.Callback() { - @Override - public void certificateTrusted() { - callback.certificateTrusted(); - } - }); - } - private void viewHelp(Origin origin) { - List extraSupportTags = getLoginMode() == LoginMode.JETPACK_STATS ? Collections + List extraSupportTags = getLoginFlow() == LoginFlow.JETPACK_STATS ? Collections .singletonList(ZendeskExtraTags.connectingJetpack) : null; ActivityLauncher.viewHelp(this, origin, null, extraSupportTags, mExperimentalFeatures); } - @Override public void helpSiteAddress(String url) { viewHelp(Origin.LOGIN_SITE_ADDRESS); } - @Override public void startPostLoginServices() { // Get reader tags so they're available as soon as the Reader is accessed - done for // both wp.com and self-hosted (self-hosted = "logged out" reader) - note that this @@ -515,11 +443,10 @@ public void onPositiveClicked(@NonNull String instanceTag) { return mDispatchingAndroidInjector; } - @Override public void startOver() { + public void startOver() { // Not used in WordPress app } - @Override public void gotConnectedSiteInfo( @NonNull String siteAddress, @Nullable String redirectUrl, @@ -527,7 +454,6 @@ public void gotConnectedSiteInfo( // Not used in WordPress app } - @Override public void handleSiteAddressError(ConnectSiteInfoPayload siteInfo) { mViewModel.onHandleSiteAddressError(siteInfo); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt new file mode 100644 index 000000000000..a2e67b90e9bd --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt @@ -0,0 +1,109 @@ +package org.wordpress.android.ui.accounts + +import android.content.Intent + +/** + * Represents the different login flows in the app. + * + * @property analyticsSource The analytics source value for tracking + * @property initialScreen The initial screen to show when starting this flow + * @property completionBehavior How the login activity should behave after successful login + */ +enum class LoginFlow( + val analyticsSource: String, + val initialScreen: InitialScreen, + val completionBehavior: CompletionBehavior +) { + /** Default login flow showing the prologue with all login options */ + PROLOGUE( + analyticsSource = "default", + initialScreen = InitialScreen.PROLOGUE, + completionBehavior = CompletionBehavior.MAIN_ACTIVITY + ), + + /** Direct WP.com OAuth login, skipping the prologue */ + WPCOM_LOGIN( + analyticsSource = "add_wordpress_com_account", + initialScreen = InitialScreen.WPCOM_OAUTH, + completionBehavior = CompletionBehavior.MAIN_ACTIVITY + ), + + /** Self-hosted site login only */ + SELFHOSTED_ONLY( + analyticsSource = "self_hosted", + initialScreen = InitialScreen.SELF_HOSTED, + completionBehavior = CompletionBehavior.FINISH_WITH_SITE + ), + + /** Login for viewing Jetpack stats */ + JETPACK_STATS( + analyticsSource = "jetpack", + initialScreen = InitialScreen.WPCOM_OAUTH, + completionBehavior = CompletionBehavior.FINISH + ), + + /** Login for Jetpack REST API connection */ + JETPACK_REST_CONNECT( + analyticsSource = "add_wordpress_com_account", + initialScreen = InitialScreen.WPCOM_OAUTH, + completionBehavior = CompletionBehavior.FINISH + ), + + /** Login triggered by a deep link */ + WPCOM_LOGIN_DEEPLINK( + analyticsSource = "deeplink", + initialScreen = InitialScreen.WPCOM_OAUTH, + completionBehavior = CompletionBehavior.FINISH + ), + + /** Re-authentication after token expiry */ + WPCOM_REAUTHENTICATE( + analyticsSource = "reauthentication", + initialScreen = InitialScreen.WPCOM_OAUTH, + completionBehavior = CompletionBehavior.FINISH + ), + + /** Login triggered by share intent from another app */ + SHARE_INTENT( + analyticsSource = "share", + initialScreen = InitialScreen.PROLOGUE, + completionBehavior = CompletionBehavior.FINISH_WITH_SITE + ); + + /** The initial screen to display when starting a login flow */ + enum class InitialScreen { + /** Show the login prologue with all login options */ + PROLOGUE, + /** Go directly to WP.com OAuth */ + WPCOM_OAUTH, + /** Show the self-hosted site login form */ + SELF_HOSTED + } + + /** How the login activity should behave after successful login */ + enum class CompletionBehavior { + /** Navigate to main activity */ + MAIN_ACTIVITY, + /** Just finish the activity, letting the caller handle navigation */ + FINISH, + /** Finish with the newly added site ID */ + FINISH_WITH_SITE + } + + fun putInto(intent: Intent) { + intent.putExtra(ARG_LOGIN_FLOW, this.name) + } + + companion object { + private const val ARG_LOGIN_FLOW = "ARG_LOGIN_MODE" // Keep same key for compatibility + + @JvmStatic + fun fromIntent(intent: Intent): LoginFlow { + return if (intent.hasExtra(ARG_LOGIN_FLOW)) { + valueOf(intent.getStringExtra(ARG_LOGIN_FLOW)!!) + } else { + PROLOGUE + } + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginMagicLinkInterceptActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginMagicLinkInterceptActivity.java index c632e773603e..a4ed23fb310e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginMagicLinkInterceptActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginMagicLinkInterceptActivity.java @@ -6,7 +6,7 @@ import androidx.annotation.Nullable; -import org.wordpress.android.login.LoginAnalyticsListener; +import org.wordpress.android.ui.accounts.login.LoginAnalyticsListener; import org.wordpress.android.ui.JetpackConnectionSource; import org.wordpress.android.ui.main.BaseAppCompatActivity; import org.wordpress.android.ui.main.WPMainActivity; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt index a66928a4e455..0686361e1b0f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginViewModel.kt @@ -15,7 +15,7 @@ import org.wordpress.android.fluxc.network.discovery.SelfHostedEndpointFinder import org.wordpress.android.fluxc.store.SiteStore import org.wordpress.android.fluxc.store.SiteStore.OnSiteChanged import org.wordpress.android.fluxc.utils.AppLogWrapper -import org.wordpress.android.login.util.SiteUtils +import org.wordpress.android.util.SiteUtils import org.wordpress.android.modules.IO_THREAD import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper.UriLogin diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsListener.kt similarity index 71% rename from libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt rename to WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsListener.kt index e1fc9584fbd5..cc3ab83cc8a4 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginAnalyticsListener.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsListener.kt @@ -1,4 +1,4 @@ -package org.wordpress.android.login +package org.wordpress.android.ui.accounts.login interface LoginAnalyticsListener { fun trackAnalyticsSignIn(isWpcomLogin: Boolean) @@ -8,7 +8,12 @@ interface LoginAnalyticsListener { fun trackLoginMagicLinkSucceeded() fun trackUrlFormViewed() fun trackConnectedSiteInfoRequested(url: String?) - fun trackConnectedSiteInfoFailed(url: String?, errorContext: String?, errorType: String?, errorDescription: String?) + fun trackConnectedSiteInfoFailed( + url: String?, + errorContext: String?, + errorType: String?, + errorDescription: String? + ) fun trackConnectedSiteInfoSucceeded(properties: Map) fun trackFailure(message: String?) fun trackSubmitClicked() diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java index eafcf21aa4b7..c95ab8161ab4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginAnalyticsTracker.java @@ -5,7 +5,6 @@ import org.wordpress.android.analytics.AnalyticsTracker; import org.wordpress.android.fluxc.store.AccountStore; import org.wordpress.android.fluxc.store.SiteStore; -import org.wordpress.android.login.LoginAnalyticsListener; import org.wordpress.android.ui.accounts.UnifiedLoginTracker; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Click; import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Flow; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt deleted file mode 100644 index 023e5d38bc08..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginCompletionUseCase.kt +++ /dev/null @@ -1,62 +0,0 @@ -package org.wordpress.android.ui.accounts.login - -import org.wordpress.android.login.LoginMode -import javax.inject.Inject - -/** - * Use case for determining the appropriate action after login completion. - * This extracts testable logic from LoginActivity. - */ -class LoginCompletionUseCase @Inject constructor() { - /** - * Determines the navigation action to take based on the login mode. - * - * @param loginMode The current login mode - * @return The appropriate navigation action - */ - fun getLoginCompletionAction(loginMode: LoginMode): LoginCompletionAction { - return when (loginMode) { - LoginMode.SHARE_INTENT, - LoginMode.SELFHOSTED_ONLY -> LoginCompletionAction.FINISH_WITH_NEW_SITE - - else -> LoginCompletionAction.NAVIGATE_TO_MAIN - } - } - - /** - * Determines the navigation destination for the main activity flow. - * - * @param loginMode The current login mode - * @return The navigation destination - */ - fun getMainNavigationDestination(loginMode: LoginMode): MainNavigationDestination { - return when (loginMode) { - LoginMode.FULL, - LoginMode.JETPACK_LOGIN_ONLY, - LoginMode.WPCOM_LOGIN_ONLY -> MainNavigationDestination.MAIN_ACTIVITY - else -> MainNavigationDestination.FINISH_ONLY - } - } - - /** - * Enum representing the high-level login completion action. - */ - enum class LoginCompletionAction { - /** Navigate to main activity (or post-signup interstitial) */ - NAVIGATE_TO_MAIN, - /** Finish with the newly added site ID (for self-hosted) */ - FINISH_WITH_NEW_SITE, - /** Just finish the activity (WooCommerce handles its own navigation) */ - FINISH_ONLY - } - - /** - * Enum representing the specific navigation destination. - */ - enum class MainNavigationDestination { - /** Show the main activity */ - MAIN_ACTIVITY, - /** Just finish the activity */ - FINISH_ONLY - } -} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginPrologueListener.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginPrologueListener.kt deleted file mode 100644 index 98b975a38024..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginPrologueListener.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.wordpress.android.ui.accounts.login - -import android.content.Context - -interface LoginPrologueListener { - // Login Prologue callbacks - fun showWPcomLoginScreen(context: Context) - fun loginViaSiteAddress() -} diff --git a/libs/login/src/main/java/org/wordpress/android/login/LoginSiteAddressValidator.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginSiteAddressValidator.java similarity index 96% rename from libs/login/src/main/java/org/wordpress/android/login/LoginSiteAddressValidator.java rename to WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginSiteAddressValidator.java index a6aca03cf282..bd82e3af4f3f 100644 --- a/libs/login/src/main/java/org/wordpress/android/login/LoginSiteAddressValidator.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/LoginSiteAddressValidator.java @@ -1,4 +1,4 @@ -package org.wordpress.android.login; +package org.wordpress.android.ui.accounts.login; import android.util.Patterns; @@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import org.wordpress.android.R; import org.wordpress.android.util.helpers.Debouncer; import java.util.concurrent.TimeUnit; diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt index 1f461ae3b2da..299624b20282 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt @@ -1,16 +1,27 @@ +@file:Suppress("DEPRECATION") + package org.wordpress.android.ui.accounts.login.applicationpassword +import android.app.ProgressDialog +import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.view.ViewStub +import android.view.inputmethod.EditorInfo import android.widget.Button -import android.widget.EditText import android.widget.TextView -import androidx.annotation.LayoutRes -import androidx.appcompat.app.ActionBar +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import com.google.android.material.textfield.TextInputEditText +import com.google.android.material.textfield.TextInputLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.flowWithLifecycle @@ -21,20 +32,24 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.wordpress.android.WordPress import org.wordpress.android.fluxc.store.AccountStore -import org.wordpress.android.login.LoginBaseFormFragment -import org.wordpress.android.login.LoginListener -import org.wordpress.android.login.LoginSiteAddressValidator -import org.wordpress.android.login.R -import org.wordpress.android.login.widgets.WPLoginInputRow -import org.wordpress.android.login.widgets.WPLoginInputRow.OnEditorCommitListener +import org.wordpress.android.ui.accounts.login.LoginAnalyticsListener +import org.wordpress.android.R +import org.wordpress.android.ui.accounts.LoginActivity +import org.wordpress.android.ui.accounts.login.LoginSiteAddressValidator import org.wordpress.android.ui.ActivityNavigator +import org.wordpress.android.util.AppLog +import org.wordpress.android.util.AppLog.T import org.wordpress.android.util.EditTextUtils import org.wordpress.android.util.NetworkUtils import javax.inject.Inject -class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment(), TextWatcher, - OnEditorCommitListener { - private var siteAddressInput: WPLoginInputRow? = null +class LoginSiteApplicationPasswordFragment : Fragment(), TextWatcher { + private var siteAddressInputLayout: TextInputLayout? = null + private var siteAddressInput: TextInputEditText? = null + private var bottomButton: Button? = null + private var progressDialog: ProgressDialog? = null + private var inProgress = false + private var loginActivity: LoginActivity? = null private var loginSiteAddressValidator = LoginSiteAddressValidator() @@ -48,59 +63,75 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment(R.id.login_form_content_stub) + formContainer.layoutResource = R.layout.login_site_address_screen + formContainer.inflate() + + rootView.findViewById(R.id.label).setText(R.string.enter_site_address) + bottomButton = rootView.findViewById(R.id.bottom_button) + bottomButton?.setOnClickListener { discover() } + + return rootView } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mAnalyticsListener.trackUrlFormViewed() + val toolbar = view.findViewById(R.id.toolbar) + (activity as? AppCompatActivity)?.setSupportActionBar(toolbar) + + (activity as? AppCompatActivity)?.supportActionBar?.let { actionBar -> + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setTitle(R.string.log_in) + } + + analyticsListener.trackUrlFormViewed() requireActivity().setTitle(R.string.site_address_login_title) - this.siteAddressInput = view.findViewById(R.id.login_site_address_row) + siteAddressInputLayout = view.findViewById(R.id.login_site_address_input_layout) + siteAddressInput = view.findViewById(R.id.login_site_address_input) siteAddressInput?.addTextChangedListener(this) - siteAddressInput?.setOnEditorCommitListener(this) + siteAddressInput?.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE && bottomButton?.isEnabled == true) { + discover() + } + true + } + + if (savedInstanceState == null) { + @Suppress("DEPRECATION") + try { + EditTextUtils.showSoftInput(siteAddressInput) + } catch (e: Exception) { + AppLog.e(T.MAIN, "Error showing soft input", e) + } + } loginSiteAddressValidator.isValid.observe(viewLifecycleOwner) { enabled -> - bottomButton.isEnabled = enabled + bottomButton?.isEnabled = enabled } loginSiteAddressValidator.errorMessageResId.observe(viewLifecycleOwner) { resId -> if (resId != null) { showError(resId) } else { - siteAddressInput?.setError(null) + siteAddressInputLayout?.error = null + siteAddressInputLayout?.isErrorEnabled = false } } @@ -120,7 +151,7 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment if (loading) { startProgress() @@ -131,26 +162,55 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment - loginSiteAddressValidator.setAddress(EditTextUtils.getText(siteAddressInput.editText)) + siteAddressInput?.let { input -> + loginSiteAddressValidator.setAddress(EditTextUtils.getText(input)) } } @@ -159,28 +219,55 @@ class LoginSiteApplicationPasswordFragment : LoginBaseFormFragment getCurrentSiteIds(SiteStore siteStore, boolean selfhostedOnly) { + ArrayList siteIDs = new ArrayList<>(); + List sites = selfhostedOnly ? siteStore.getSitesAccessedViaXMLRPC() : siteStore.getSites(); + for (SiteModel site : sites) { + siteIDs.add(site.getId()); + } + return siteIDs; + } + public static final String GB_EDITOR_NAME = "gutenberg"; public static final String AZTEC_EDITOR_NAME = "aztec"; public static final String WP_VIDEOPRESS_V5_JETPACK_VERSION = "8.5"; diff --git a/WordPress/src/main/java/org/wordpress/android/util/image/ImagePlaceholderManager.kt b/WordPress/src/main/java/org/wordpress/android/util/image/ImagePlaceholderManager.kt index 7e20ac25412c..f85b52586049 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/image/ImagePlaceholderManager.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/image/ImagePlaceholderManager.kt @@ -4,7 +4,6 @@ import org.wordpress.android.R import javax.inject.Inject import javax.inject.Singleton import org.wordpress.android.editor.R as EditorR -import org.wordpress.android.login.R as LoginR @Singleton class ImagePlaceholderManager @Inject constructor() { @@ -13,7 +12,7 @@ class ImagePlaceholderManager @Inject constructor() { return when (imgType) { ImageType.AVATAR -> R.drawable.bg_rectangle_placeholder_user_32dp ImageType.AVATAR_WITH_BACKGROUND -> R.drawable.bg_oval_placeholder_user_32dp - ImageType.AVATAR_WITHOUT_BACKGROUND -> LoginR.drawable.ic_user_circle_no_padding_grey_24dp + ImageType.AVATAR_WITHOUT_BACKGROUND -> R.drawable.ic_user_placeholder_primary_24 ImageType.BLAVATAR -> R.drawable.bg_rectangle_placeholder_globe_32dp ImageType.P2_BLAVATAR -> R.drawable.bg_rectangle_placeholder_p2_32dp ImageType.BLAVATAR_ROUNDED_CORNERS -> R.drawable.bg_rectangle_placeholder_radius_4dp_globe_32dp @@ -39,7 +38,7 @@ class ImagePlaceholderManager @Inject constructor() { return when (imgType) { ImageType.AVATAR -> R.drawable.bg_oval_placeholder ImageType.AVATAR_WITH_BACKGROUND -> R.drawable.bg_oval_placeholder_user_32dp - ImageType.AVATAR_WITHOUT_BACKGROUND -> LoginR.drawable.ic_user_circle_no_padding_grey_24dp + ImageType.AVATAR_WITHOUT_BACKGROUND -> R.drawable.ic_user_placeholder_primary_24 ImageType.BLAVATAR -> R.color.placeholder ImageType.P2_BLAVATAR -> R.color.placeholder ImageType.BLAVATAR_ROUNDED_CORNERS -> R.drawable.bg_rectangle_placeholder_radius_4dp diff --git a/libs/login/src/main/res/color/login_on_background_medium_selector.xml b/WordPress/src/main/res/color/login_on_background_medium_selector.xml similarity index 100% rename from libs/login/src/main/res/color/login_on_background_medium_selector.xml rename to WordPress/src/main/res/color/login_on_background_medium_selector.xml diff --git a/libs/login/src/main/res/color/login_on_surface_high_selector.xml b/WordPress/src/main/res/color/login_on_surface_high_selector.xml similarity index 100% rename from libs/login/src/main/res/color/login_on_surface_high_selector.xml rename to WordPress/src/main/res/color/login_on_surface_high_selector.xml diff --git a/libs/login/src/main/res/color/login_on_surface_medium_selector.xml b/WordPress/src/main/res/color/login_on_surface_medium_selector.xml similarity index 100% rename from libs/login/src/main/res/color/login_on_surface_medium_selector.xml rename to WordPress/src/main/res/color/login_on_surface_medium_selector.xml diff --git a/libs/login/src/main/res/drawable/login_toolbar_icon.xml b/WordPress/src/main/res/drawable/login_toolbar_icon.xml similarity index 100% rename from libs/login/src/main/res/drawable/login_toolbar_icon.xml rename to WordPress/src/main/res/drawable/login_toolbar_icon.xml diff --git a/WordPress/src/main/res/layout/help_activity.xml b/WordPress/src/main/res/layout/help_activity.xml index 7469bef0e6ff..3e5376a60bb8 100644 --- a/WordPress/src/main/res/layout/help_activity.xml +++ b/WordPress/src/main/res/layout/help_activity.xml @@ -53,7 +53,7 @@ android:layout_height="@dimen/avatar_sz_large" android:layout_centerHorizontal="true" android:contentDescription="@string/reader_avatar_desc" - android:src="@drawable/ic_user_circle_no_padding_grey_24dp" /> + android:src="@drawable/ic_user_placeholder_primary_24" /> + tools:srcCompat="@drawable/ic_user_placeholder_primary_24" /> + android:src="@drawable/ic_user_placeholder_primary_24" /> - + android:hint="@string/login_site_address"> + + + diff --git a/WordPress/src/main/res/layout/reader_listitem_comment.xml b/WordPress/src/main/res/layout/reader_listitem_comment.xml index aaf31b73be81..6e069f0fbe01 100644 --- a/WordPress/src/main/res/layout/reader_listitem_comment.xml +++ b/WordPress/src/main/res/layout/reader_listitem_comment.xml @@ -77,7 +77,7 @@ android:contentDescription="@string/description_user" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:src="@drawable/ic_user_circle_no_padding_grey_24dp" /> + tools:src="@drawable/ic_user_placeholder_primary_24" /> + + + + + + diff --git a/libs/login/src/main/res/values-v27/themes.xml b/WordPress/src/main/res/values-v27/styles_login.xml similarity index 100% rename from libs/login/src/main/res/values-v27/themes.xml rename to WordPress/src/main/res/values-v27/styles_login.xml diff --git a/WordPress/src/main/res/values/dimens.xml b/WordPress/src/main/res/values/dimens.xml index 008e86b4f402..ea7854c19be7 100644 --- a/WordPress/src/main/res/values/dimens.xml +++ b/WordPress/src/main/res/values/dimens.xml @@ -102,6 +102,7 @@ 24dp 32dp 48dp + 92dp 64dp 68dp 114dp diff --git a/WordPress/src/main/res/values/styles.xml b/WordPress/src/main/res/values/styles.xml index 64b9c1cdcabe..d49098b56afe 100644 --- a/WordPress/src/main/res/values/styles.xml +++ b/WordPress/src/main/res/values/styles.xml @@ -1367,7 +1367,7 @@ wrap_content center center - @color/login_on_surface_high_selector + ?attr/colorOnSurface + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - diff --git a/libs/login/src/main/res/values/attrs.xml b/libs/login/src/main/res/values/attrs.xml deleted file mode 100644 index 3f5b74b856cd..000000000000 --- a/libs/login/src/main/res/values/attrs.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/values/colors.xml b/libs/login/src/main/res/values/colors.xml deleted file mode 100644 index b083926356f7..000000000000 --- a/libs/login/src/main/res/values/colors.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - #399ce3 - #0675c4 - #044b7a - - #c9356e - #8c1749 - - #d63638 - - #ffffff - #121212 - #000000 - - - #61121212 - - - #87a6bc - - - #c8d7e1 - #0087be - diff --git a/libs/login/src/main/res/values/dimens.xml b/libs/login/src/main/res/values/dimens.xml deleted file mode 100644 index 1dd0f0273f50..000000000000 --- a/libs/login/src/main/res/values/dimens.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - 0.12 - - 0dp - 24dp - 1dp - 2dp - 4dp - 6dp - 8dp - 10dp - 12dp - 16dp - 24dp - 32dp - 48dp - - 18dp - 24dp - 32dp - 38dp - 64dp - 40dp - 32dp - 40dp - - - 92dp - 4dp - - diff --git a/libs/login/src/main/res/values/shapes.xml b/libs/login/src/main/res/values/shapes.xml deleted file mode 100644 index db11611bf767..000000000000 --- a/libs/login/src/main/res/values/shapes.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/values/themes.xml b/libs/login/src/main/res/values/themes.xml deleted file mode 100644 index b1e85c40f188..000000000000 --- a/libs/login/src/main/res/values/themes.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/libs/login/src/main/res/values/types.xml b/libs/login/src/main/res/values/types.xml deleted file mode 100644 index cff70fb995c7..000000000000 --- a/libs/login/src/main/res/values/types.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/settings.gradle b/settings.gradle index 968f72b13ac9..1f9f9364dd8e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -104,7 +104,6 @@ include ':libs:annotations' include ':libs:mocks' include ':libs:fluxc', ':libs:fluxc-annotations', ':libs:fluxc-processor' -include ':libs:login' include ':libs:posttypes' apply from: './config/gradle/included_builds.gradle' @@ -141,7 +140,6 @@ buildCache { def checkForRemoteBuildCacheOptimizedExperience() { assertSecretsApplied() - assertDeveloperPropertiesMatch("${rootDir}/libs/login/developer.properties") assertDeveloperPropertiesMatch("${rootDir}/libs/fluxc/developer.properties") assertJava21Amazon() } From e761315580fa361ec9f7b1b2afb25f7f7df2c040 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:05:53 -0700 Subject: [PATCH 27/71] Flatten theme heirarchies --- WordPress/src/main/AndroidManifest.xml | 4 +- .../main/res/values-night/styles_login.xml | 54 ++++++-- .../src/main/res/values-v27/styles_login.xml | 57 +++++++- .../src/main/res/values-v29/styles_login.xml | 58 +++++++- .../src/main/res/values/styles_login.xml | 125 +++++++----------- 5 files changed, 205 insertions(+), 93 deletions(-) diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 8c208c58c084..2a0d592311d9 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -130,7 +130,7 @@ @@ -146,7 +146,7 @@ diff --git a/WordPress/src/main/res/values-night/styles_login.xml b/WordPress/src/main/res/values-night/styles_login.xml index 31cb40b8f724..442a52e87454 100644 --- a/WordPress/src/main/res/values-night/styles_login.xml +++ b/WordPress/src/main/res/values-night/styles_login.xml @@ -1,27 +1,61 @@ - - diff --git a/WordPress/src/main/res/values-v27/styles_login.xml b/WordPress/src/main/res/values-v27/styles_login.xml index fc3d18102ebf..e67dafae5120 100644 --- a/WordPress/src/main/res/values-v27/styles_login.xml +++ b/WordPress/src/main/res/values-v27/styles_login.xml @@ -1,10 +1,61 @@ + + diff --git a/WordPress/src/main/res/values-v29/styles_login.xml b/WordPress/src/main/res/values-v29/styles_login.xml index 49f7fc02559d..89ddc106c404 100644 --- a/WordPress/src/main/res/values-v29/styles_login.xml +++ b/WordPress/src/main/res/values-v29/styles_login.xml @@ -1,8 +1,62 @@ + + - diff --git a/WordPress/src/main/res/values/styles_login.xml b/WordPress/src/main/res/values/styles_login.xml index 89c3b40621d4..e3448651654d 100644 --- a/WordPress/src/main/res/values/styles_login.xml +++ b/WordPress/src/main/res/values/styles_login.xml @@ -83,76 +83,6 @@ @color/login_on_surface_high_selector - - - - - - - - - - - @@ -276,22 +206,65 @@ - + @color/white + @color/white + @color/red_50 + @color/white + @color/white + @color/black + @color/black + @color/white - - diff --git a/WordPress/src/main/res/values/styles_login.xml b/WordPress/src/main/res/values/styles_login.xml index e3448651654d..a9a2eaa4630c 100644 --- a/WordPress/src/main/res/values/styles_login.xml +++ b/WordPress/src/main/res/values/styles_login.xml @@ -141,8 +141,6 @@ @style/Animation.MaterialComponents.BottomSheetDialog - @android:color/transparent - ?attr/colorSurface @style/Widget.LoginFlow.BottomSheet.Modal diff --git a/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueFragment.kt b/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueFragment.kt deleted file mode 100644 index bd4456cb60f3..000000000000 --- a/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueFragment.kt +++ /dev/null @@ -1,134 +0,0 @@ -package org.wordpress.android.ui.accounts.login - -import android.content.Context -import android.os.Bundle -import android.view.View -import androidx.annotation.FloatRange -import androidx.fragment.app.Fragment -import androidx.viewpager2.widget.ViewPager2 -import com.google.android.material.tabs.TabLayoutMediator -import dagger.hilt.android.AndroidEntryPoint -import org.wordpress.android.R -import org.wordpress.android.analytics.AnalyticsTracker -import org.wordpress.android.analytics.AnalyticsTracker.Stat.LOGIN_PROLOGUE_VIEWED -import org.wordpress.android.databinding.LoginSignupScreenBinding -import org.wordpress.android.ui.accounts.LoginActivity -import org.wordpress.android.ui.accounts.UnifiedLoginTracker -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Click -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Flow -import org.wordpress.android.ui.accounts.UnifiedLoginTracker.Step.PROLOGUE -import org.wordpress.android.util.analytics.AnalyticsUtils -import javax.inject.Inject - -@AndroidEntryPoint -class LoginPrologueFragment : Fragment(R.layout.login_signup_screen) { - private lateinit var loginActivity: LoginActivity - - @Inject - lateinit var unifiedLoginTracker: UnifiedLoginTracker - - companion object { - const val TAG = "login_prologue_fragment_tag" - } - - override fun onAttach(context: Context) { - super.onAttach(context) - check(context is LoginActivity) { "$context must be LoginActivity" } - loginActivity = context - } - - @Suppress("DEPRECATION") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - // setting up a full screen flags for the decor view of this fragment, - // that will work with transparent status bar - val decorView: View = view - var flags = decorView.systemUiVisibility - flags = flags or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE - decorView.systemUiVisibility = flags - val binding = LoginSignupScreenBinding.bind(view) - - with(binding.bottomButtonsContainer) { - continueWithWpcomButton.setText(R.string.continue_with_wpcom) - - continueWithWpcomButton.setOnClickListener { - unifiedLoginTracker.trackClick(Click.CONTINUE_WITH_WORDPRESS_COM) - loginActivity.showWPcomLoginScreen(view.context) - } - - enterYourSiteAddressButton.setOnClickListener { - unifiedLoginTracker.trackClick(Click.LOGIN_WITH_SITE_ADDRESS) - loginActivity.loginViaSiteAddress() - } - } - - val adapter = LoginProloguePagerAdapter(this) - - binding.introsPager.adapter = adapter - binding.introsPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - AnalyticsUtils.trackLoginProloguePages(position) - } - }) - binding.introsPager.setPageTransformer(object : ViewPager2.PageTransformer { - @Suppress("MagicNumber") - override fun transformPage(page: View, position: Float) { - // Since we want to achieve the illusion of having a single continuous background, we apply a - // parallax effect to the foreground views of each page, making them enter and exit the screen - // at a different speed than the background, which just follows the speed of the swipe gesture. - page.apply { - applyParallaxEffect(this.findViewById(R.id.promo_title), position, width, 0.25f) - applyParallaxEffect(this.findViewById(R.id.promo_layout_container), position, width, 0.5f) - } - } - - /** - * Apply a parallax effect to a given view. - * - * @param view The view to which the effect should be applied. - * @param pagePosition Position of page relative to the current front-and-center position of the pager. - * 0 is front and center. 1 is one full page position to the right, and -1 is one - * page position to the left. - * @param pageWidth Total width of the page containing the view to which the effect should be applied. - * @param speedFactor The factor by which the speed of the view is altered while the page is scrolled. - * For example, a value of 0.25 would cause the view speed to increase by 25% when - * moving out of the screen and to decrease by 25% when moving in. - */ - private fun applyParallaxEffect( - view: View, - pagePosition: Float, - pageWidth: Int, - @FloatRange(from = 0.0, to = 1.0) speedFactor: Float - ) { - // If pagePosition is between -1 and 1, then at least a portion of the page is visible. - if (pagePosition in -1.0..1.0) view.translationX = pagePosition * pageWidth * speedFactor - } - }) - - if (adapter.itemCount > 1) { - TabLayoutMediator( - binding.tabLayoutIndicator, - binding.introsPager - ) { _, _ -> }.attach() - } - - if (savedInstanceState == null) { - AnalyticsTracker.track(LOGIN_PROLOGUE_VIEWED) - unifiedLoginTracker.track(Flow.PROLOGUE, PROLOGUE) - } - } - - override fun onResume() { - super.onResume() - unifiedLoginTracker.setFlowAndStep(Flow.PROLOGUE, PROLOGUE) - } - - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - // important for accessibility - talkback - activity?.setTitle(R.string.login_prologue_screen_title) - } -} diff --git a/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt b/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt index e8e58bb2ddb5..85233ed01625 100644 --- a/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt +++ b/WordPress/src/wordpress/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt @@ -2,15 +2,16 @@ package org.wordpress.android.ui.accounts.login import android.content.Context import android.content.res.Configuration.UI_MODE_NIGHT_YES -import android.os.Build -import android.os.Build.VERSION_CODES import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,7 +29,6 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection.Rtl import androidx.compose.ui.unit.dp -import androidx.core.view.WindowCompat import androidx.fragment.app.Fragment import org.wordpress.android.R import org.wordpress.android.ui.accounts.LoginActivity @@ -64,26 +64,6 @@ class LoginPrologueRevampedFragment : Fragment() { loginActivity = context } - override fun onResume() { - super.onResume() - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { - setEdgeToEdgeContentDisplay(true) - } - } - - override fun onPause() { - super.onPause() - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { - setEdgeToEdgeContentDisplay(false) - } - } - - private fun setEdgeToEdgeContentDisplay(isEnabled: Boolean) { - val decorFitsSystemWindows = !isEnabled - WindowCompat.setDecorFitsSystemWindows(requireActivity().window, decorFitsSystemWindows) - } - - companion object { const val TAG = "login_prologue_revamped_fragment_tag" } @@ -102,6 +82,7 @@ fun LoginScreenRevamped( val offsetY = with(LocalDensity.current) { 75.dp.toPx() } Box(modifier = Modifier + .fillMaxSize() .background(color = colorResource(id = R.color.login_prologue_revamped_background)) .drawBehind { scale(scaleX = scaleX, scaleY = 1f) { @@ -118,7 +99,11 @@ fun LoginScreenRevamped( ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(vertical = 45.dp) + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .navigationBarsPadding() + .padding(vertical = 45.dp) ) { Tagline(text = stringResource(R.string.login_prologue_revamped_tagline)) PrimaryButton( diff --git a/WordPress/src/wordpress/res/layout/login_signup_screen.xml b/WordPress/src/wordpress/res/layout/login_signup_screen.xml deleted file mode 100644 index b59fada49655..000000000000 --- a/WordPress/src/wordpress/res/layout/login_signup_screen.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - From 210cfe4cad8d7ae121b69018a71d25e764e753b6 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:57:29 -0700 Subject: [PATCH 29/71] Fix edge-to-edge for login --- .../ui/accounts/login/LoginPrologueRevampedFragment.kt | 3 +++ .../applicationpassword/ApplicationPasswordLoginActivity.kt | 2 ++ .../applicationpassword/LoginSiteApplicationPasswordScreen.kt | 4 ++++ .../org/wordpress/android/ui/main/BaseAppCompatActivity.kt | 2 ++ 4 files changed, 11 insertions(+) diff --git a/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt b/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt index 283bbb18a4d4..286e7b5495cd 100644 --- a/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt +++ b/WordPress/src/jetpack/java/org/wordpress/android/ui/accounts/login/LoginPrologueRevampedFragment.kt @@ -6,7 +6,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.width @@ -135,6 +137,7 @@ private fun LoginScreenRevamped( onClick = onWpComLoginClicked, modifier = Modifier.testTag(TestTags.BUTTON_WPCOM_AUTH) ) + Spacer(modifier = Modifier.height(16.dp)) SecondaryButton(onClick = onSiteAddressLoginClicked) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt index 56db194e18ae..061f1eafc195 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordLoginActivity.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.CircularProgressIndicator import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.core.view.WindowCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint @@ -35,6 +36,7 @@ class ApplicationPasswordLoginActivity: BaseAppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + WindowCompat.setDecorFitsSystemWindows(window, false) initViewModel() setContent { AppThemeM3 { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt index 387c8a6b47f9..096375a70702 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt @@ -3,10 +3,12 @@ package org.wordpress.android.ui.accounts.login.applicationpassword import android.util.Patterns import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions @@ -71,6 +73,7 @@ fun LoginSiteApplicationPasswordScreen( Scaffold( modifier = modifier, + contentWindowInsets = WindowInsets(0, 0, 0, 0), topBar = { TopAppBar( title = { Text(text = stringResource(R.string.log_in)) }, @@ -150,6 +153,7 @@ fun LoginSiteApplicationPasswordScreen( onClick = { onContinueClick(cleanedAddress) }, modifier = Modifier .fillMaxWidth() + .navigationBarsPadding() .padding(16.dp), enabled = isValid && !isLoading ) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/BaseAppCompatActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/main/BaseAppCompatActivity.kt index 8f36dfa2e830..cb85ba25354a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/BaseAppCompatActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/BaseAppCompatActivity.kt @@ -39,6 +39,7 @@ import org.wordpress.android.ui.selfhostedusers.SelfHostedUsersActivity import org.wordpress.android.ui.sitemonitor.SiteMonitorParentActivity import org.wordpress.android.ui.subscribers.SubscribersActivity import org.wordpress.android.ui.accounts.LoginActivity +import org.wordpress.android.ui.accounts.applicationpassword.ApplicationPasswordLoginActivity import org.wordpress.android.ui.taxonomies.TermsDataViewActivity /** @@ -108,6 +109,7 @@ private val excludedActivities = listOf( SupportWebViewActivity::class.java.name, // these are excluded because they explicitly enable edge-to-edge + ApplicationPasswordLoginActivity::class.java.name, LoginActivity::class.java.name, MediaSettingsActivity::class.java.name, ReaderPostPagerActivity::class.java.name, From 3d44f861f8df2951df4cd5207d970019a9420a91 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:06:55 -0700 Subject: [PATCH 30/71] Automatically load notifications when switching to that tab --- .../NotificationsListFragmentPage.kt | 16 ++++++++++++++++ .../layout/notifications_list_fragment_page.xml | 11 +++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt index 4289888e9228..f4011cf2f422 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragmentPage.kt @@ -80,6 +80,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l private lateinit var notesAdapter: NotesAdapter private var swipeToRefreshHelper: SwipeToRefreshHelper? = null private var isAnimatingOutNewNotificationsBar = false + private var isFetchingRemoteNotes = false private var tabPosition = 0 private val viewModel: NotificationsListViewModel by viewModels() @@ -171,7 +172,14 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l binding?.apply { if (itemsCount > 0) { hideEmptyView() + progressLoading.visibility = View.GONE + } else if (isFetchingRemoteNotes) { + // Show loading spinner while fetching and list is empty + progressLoading.visibility = View.VISIBLE + actionableEmptyView.visibility = View.GONE + notificationsList.visibility = View.GONE } else { + progressLoading.visibility = View.GONE showEmptyViewForCurrentFilter() } } @@ -185,6 +193,8 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l super.onResume() binding?.hideNewNotificationsBar() EventBus.getDefault().post(NotificationsUnseenStatus(false)) + // Fetch remote notes when resumed (e.g., after login or returning to this screen) + fetchRemoteNotes() } override fun onSaveInstanceState(outState: Bundle) { @@ -268,8 +278,11 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l } if (!NetworkUtils.isNetworkAvailable(activity)) { swipeToRefreshHelper?.isRefreshing = false + isFetchingRemoteNotes = false + updateEmptyLayouts(notesAdapter.itemCount) return } + isFetchingRemoteNotes = true swipeToRefreshHelper?.isRefreshing = true NotificationsUpdateServiceStarter.startService(activity) } @@ -488,6 +501,7 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l if (!isAdded) { return } + isFetchingRemoteNotes = false swipeToRefreshHelper?.isRefreshing = false notesAdapter.addAll(event.notes) } @@ -496,7 +510,9 @@ class NotificationsListFragmentPage : ViewPagerFragment(R.layout.notifications_l @Subscribe(threadMode = MAIN) fun onEventMainThread(error: NotificationsRefreshError?) { if (isAdded) { + isFetchingRemoteNotes = false swipeToRefreshHelper?.isRefreshing = false + updateEmptyLayouts(notesAdapter.itemCount) } } diff --git a/WordPress/src/main/res/layout/notifications_list_fragment_page.xml b/WordPress/src/main/res/layout/notifications_list_fragment_page.xml index 5eb55c9a2669..de0b30185353 100644 --- a/WordPress/src/main/res/layout/notifications_list_fragment_page.xml +++ b/WordPress/src/main/res/layout/notifications_list_fragment_page.xml @@ -27,6 +27,14 @@ + + + app:aevTitle="@string/notifications_empty_unread"> Date: Thu, 5 Feb 2026 11:54:36 -0700 Subject: [PATCH 31/71] Fix Application Password login focus --- .../LoginSiteApplicationPasswordScreen.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt index 096375a70702..20290034327e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordScreen.kt @@ -63,10 +63,6 @@ fun LoginSiteApplicationPasswordScreen( val cleanedAddress = siteAddress.trim().replace(Regex("[\r\n]"), "") val isValid = cleanedAddress.isNotEmpty() && Patterns.WEB_URL.matcher(cleanedAddress).matches() - LaunchedEffect(Unit) { - focusRequester.requestFocus() - } - if (isLoading) { LoadingDialog() } @@ -143,6 +139,10 @@ fun LoginSiteApplicationPasswordScreen( ), singleLine = true ) + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } } Surface( From 7c035f746479047b1953175acd886dd1611cbb3c Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:15:13 -0700 Subject: [PATCH 32/71] Remove login module from CI --- .buildkite/commands/run-unit-tests.sh | 3 --- fastlane/lanes/localization.rb | 1 - 2 files changed, 4 deletions(-) diff --git a/.buildkite/commands/run-unit-tests.sh b/.buildkite/commands/run-unit-tests.sh index 9b1e5be2c019..bd42a8c13a24 100755 --- a/.buildkite/commands/run-unit-tests.sh +++ b/.buildkite/commands/run-unit-tests.sh @@ -14,12 +14,10 @@ set +e :libs:processors:test \ :libs:image-editor:testDebugUnitTest \ :libs:fluxc:testDebugUnitTest \ - :libs:login:testDebugUnitTest \ koverXmlReportWordpressWasabiDebug \ :libs:processors:koverXmlReportJvm \ :libs:image-editor:koverXmlReportDebug \ :libs:fluxc:koverXmlReportDebug \ - :libs:login:koverXmlReportDebug TESTS_EXIT_STATUS=$? set -e echo "" @@ -49,7 +47,6 @@ declare -A TEST_RESULT_DIRS=( ["processors"]="libs/processors/build/test-results/test" ["image-editor"]="libs/image-editor/build/test-results/testDebugUnitTest" ["fluxc"]="libs/fluxc/build/test-results/testDebugUnitTest" - ["login"]="libs/login/build/test-results/testDebugUnitTest" ) # Create temporary directory for collecting all test results diff --git a/fastlane/lanes/localization.rb b/fastlane/lanes/localization.rb index 7a1507239ca7..9f2dade5992e 100644 --- a/fastlane/lanes/localization.rb +++ b/fastlane/lanes/localization.rb @@ -262,7 +262,6 @@ # Which will correctly detect strings from the app's `strings.xml` being used by one of the module. { library: 'Image Editor', strings_path: './libs/image-editor/src/main/res/values/strings.xml', source_id: 'module:image-editor' }, { library: 'Editor', strings_path: './libs/editor/src/main/res/values/strings.xml', source_id: 'module:editor' }, - { library: 'Login Library', strings_path: './libs/login/src/main/res/values/strings.xml', source_id: 'module:login' } ].freeze REMOTE_LIBRARIES_STRINGS_PATHS = [ { From 71694fe2a8a45ed140ebbb60cf8793c4eefd662d Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:10:36 -0700 Subject: [PATCH 33/71] Remove unused styles --- .../src/jetpack/res/values/styles_login.xml | 23 ------- .../src/main/res/layout/login_loading.xml | 3 +- WordPress/src/main/res/values/styles.xml | 61 ------------------- .../src/main/res/values/styles_login.xml | 36 ----------- 4 files changed, 1 insertion(+), 122 deletions(-) diff --git a/WordPress/src/jetpack/res/values/styles_login.xml b/WordPress/src/jetpack/res/values/styles_login.xml index de1807300aa4..045e125f3d8d 100644 --- a/WordPress/src/jetpack/res/values/styles_login.xml +++ b/WordPress/src/jetpack/res/values/styles_login.xml @@ -1,26 +1,3 @@ - - - - - - - - - - - diff --git a/WordPress/src/main/res/layout/login_loading.xml b/WordPress/src/main/res/layout/login_loading.xml index 3d430fe630e8..ba92b7fa533d 100644 --- a/WordPress/src/main/res/layout/login_loading.xml +++ b/WordPress/src/main/res/layout/login_loading.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> @dimen/prologue_promo_title_size - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -306,10 +276,4 @@ center_horizontal - - - - - From ad786389df26b52b3dd26368d15f91b3fa86de70 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:06:44 -0700 Subject: [PATCH 59/71] Add @Singleton to LoginAnalyticsTracker provider method The class had @Singleton but the Dagger provider didn't, so a new instance was created on every injection. Co-Authored-By: Claude Opus 4.6 --- .../org/wordpress/android/modules/LoginAnalyticsModule.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java index 160c2c36e849..622db175944f 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/LoginAnalyticsModule.java @@ -6,6 +6,8 @@ import org.wordpress.android.ui.accounts.UnifiedLoginTracker; import org.wordpress.android.ui.accounts.login.LoginAnalyticsTracker; +import javax.inject.Singleton; + import dagger.Module; import dagger.Provides; import dagger.hilt.InstallIn; @@ -14,6 +16,7 @@ @InstallIn(SingletonComponent.class) @Module public class LoginAnalyticsModule { + @Singleton @Provides public LoginAnalyticsListener provideAnalyticsListener(AccountStore accountStore, SiteStore siteStore, UnifiedLoginTracker unifiedLoginTracker) { From 983d93533f907d67e0cffc58267303bae8c887db Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:06:50 -0700 Subject: [PATCH 60/71] Call startPostLoginServices and remove dead stub methods startPostLoginServices was defined but never called, so Reader tags and notification services weren't started after login. Also removes unused startOver and gotConnectedSiteInfo methods. Co-Authored-By: Claude Opus 4.6 --- .../android/ui/accounts/LoginActivity.java | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java index 1da70ef545e3..fb6064f1a4b6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginActivity.java @@ -302,6 +302,8 @@ private void finishLoginAfterSitesLoaded() { * This is the common exit point for successful logins. */ private void navigateToMainActivityOrFinish() { + startPostLoginServices(); + LoginFlow.CompletionBehavior behavior = getLoginFlow().getCompletionBehavior(); if (behavior == LoginFlow.CompletionBehavior.MAIN_ACTIVITY) { // Select the primary site after WP.com login @@ -512,17 +514,6 @@ public void onPositiveClicked(@NonNull String instanceTag) { return mDispatchingAndroidInjector; } - public void startOver() { - // Not used in WordPress app - } - - public void gotConnectedSiteInfo( - @NonNull String siteAddress, - @Nullable String redirectUrl, - boolean hasJetpack) { - // Not used in WordPress app - } - public void handleSiteAddressError(ConnectSiteInfoPayload siteInfo) { mViewModel.onHandleSiteAddressError(siteInfo); } From e2d6923a3a0e291f65370e7246cca8af37e3e8ac Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:06:54 -0700 Subject: [PATCH 61/71] Remove leftover debug Log.e in WPMainActivity Co-Authored-By: Claude Opus 4.6 --- .../main/java/org/wordpress/android/ui/main/WPMainActivity.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java index 91550fe49388..12ffcf18cbbd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/main/WPMainActivity.java @@ -10,7 +10,6 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.widget.TextView; @@ -1261,7 +1260,6 @@ private void setSite(Intent data) { @Override @SuppressWarnings("deprecation") public void onActivityResult(int requestCode, int resultCode, Intent data) { - Log.e("WPMainActivity", "onActivityResult: " + requestCode + " " + resultCode); super.onActivityResult(requestCode, resultCode, data); if (!mSelectedSiteRepository.hasSelectedSite()) { initSelectedSite(); From bc91e57978eeb059461f52963ee07d47bd39db25 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:06:59 -0700 Subject: [PATCH 62/71] Remove plaintext username from debug logs The hasApiRestUsername boolean check is sufficient for debugging without exposing the actual credential value. Co-Authored-By: Claude Opus 4.6 --- .../applicationpassword/ApplicationPasswordViewModelSlice.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt index 6ca0b464b6be..a72bdec7ebad 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/mysite/cards/applicationpassword/ApplicationPasswordViewModelSlice.kt @@ -69,7 +69,6 @@ class ApplicationPasswordViewModelSlice @Inject constructor( appLogWrapper.d(AppLog.T.MAIN, "A_P: isUsingSelfHostedRestApi: ${site.isUsingSelfHostedRestApi}") appLogWrapper.d(AppLog.T.MAIN, "A_P: hasApiRestUsername: ${!site.apiRestUsernamePlain.isNullOrEmpty()}") appLogWrapper.d(AppLog.T.MAIN, "A_P: hasApiRestPassword: ${!site.apiRestPasswordPlain.isNullOrEmpty()}") - appLogWrapper.d(AppLog.T.MAIN, "A_P: apiRestUsername: ${site.apiRestUsernamePlain}") appLogWrapper.d(AppLog.T.MAIN, "A_P: wpApiRestUrl: ${site.wpApiRestUrl}") appLogWrapper.d(AppLog.T.MAIN, "A_P: origin: ${site.origin}") From 257a9f5e469910f4be64f2d47279581f3b672624 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:07:03 -0700 Subject: [PATCH 63/71] Replace deprecated onActivityCreated with onViewCreated Move activity title setup into onViewCreated, removing the suppressed deprecation warning. Co-Authored-By: Claude Opus 4.6 --- .../LoginSiteApplicationPasswordFragment.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt index 794b6cf553a1..f30c65e4f90f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt @@ -84,6 +84,7 @@ class LoginSiteApplicationPasswordFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + activity?.setTitle(R.string.site_address_login_title) analyticsListener.trackUrlFormViewed() viewLifecycleOwner.lifecycleScope.launch { @@ -114,12 +115,6 @@ class LoginSiteApplicationPasswordFragment : Fragment() { analyticsListener.siteAddressFormScreenResumed() } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - activity?.setTitle(R.string.site_address_login_title) - } - private fun discover(cleanedUrl: String) { if (!NetworkUtils.checkConnection(activity)) { return From a9c4847aee74ae592815a134221ad53fe8756f75 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:07:07 -0700 Subject: [PATCH 64/71] Align ARG_LOGIN_FLOW constant name with its string value Co-Authored-By: Claude Opus 4.6 --- .../main/java/org/wordpress/android/ui/accounts/LoginFlow.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt index 0eda273698ad..b71d435f24da 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/LoginFlow.kt @@ -95,7 +95,7 @@ enum class LoginFlow( } companion object { - private const val ARG_LOGIN_FLOW = "ARG_LOGIN_MODE" // Keep same key for compatibility + private const val ARG_LOGIN_FLOW = "ARG_LOGIN_FLOW" @JvmStatic fun fromIntent(intent: Intent): LoginFlow { From 55273b0dec362433689380c94c845a7a1a95e03f Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:07:11 -0700 Subject: [PATCH 65/71] Unbind CustomTabsServiceConnection on dispose The service was bound but never unbound, causing a minor resource leak. Now unbinds when the helper is disposed in onDestroy. Co-Authored-By: Claude Opus 4.6 --- .../android/ui/accounts/login/WPcomLoginHelper.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt index e0b17cdb72ac..4a4fcb5a7c84 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt @@ -72,6 +72,7 @@ class WPcomLoginHelper @Inject constructor( fun dispose() { scope.cancel() + customTabsServiceConnection.unbind() } fun bindCustomTabsService(context: Context) { @@ -82,6 +83,7 @@ class WPcomLoginHelper @Inject constructor( class ServiceConnection( var uri: Uri ) : CustomTabsServiceConnection() { + private var boundContext: Context? = null private var client: CustomTabsClient? = null private var session: CustomTabsSession? = null @@ -124,5 +126,13 @@ class ServiceConnection( packageName, this ) + boundContext = context + } + + fun unbind() { + boundContext?.unbindService(this) + boundContext = null + client = null + session = null } } From 73b73bfbc4f904bb8b056bf0c35c9abf863687d8 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:15:53 -0700 Subject: [PATCH 66/71] Use Channel instead of SharedFlow for discovery URL event Channel guarantees the event is buffered and consumed exactly once, even if the collector starts slightly after emission. Safer than SharedFlow(replay=0) for one-shot navigation events. Co-Authored-By: Claude Opus 4.6 --- .../LoginSiteApplicationPasswordViewModel.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordViewModel.kt index e9c76d6acd5c..db3d4cad554b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordViewModel.kt @@ -3,10 +3,10 @@ package org.wordpress.android.ui.accounts.login.applicationpassword import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.wordpress.android.ui.accounts.login.ApplicationPasswordLoginHelper import javax.inject.Inject @@ -15,9 +15,8 @@ import javax.inject.Inject class LoginSiteApplicationPasswordViewModel @Inject constructor( private val applicationPasswordLoginHelper: ApplicationPasswordLoginHelper, ) : ViewModel() { - // replay = 0: New subscribers will not receive previously emitted values - private val _discoveryURL = MutableSharedFlow(replay = 0) - val discoveryURL = _discoveryURL.asSharedFlow() + private val _discoveryURL = Channel(Channel.BUFFERED) + val discoveryURL = _discoveryURL.receiveAsFlow() private val _loadingStateFlow = MutableStateFlow(false) val loadingStateFlow get() = _loadingStateFlow.asStateFlow() @@ -30,7 +29,7 @@ class LoginSiteApplicationPasswordViewModel @Inject constructor( _loadingStateFlow.value = true viewModelScope.launch { val discoveryUrl = applicationPasswordLoginHelper.getAuthorizationUrlComplete(siteUrl) - _discoveryURL.emit(discoveryUrl) + _discoveryURL.send(discoveryUrl) _loadingStateFlow.value = false } } From 10af360bf1f26d2efc3249903ec0e7c706c1fc40 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:22:16 -0700 Subject: [PATCH 67/71] Suppress overdraw lint warning on login loading layout The window background from LoginTheme triggers a false positive overdraw warning since the ConstraintLayout itself has no explicit background. Co-Authored-By: Claude Opus 4.6 --- WordPress/src/main/res/layout/login_loading.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/res/layout/login_loading.xml b/WordPress/src/main/res/layout/login_loading.xml index 43d1aab7b7a7..b0d703224a53 100644 --- a/WordPress/src/main/res/layout/login_loading.xml +++ b/WordPress/src/main/res/layout/login_loading.xml @@ -1,8 +1,10 @@ + android:layout_height="match_parent" + tools:ignore="Overdraw"> Date: Wed, 25 Feb 2026 19:34:06 -0700 Subject: [PATCH 68/71] Add tools:keep for flavor-specific login prologue colors Android Lint can't trace cross-flavor resource usage, so it flags these colors as unused. They're referenced by Compose code in the wordpress flavor. Co-Authored-By: Claude Opus 4.6 --- WordPress/src/wordpress/res/values-night/colors.xml | 13 +++++++++---- WordPress/src/wordpress/res/values/colors.xml | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/WordPress/src/wordpress/res/values-night/colors.xml b/WordPress/src/wordpress/res/values-night/colors.xml index 570ddf5c61ac..4fd974473b4d 100644 --- a/WordPress/src/wordpress/res/values-night/colors.xml +++ b/WordPress/src/wordpress/res/values-night/colors.xml @@ -1,6 +1,11 @@ - - - @color/gray_80 - @color/white + + + @color/gray_80 + @color/white diff --git a/WordPress/src/wordpress/res/values/colors.xml b/WordPress/src/wordpress/res/values/colors.xml index 8582cb37faee..27572b4602b1 100644 --- a/WordPress/src/wordpress/res/values/colors.xml +++ b/WordPress/src/wordpress/res/values/colors.xml @@ -1,6 +1,11 @@ - - - @color/gray_0 - @color/gray_80 + + + @color/gray_0 + @color/gray_80 From 41ac084eda2517fc469f6b6086605c5e38b92c85 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 19:52:26 -0700 Subject: [PATCH 69/71] Redirect WP.com site addresses to OAuth login flow When a user enters a WordPress.com site address in the site address field, redirect them to the WP.com OAuth flow instead of proceeding with application password authorization. This ensures WP.com account state is properly established so the Me tab shows the user's profile. Co-Authored-By: Claude Opus 4.6 --- .../LoginSiteApplicationPasswordFragment.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt index f30c65e4f90f..165c8d2c6bfa 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/applicationpassword/LoginSiteApplicationPasswordFragment.kt @@ -22,6 +22,8 @@ import org.wordpress.android.ui.accounts.LoginActivity import org.wordpress.android.ui.accounts.login.LoginAnalyticsListener import org.wordpress.android.ui.compose.theme.AppThemeM3 import org.wordpress.android.util.NetworkUtils +import org.wordpress.android.util.UrlUtils +import org.wordpress.android.util.WPUrlUtils import javax.inject.Inject @AndroidEntryPoint @@ -119,6 +121,12 @@ class LoginSiteApplicationPasswordFragment : Fragment() { if (!NetworkUtils.checkConnection(activity)) { return } + // WP.com sites should use the OAuth flow, not application passwords + val urlWithScheme = UrlUtils.addUrlSchemeIfNeeded(cleanedUrl, true) + if (WPUrlUtils.isWordPressCom(urlWithScheme)) { + loginActivity?.showWPcomLoginScreen(requireContext()) + return + } analyticsListener.trackSubmitClicked() analyticsListener.trackConnectedSiteInfoRequested(cleanedUrl) viewModel.runApiDiscovery(cleanedUrl) From 9ad40db0c1a8046150b85bf57f75a286ecc4d1f2 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:21:48 -0700 Subject: [PATCH 70/71] Remove old login lib references from e2e tests The LoginFlow e2e helper imported resources from the deleted login library and referenced views (login_username_row, login_password_row) that no longer exist. Remove the dead enterUsernameAndPassword and enterSiteAddress methods since login forms are now Compose-based and WP.com login uses OAuth via Custom Tabs. Co-Authored-By: Claude Opus 4.6 --- .../org/wordpress/android/e2e/LoginTests.kt | 11 ++--- .../wordpress/android/e2e/flows/LoginFlow.kt | 41 ------------------- 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt index b21707c91685..87592bca35c2 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/LoginTests.kt @@ -6,7 +6,6 @@ import org.junit.Test import org.wordpress.android.e2e.flows.LoginFlow import org.wordpress.android.support.BaseTest import org.wordpress.android.support.ComposeEspressoLink -import org.wordpress.android.support.E2ECredentials @HiltAndroidTest class LoginTests : BaseTest() { @@ -17,14 +16,10 @@ class LoginTests : BaseTest() { } @Test - fun e2eLoginWithSelfHostedAccount() { + fun e2eLoginWithSiteAddress() { + // Self-hosted login now uses application passwords via Custom Tabs, + // so this test only verifies navigation to the site address screen LoginFlow().chooseEnterYourSiteAddress(super.mComposeTestRule) - .enterSiteAddress(E2ECredentials.SELF_HOSTED_USER_SITE_ADDRESS) - .enterUsernameAndPassword( - E2ECredentials.SELF_HOSTED_USER_USERNAME, - E2ECredentials.SELF_HOSTED_USER_PASSWORD - ) - .confirmLogin() ComposeEspressoLink().unregister() } diff --git a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt index aeb324ba6029..5934d59f8995 100644 --- a/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt +++ b/WordPress/src/androidTest/java/org/wordpress/android/e2e/flows/LoginFlow.kt @@ -1,17 +1,13 @@ package org.wordpress.android.e2e.flows -import android.widget.EditText import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.test.espresso.Espresso import androidx.test.espresso.matcher.ViewMatchers -import org.hamcrest.CoreMatchers -import org.hamcrest.Matchers import org.wordpress.android.R import org.wordpress.android.e2e.pages.HelpScreen import org.wordpress.android.e2e.pages.LandingPage.tapContinueWithWpCom import org.wordpress.android.e2e.pages.LandingPage.tapEnterYourSiteAddress import org.wordpress.android.support.WPSupportUtils -import org.wordpress.android.login.R as LoginR class LoginFlow { fun chooseContinueWithWpCom(composeTestRule: ComposeTestRule?): LoginFlow { @@ -21,49 +17,12 @@ class LoginFlow { return this } - fun enterUsernameAndPassword(username: String, password: String): LoginFlow { - val usernameElement = Espresso.onView( - CoreMatchers.allOf( - ViewMatchers.isDescendantOfA(ViewMatchers.withId(LoginR.id.login_username_row)), - Matchers.instanceOf(EditText::class.java) - ) - ) - val passwordElement = Espresso.onView( - CoreMatchers.allOf( - ViewMatchers.isDescendantOfA(ViewMatchers.withId(LoginR.id.login_password_row)), - Matchers.instanceOf(EditText::class.java) - ) - ) - WPSupportUtils.populateTextField( - usernameElement, """ - $username - - """.trimIndent() - ) - WPSupportUtils.populateTextField( - passwordElement, """ - $password - - """.trimIndent() - ) - WPSupportUtils.clickOn(R.id.bottom_button) - return this - } - fun chooseEnterYourSiteAddress(composeTestRule: ComposeTestRule?): LoginFlow { // Login Prologue – We want to continue with a site address not a WordPress.com account tapEnterYourSiteAddress(composeTestRule!!) return this } - fun enterSiteAddress(siteAddress: String?): LoginFlow { - // Site Address Screen – Fill it in and click "Continue" - // See LoginSiteApplicationPasswordFragment - WPSupportUtils.populateTextField(R.id.input, siteAddress) - WPSupportUtils.clickOn(R.id.bottom_button) - return this - } - fun tapHelp(): HelpScreen { WPSupportUtils.clickOn(Espresso.onView(ViewMatchers.withId(R.id.help))) return HelpScreen() From 01b78c7dcacda6c0ca8fe674edeeed5127e11d41 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:21:55 -0700 Subject: [PATCH 71/71] Call onFailure when OAuth callback is missing code parameter Previously tryLoginWithDataString silently returned if the code query parameter was absent, leaving the caller on a loading screen indefinitely. Now reports the error via the onFailure callback. Co-Authored-By: Claude Opus 4.6 --- .../android/ui/accounts/login/WPcomLoginHelper.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt index 4a4fcb5a7c84..bceaeeef73af 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/accounts/login/WPcomLoginHelper.kt @@ -50,7 +50,13 @@ class WPcomLoginHelper @Inject constructor( onSuccess: Runnable, onFailure: java.util.function.Consumer ) { - val code = data.toUri().getQueryParameter("code") ?: return + val code = data.toUri().getQueryParameter("code") + if (code == null) { + onFailure.accept( + IllegalArgumentException("Missing OAuth code in callback") + ) + return + } scope.launch { try {