From 861d2b9bf657abf6eef5c96737ac64ebccdd8efb Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sat, 11 Nov 2023 07:54:08 -0600 Subject: [PATCH] Removed butterknife from login activity (#5380) * Removed butterknife view bindings * Migrated click listeners to view binding * Migrate onEditorAction to use ViewBinding * Finally, removed butterknife --- .../free/nrw/commons/auth/LoginActivity.java | 131 +++++++----------- .../commons/auth/LoginActivityUnitTests.kt | 35 +---- 2 files changed, 56 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java index f3ee63f0c..e911cef4b 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java @@ -14,10 +14,8 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; +import android.widget.TextView; import androidx.annotation.ColorRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -27,8 +25,7 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.core.app.NavUtils; import androidx.core.content.ContextCompat; -import com.google.android.material.textfield.TextInputLayout; - +import fr.free.nrw.commons.databinding.ActivityLoginBinding; import fr.free.nrw.commons.utils.ActivityUtils; import java.util.Locale; import org.wikipedia.AppAdapter; @@ -42,15 +39,9 @@ import org.wikipedia.login.LoginResult; import javax.inject.Inject; import javax.inject.Named; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.OnEditorAction; -import butterknife.OnFocusChange; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; -import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.kvstore.JsonKvStore; @@ -87,30 +78,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { @Inject SystemThemeUtils systemThemeUtils; - @BindView(R.id.login_button) - Button loginButton; - - @BindView(R.id.login_username) - EditText usernameEdit; - - @BindView(R.id.login_password) - EditText passwordEdit; - - @BindView(R.id.login_two_factor) - EditText twoFactorEdit; - - @BindView(R.id.error_message_container) - ViewGroup errorMessageContainer; - - @BindView(R.id.error_message) - TextView errorMessage; - - @BindView(R.id.login_credentials) - TextView loginCredentials; - - @BindView(R.id.two_factor_container) - TextInputLayout twoFactorContainer; - + private ActivityLoginBinding binding; ProgressDialog progressDialog; private AppCompatDelegate delegate; private LoginTextWatcher textWatcher = new LoginTextWatcher(); @@ -120,6 +88,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { final String saveErrorMessage ="errorMessage"; final String saveUsername="username"; final String savePassword="password"; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -133,18 +102,26 @@ public class LoginActivity extends AccountAuthenticatorActivity { getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); - setContentView(R.layout.activity_login); + binding = ActivityLoginBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); - ButterKnife.bind(this); + binding.loginUsername.addTextChangedListener(textWatcher); + binding.loginPassword.addTextChangedListener(textWatcher); + binding.loginTwoFactor.addTextChangedListener(textWatcher); - usernameEdit.addTextChangedListener(textWatcher); - passwordEdit.addTextChangedListener(textWatcher); - twoFactorEdit.addTextChangedListener(textWatcher); + binding.skipLogin.setOnClickListener(view -> skipLogin()); + binding.forgotPassword.setOnClickListener(view -> forgotPassword()); + binding.aboutPrivacyPolicy.setOnClickListener(view -> onPrivacyPolicyClicked()); + binding.signUpButton.setOnClickListener(view -> signUp()); + binding.loginButton.setOnClickListener(view -> performLogin()); + + binding.loginPassword.setOnEditorActionListener(this::onEditorAction); + binding.loginPassword.setOnFocusChangeListener(this::onPasswordFocusChanged); if (ConfigUtils.isBetaFlavour()) { - loginCredentials.setText(getString(R.string.login_credential)); + binding.loginCredentials.setText(getString(R.string.login_credential)); } else { - loginCredentials.setVisibility(View.GONE); + binding.loginCredentials.setVisibility(View.GONE); } } /** @@ -152,16 +129,14 @@ public class LoginActivity extends AccountAuthenticatorActivity { * @param view The keyboard * @param hasFocus Set to true if the keyboard has focus */ - @OnFocusChange(R.id.login_password) void onPasswordFocusChanged(View view, boolean hasFocus) { if (!hasFocus) { ViewUtil.hideKeyboard(view); } } - @OnEditorAction(R.id.login_password) - boolean onEditorAction(int actionId, KeyEvent keyEvent) { - if (loginButton.isEnabled()) { + boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) { + if (binding.loginButton.isEnabled()) { if (actionId == IME_ACTION_DONE) { performLogin(); return true; @@ -174,8 +149,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { } - @OnClick(R.id.skip_login) - void skipLogin() { + protected void skipLogin() { new AlertDialog.Builder(this).setTitle(R.string.skip_login_title) .setMessage(R.string.skip_login_message) .setCancelable(false) @@ -187,18 +161,15 @@ public class LoginActivity extends AccountAuthenticatorActivity { .show(); } - @OnClick(R.id.forgot_password) - void forgotPassword() { + protected void forgotPassword() { Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)); } - @OnClick(R.id.about_privacy_policy) - void onPrivacyPolicyClicked() { + protected void onPrivacyPolicyClicked() { Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)); } - @OnClick(R.id.sign_up_button) - void signUp() { + protected void signUp() { Intent intent = new Intent(this, SignupActivity.class); startActivity(intent); } @@ -236,23 +207,23 @@ public class LoginActivity extends AccountAuthenticatorActivity { } catch (Exception e) { e.printStackTrace(); } - usernameEdit.removeTextChangedListener(textWatcher); - passwordEdit.removeTextChangedListener(textWatcher); - twoFactorEdit.removeTextChangedListener(textWatcher); + binding.loginUsername.removeTextChangedListener(textWatcher); + binding.loginPassword.removeTextChangedListener(textWatcher); + binding.loginTwoFactor.removeTextChangedListener(textWatcher); delegate.onDestroy(); if(null!=loginClient) { loginClient.cancel(); } + binding = null; super.onDestroy(); } - @OnClick(R.id.login_button) public void performLogin() { Timber.d("Login to start!"); - final String username = usernameEdit.getText().toString(); - final String rawUsername = usernameEdit.getText().toString().trim(); - final String password = passwordEdit.getText().toString(); - String twoFactorCode = twoFactorEdit.getText().toString(); + final String username = binding.loginUsername.getText().toString(); + final String rawUsername = binding.loginUsername.getText().toString().trim(); + final String password = binding.loginPassword.getText().toString(); + String twoFactorCode = binding.loginTwoFactor.getText().toString(); showLoggingProgressBar(); doLogin(username, password, twoFactorCode); @@ -389,9 +360,9 @@ public class LoginActivity extends AccountAuthenticatorActivity { public void askUserForTwoFactorAuth() { progressDialog.dismiss(); - twoFactorContainer.setVisibility(VISIBLE); - twoFactorEdit.setVisibility(VISIBLE); - twoFactorEdit.requestFocus(); + binding.twoFactorContainer.setVisibility(VISIBLE); + binding.loginTwoFactor.setVisibility(VISIBLE); + binding.loginTwoFactor.requestFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); showMessageAndCancelDialog(R.string.login_failed_2fa_needed); @@ -422,15 +393,15 @@ public class LoginActivity extends AccountAuthenticatorActivity { } private void showMessage(@StringRes int resId, @ColorRes int colorResId) { - errorMessage.setText(getString(resId)); - errorMessage.setTextColor(ContextCompat.getColor(this, colorResId)); - errorMessageContainer.setVisibility(VISIBLE); + binding.errorMessage.setText(getString(resId)); + binding.errorMessage.setTextColor(ContextCompat.getColor(this, colorResId)); + binding.errorMessageContainer.setVisibility(VISIBLE); } private void showMessage(String message, @ColorRes int colorResId) { - errorMessage.setText(message); - errorMessage.setTextColor(ContextCompat.getColor(this, colorResId)); - errorMessageContainer.setVisibility(VISIBLE); + binding.errorMessage.setText(message); + binding.errorMessage.setTextColor(ContextCompat.getColor(this, colorResId)); + binding.errorMessageContainer.setVisibility(VISIBLE); } private AppCompatDelegate getDelegate() { @@ -451,9 +422,11 @@ public class LoginActivity extends AccountAuthenticatorActivity { @Override public void afterTextChanged(Editable editable) { - boolean enabled = usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0 - && (BuildConfig.DEBUG || twoFactorEdit.getText().length() != 0 || twoFactorEdit.getVisibility() != VISIBLE); - loginButton.setEnabled(enabled); + boolean enabled = binding.loginUsername.getText().length() != 0 && + binding.loginPassword.getText().length() != 0 && + (BuildConfig.DEBUG || binding.loginTwoFactor.getText().length() != 0 || + binding.loginTwoFactor.getVisibility() != VISIBLE); + binding.loginButton.setEnabled(enabled); } } @@ -471,22 +444,22 @@ public class LoginActivity extends AccountAuthenticatorActivity { } else { outState.putBoolean(saveProgressDailog,false); } - outState.putString(saveErrorMessage,errorMessage.getText().toString()); //Save the errorMessage + outState.putString(saveErrorMessage,binding.errorMessage.getText().toString()); //Save the errorMessage outState.putString(saveUsername,getUsername()); // Save the username outState.putString(savePassword,getPassword()); // Save the password } private String getUsername() { - return usernameEdit.getText().toString(); + return binding.loginUsername.getText().toString(); } private String getPassword(){ - return passwordEdit.getText().toString(); + return binding.loginPassword.getText().toString(); } @Override protected void onRestoreInstanceState(final Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - usernameEdit.setText(savedInstanceState.getString(saveUsername)); - passwordEdit.setText(savedInstanceState.getString(savePassword)); + binding.loginUsername.setText(savedInstanceState.getString(saveUsername)); + binding.loginPassword.setText(savedInstanceState.getString(savePassword)); if(savedInstanceState.getBoolean(saveProgressDailog)) { performLogin(); } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt index 24ca497a3..fb3e7ce7e 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/auth/LoginActivityUnitTests.kt @@ -10,6 +10,7 @@ import android.view.View import android.view.ViewGroup import android.view.inputmethod.EditorInfo import android.widget.Button +import android.widget.TextView import androidx.test.core.app.ApplicationProvider import fr.free.nrw.commons.R import fr.free.nrw.commons.TestAppAdapter @@ -55,7 +56,7 @@ class LoginActivityUnitTests { private lateinit var keyEvent: KeyEvent @Mock - private lateinit var loginButton: Button + private lateinit var textView: TextView @Mock private lateinit var bundle: Bundle @@ -95,40 +96,12 @@ class LoginActivityUnitTests { fun testOnEditorActionCaseDefault() { val method: Method = LoginActivity::class.java.getDeclaredMethod( "onEditorAction", + TextView::class.java, Int::class.java, KeyEvent::class.java ) method.isAccessible = true - method.invoke(activity, 0, keyEvent) - } - - @Test - @Throws(Exception::class) - fun testOnEditorActionCaseLoginEnabledFirstCase() { - Whitebox.setInternalState(activity, "loginButton", loginButton) - `when`(loginButton.isEnabled).thenReturn(true) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onEditorAction", - Int::class.java, - KeyEvent::class.java - ) - method.isAccessible = true - method.invoke(activity, EditorInfo.IME_ACTION_DONE, keyEvent) - } - - @Test - @Throws(Exception::class) - fun testOnEditorActionCaseLoginEnabledSecondCase() { - Whitebox.setInternalState(activity, "loginButton", loginButton) - `when`(loginButton.isEnabled).thenReturn(true) - `when`(keyEvent.keyCode).thenReturn(KeyEvent.KEYCODE_ENTER) - val method: Method = LoginActivity::class.java.getDeclaredMethod( - "onEditorAction", - Int::class.java, - KeyEvent::class.java - ) - method.isAccessible = true - method.invoke(activity, 0, keyEvent) + method.invoke(activity, textView, 0, keyEvent) } @Test