mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Convert auth package to kotlin (#5966)
* Convert SessionManager to kotlin along with other small fixes * Convert WikiAccountAuthenticator to kotlin * Migrate WikiAccountAuthenticatorService to kotlin * Converted AccountUtil to kotlin * Convert SignupActivity to kotlin * Convert LoginActivity to kotlin * Merge from main
This commit is contained in:
parent
238023056f
commit
0c969c365b
25 changed files with 752 additions and 964 deletions
|
|
@ -17,7 +17,7 @@ class PasteSensitiveTextInputEditTextTest {
|
|||
@Before
|
||||
fun setup() {
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
textView = PasteSensitiveTextInputEditText(context)
|
||||
textView = PasteSensitiveTextInputEditText(context!!)
|
||||
}
|
||||
|
||||
// this test has no real value, just % for test code coverage
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class AccountUtil {
|
||||
|
||||
public static final String AUTH_TOKEN_TYPE = "CommonsAndroid";
|
||||
|
||||
public AccountUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
@Nullable
|
||||
public static Account account(Context context) {
|
||||
try {
|
||||
Account[] accounts = accountManager(context).getAccountsByType(BuildConfig.ACCOUNT_TYPE);
|
||||
if (accounts.length > 0) {
|
||||
return accounts[0];
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getUserName(Context context) {
|
||||
Account account = account(context);
|
||||
return account == null ? null : account.name;
|
||||
}
|
||||
|
||||
private static AccountManager accountManager(Context context) {
|
||||
return AccountManager.get(context);
|
||||
}
|
||||
}
|
||||
24
app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.kt
Normal file
24
app/src/main/java/fr/free/nrw/commons/auth/AccountUtil.kt
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import fr.free.nrw.commons.BuildConfig.ACCOUNT_TYPE
|
||||
import timber.log.Timber
|
||||
|
||||
const val AUTH_TOKEN_TYPE: String = "CommonsAndroid"
|
||||
|
||||
fun getUserName(context: Context): String? {
|
||||
return account(context)?.name
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun account(context: Context): Account? = try {
|
||||
val accountManager = AccountManager.get(context)
|
||||
val accounts = accountManager.getAccountsByType(ACCOUNT_TYPE)
|
||||
if (accounts.isNotEmpty()) accounts[0] else null
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e(e)
|
||||
null
|
||||
}
|
||||
|
|
@ -1,456 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.AccountAuthenticatorActivity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.app.NavUtils;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import fr.free.nrw.commons.auth.login.LoginClient;
|
||||
import fr.free.nrw.commons.auth.login.LoginResult;
|
||||
import fr.free.nrw.commons.databinding.ActivityLoginBinding;
|
||||
import fr.free.nrw.commons.utils.ActivityUtils;
|
||||
import java.util.Locale;
|
||||
import fr.free.nrw.commons.auth.login.LoginCallback;
|
||||
|
||||
import java.util.Objects;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.view.KeyEvent.KEYCODE_ENTER;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
import static fr.free.nrw.commons.CommonsApplication.LOGIN_MESSAGE_INTENT_KEY;
|
||||
import static fr.free.nrw.commons.CommonsApplication.LOGIN_USERNAME_INTENT_KEY;
|
||||
|
||||
public class LoginActivity extends AccountAuthenticatorActivity {
|
||||
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
JsonKvStore applicationKvStore;
|
||||
|
||||
@Inject
|
||||
LoginClient loginClient;
|
||||
|
||||
@Inject
|
||||
SystemThemeUtils systemThemeUtils;
|
||||
|
||||
private ActivityLoginBinding binding;
|
||||
ProgressDialog progressDialog;
|
||||
private AppCompatDelegate delegate;
|
||||
private LoginTextWatcher textWatcher = new LoginTextWatcher();
|
||||
private CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
final String saveProgressDailog="ProgressDailog_state";
|
||||
final String saveErrorMessage ="errorMessage";
|
||||
final String saveUsername="username";
|
||||
final String savePassword="password";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ApplicationlessInjection
|
||||
.getInstance(this.getApplicationContext())
|
||||
.getCommonsApplicationComponent()
|
||||
.inject(this);
|
||||
|
||||
boolean isDarkTheme = systemThemeUtils.isDeviceInNightMode();
|
||||
setTheme(isDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme);
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityLoginBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
String message = getIntent().getStringExtra(LOGIN_MESSAGE_INTENT_KEY);
|
||||
String username = getIntent().getStringExtra(LOGIN_USERNAME_INTENT_KEY);
|
||||
|
||||
binding.loginUsername.addTextChangedListener(textWatcher);
|
||||
binding.loginPassword.addTextChangedListener(textWatcher);
|
||||
binding.loginTwoFactor.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()) {
|
||||
binding.loginCredentials.setText(getString(R.string.login_credential));
|
||||
} else {
|
||||
binding.loginCredentials.setVisibility(View.GONE);
|
||||
}
|
||||
if (message != null) {
|
||||
showMessage(message, R.color.secondaryDarkColor);
|
||||
}
|
||||
if (username != null) {
|
||||
binding.loginUsername.setText(username);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Hides the keyboard if the user's focus is not on the password (hasFocus is false).
|
||||
* @param view The keyboard
|
||||
* @param hasFocus Set to true if the keyboard has focus
|
||||
*/
|
||||
void onPasswordFocusChanged(View view, boolean hasFocus) {
|
||||
if (!hasFocus) {
|
||||
ViewUtil.hideKeyboard(view);
|
||||
}
|
||||
}
|
||||
|
||||
boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
|
||||
if (binding.loginButton.isEnabled()) {
|
||||
if (actionId == IME_ACTION_DONE) {
|
||||
performLogin();
|
||||
return true;
|
||||
} else if ((keyEvent != null) && keyEvent.getKeyCode() == KEYCODE_ENTER) {
|
||||
performLogin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected void skipLogin() {
|
||||
new AlertDialog.Builder(this).setTitle(R.string.skip_login_title)
|
||||
.setMessage(R.string.skip_login_message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
dialog.cancel();
|
||||
performSkipLogin();
|
||||
})
|
||||
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
|
||||
.show();
|
||||
}
|
||||
|
||||
protected void forgotPassword() {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL));
|
||||
}
|
||||
|
||||
protected void onPrivacyPolicyClicked() {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL));
|
||||
}
|
||||
|
||||
protected void signUp() {
|
||||
Intent intent = new Intent(this, SignupActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
getDelegate().onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (sessionManager.getCurrentAccount() != null
|
||||
&& sessionManager.isUserLoggedIn()) {
|
||||
applicationKvStore.putBoolean("login_skipped", false);
|
||||
startMainActivity();
|
||||
}
|
||||
|
||||
if (applicationKvStore.getBoolean("login_skipped", false)) {
|
||||
performSkipLogin();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
compositeDisposable.clear();
|
||||
try {
|
||||
// To prevent leaked window when finish() is called, see http://stackoverflow.com/questions/32065854/activity-has-leaked-window-at-alertdialog-show-method
|
||||
if (progressDialog != null && progressDialog.isShowing()) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
binding.loginUsername.removeTextChangedListener(textWatcher);
|
||||
binding.loginPassword.removeTextChangedListener(textWatcher);
|
||||
binding.loginTwoFactor.removeTextChangedListener(textWatcher);
|
||||
delegate.onDestroy();
|
||||
if(null!=loginClient) {
|
||||
loginClient.cancel();
|
||||
}
|
||||
binding = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void performLogin() {
|
||||
Timber.d("Login to start!");
|
||||
final String username = Objects.requireNonNull(binding.loginUsername.getText()).toString();
|
||||
final String password = Objects.requireNonNull(binding.loginPassword.getText()).toString();
|
||||
final String twoFactorCode = Objects.requireNonNull(binding.loginTwoFactor.getText()).toString();
|
||||
|
||||
showLoggingProgressBar();
|
||||
loginClient.doLogin(username, password, twoFactorCode, Locale.getDefault().getLanguage(),
|
||||
new LoginCallback() {
|
||||
@Override
|
||||
public void success(@NonNull LoginResult loginResult) {
|
||||
runOnUiThread(()->{
|
||||
Timber.d("Login Success");
|
||||
hideProgress();
|
||||
onLoginSuccess(loginResult);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void twoFactorPrompt(@NonNull Throwable caught, @Nullable String token) {
|
||||
runOnUiThread(()->{
|
||||
Timber.d("Requesting 2FA prompt");
|
||||
hideProgress();
|
||||
askUserForTwoFactorAuth();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passwordResetPrompt(@Nullable String token) {
|
||||
runOnUiThread(()->{
|
||||
Timber.d("Showing password reset prompt");
|
||||
hideProgress();
|
||||
showPasswordResetPrompt();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(@NonNull Throwable caught) {
|
||||
runOnUiThread(()->{
|
||||
Timber.e(caught);
|
||||
hideProgress();
|
||||
showMessageAndCancelDialog(caught.getLocalizedMessage());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void hideProgress() {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
private void showPasswordResetPrompt() {
|
||||
showMessageAndCancelDialog(getString(R.string.you_must_reset_your_passsword));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function is called when user skips the login.
|
||||
* It redirects the user to Explore Activity.
|
||||
*/
|
||||
private void performSkipLogin() {
|
||||
applicationKvStore.putBoolean("login_skipped", true);
|
||||
MainActivity.startYourself(this);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void showLoggingProgressBar() {
|
||||
progressDialog = new ProgressDialog(this);
|
||||
progressDialog.setIndeterminate(true);
|
||||
progressDialog.setTitle(getString(R.string.logging_in_title));
|
||||
progressDialog.setMessage(getString(R.string.logging_in_message));
|
||||
progressDialog.setCanceledOnTouchOutside(false);
|
||||
progressDialog.show();
|
||||
}
|
||||
|
||||
private void onLoginSuccess(LoginResult loginResult) {
|
||||
compositeDisposable.clear();
|
||||
sessionManager.setUserLoggedIn(true);
|
||||
sessionManager.updateAccount(loginResult);
|
||||
progressDialog.dismiss();
|
||||
showSuccessAndDismissDialog();
|
||||
startMainActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
delegate.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
delegate.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
getDelegate().onPostResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
NavUtils.navigateUpFromSameTask(this);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MenuInflater getMenuInflater() {
|
||||
return getDelegate().getMenuInflater();
|
||||
}
|
||||
|
||||
public void askUserForTwoFactorAuth() {
|
||||
progressDialog.dismiss();
|
||||
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);
|
||||
}
|
||||
|
||||
public void showMessageAndCancelDialog(@StringRes int resId) {
|
||||
showMessage(resId, R.color.secondaryDarkColor);
|
||||
if (progressDialog != null) {
|
||||
progressDialog.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void showMessageAndCancelDialog(String error) {
|
||||
showMessage(error, R.color.secondaryDarkColor);
|
||||
if (progressDialog != null) {
|
||||
progressDialog.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public void showSuccessAndDismissDialog() {
|
||||
showMessage(R.string.login_success, R.color.primaryDarkColor);
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
public void startMainActivity() {
|
||||
ActivityUtils.startActivityWithFlags(this, MainActivity.class, Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void showMessage(@StringRes int resId, @ColorRes int colorResId) {
|
||||
binding.errorMessage.setText(getString(resId));
|
||||
binding.errorMessage.setTextColor(ContextCompat.getColor(this, colorResId));
|
||||
binding.errorMessageContainer.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
private void showMessage(String message, @ColorRes int colorResId) {
|
||||
binding.errorMessage.setText(message);
|
||||
binding.errorMessage.setTextColor(ContextCompat.getColor(this, colorResId));
|
||||
binding.errorMessageContainer.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
private AppCompatDelegate getDelegate() {
|
||||
if (delegate == null) {
|
||||
delegate = AppCompatDelegate.create(this, null);
|
||||
}
|
||||
return delegate;
|
||||
}
|
||||
|
||||
private class LoginTextWatcher implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static void startYourself(Context context) {
|
||||
Intent intent = new Intent(context, LoginActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
// if progressDialog is visible during the configuration change then store state as true else false so that
|
||||
// we maintain visibility of progressDailog after configuration change
|
||||
if(progressDialog!=null&&progressDialog.isShowing()) {
|
||||
outState.putBoolean(saveProgressDailog,true);
|
||||
} else {
|
||||
outState.putBoolean(saveProgressDailog,false);
|
||||
}
|
||||
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 binding.loginUsername.getText().toString();
|
||||
}
|
||||
private String getPassword(){
|
||||
return binding.loginPassword.getText().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
binding.loginUsername.setText(savedInstanceState.getString(saveUsername));
|
||||
binding.loginPassword.setText(savedInstanceState.getString(savePassword));
|
||||
if(savedInstanceState.getBoolean(saveProgressDailog)) {
|
||||
performLogin();
|
||||
}
|
||||
String errorMessage=savedInstanceState.getString(saveErrorMessage);
|
||||
if(sessionManager.isUserLoggedIn()) {
|
||||
showMessage(R.string.login_success, R.color.primaryDarkColor);
|
||||
} else {
|
||||
showMessage(errorMessage, R.color.secondaryDarkColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
404
app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt
Normal file
404
app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.accounts.AccountAuthenticatorActivity
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.app.NavUtils
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.login.LoginCallback
|
||||
import fr.free.nrw.commons.auth.login.LoginClient
|
||||
import fr.free.nrw.commons.auth.login.LoginResult
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.ActivityLoginBinding
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.utils.AbstractTextWatcher
|
||||
import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class LoginActivity : AccountAuthenticatorActivity() {
|
||||
@Inject
|
||||
lateinit var sessionManager: SessionManager
|
||||
|
||||
@Inject
|
||||
@field:Named("default_preferences")
|
||||
lateinit var applicationKvStore: JsonKvStore
|
||||
|
||||
@Inject
|
||||
lateinit var loginClient: LoginClient
|
||||
|
||||
@Inject
|
||||
lateinit var systemThemeUtils: SystemThemeUtils
|
||||
|
||||
private var binding: ActivityLoginBinding? = null
|
||||
private var progressDialog: ProgressDialog? = null
|
||||
private val textWatcher = AbstractTextWatcher(::onTextChanged)
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val delegate: AppCompatDelegate by lazy {
|
||||
AppCompatDelegate.create(this, null)
|
||||
}
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ApplicationlessInjection
|
||||
.getInstance(this.applicationContext)
|
||||
.commonsApplicationComponent
|
||||
.inject(this)
|
||||
|
||||
val isDarkTheme = systemThemeUtils.isDeviceInNightMode()
|
||||
setTheme(if (isDarkTheme) R.style.DarkAppTheme else R.style.LightAppTheme)
|
||||
delegate.installViewFactory()
|
||||
delegate.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
with(binding!!) {
|
||||
setContentView(root)
|
||||
|
||||
loginUsername.addTextChangedListener(textWatcher)
|
||||
loginPassword.addTextChangedListener(textWatcher)
|
||||
loginTwoFactor.addTextChangedListener(textWatcher)
|
||||
|
||||
skipLogin.setOnClickListener { skipLogin() }
|
||||
forgotPassword.setOnClickListener { forgotPassword() }
|
||||
aboutPrivacyPolicy.setOnClickListener { onPrivacyPolicyClicked() }
|
||||
signUpButton.setOnClickListener { signUp() }
|
||||
loginButton.setOnClickListener { performLogin() }
|
||||
loginPassword.setOnEditorActionListener(::onEditorAction)
|
||||
|
||||
loginPassword.onFocusChangeListener =
|
||||
View.OnFocusChangeListener(::onPasswordFocusChanged)
|
||||
|
||||
if (isBetaFlavour) {
|
||||
loginCredentials.text = getString(R.string.login_credential)
|
||||
} else {
|
||||
loginCredentials.visibility = View.GONE
|
||||
}
|
||||
|
||||
intent.getStringExtra(CommonsApplication.LOGIN_MESSAGE_INTENT_KEY)?.let {
|
||||
showMessage(it, R.color.secondaryDarkColor)
|
||||
}
|
||||
|
||||
intent.getStringExtra(CommonsApplication.LOGIN_USERNAME_INTENT_KEY)?.let {
|
||||
loginUsername.setText(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
delegate.onPostCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (sessionManager.currentAccount != null && sessionManager.isUserLoggedIn) {
|
||||
applicationKvStore.putBoolean("login_skipped", false)
|
||||
startMainActivity()
|
||||
}
|
||||
|
||||
if (applicationKvStore.getBoolean("login_skipped", false)) {
|
||||
performSkipLogin()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
compositeDisposable.clear()
|
||||
try {
|
||||
// To prevent leaked window when finish() is called, see http://stackoverflow.com/questions/32065854/activity-has-leaked-window-at-alertdialog-show-method
|
||||
if (progressDialog?.isShowing == true) {
|
||||
progressDialog!!.dismiss()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
with(binding!!) {
|
||||
loginUsername.removeTextChangedListener(textWatcher)
|
||||
loginPassword.removeTextChangedListener(textWatcher)
|
||||
loginTwoFactor.removeTextChangedListener(textWatcher)
|
||||
}
|
||||
delegate.onDestroy()
|
||||
loginClient?.cancel()
|
||||
binding = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
delegate.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
delegate.onStop()
|
||||
}
|
||||
|
||||
override fun onPostResume() {
|
||||
super.onPostResume()
|
||||
delegate.onPostResume()
|
||||
}
|
||||
|
||||
override fun setContentView(view: View, params: ViewGroup.LayoutParams) {
|
||||
delegate.setContentView(view, params)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
NavUtils.navigateUpFromSameTask(this)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
// if progressDialog is visible during the configuration change then store state as true else false so that
|
||||
// we maintain visibility of progressDailog after configuration change
|
||||
if (progressDialog != null && progressDialog!!.isShowing) {
|
||||
outState.putBoolean(saveProgressDailog, true)
|
||||
} else {
|
||||
outState.putBoolean(saveProgressDailog, false)
|
||||
}
|
||||
outState.putString(
|
||||
saveErrorMessage,
|
||||
binding!!.errorMessage.text.toString()
|
||||
) //Save the errorMessage
|
||||
outState.putString(
|
||||
saveUsername,
|
||||
binding!!.loginUsername.text.toString()
|
||||
) // Save the username
|
||||
outState.putString(
|
||||
savePassword,
|
||||
binding!!.loginPassword.text.toString()
|
||||
) // Save the password
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
binding!!.loginUsername.setText(savedInstanceState.getString(saveUsername))
|
||||
binding!!.loginPassword.setText(savedInstanceState.getString(savePassword))
|
||||
if (savedInstanceState.getBoolean(saveProgressDailog)) {
|
||||
performLogin()
|
||||
}
|
||||
val errorMessage = savedInstanceState.getString(saveErrorMessage)
|
||||
if (sessionManager.isUserLoggedIn) {
|
||||
showMessage(R.string.login_success, R.color.primaryDarkColor)
|
||||
} else {
|
||||
showMessage(errorMessage, R.color.secondaryDarkColor)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the keyboard if the user's focus is not on the password (hasFocus is false).
|
||||
* @param view The keyboard
|
||||
* @param hasFocus Set to true if the keyboard has focus
|
||||
*/
|
||||
private fun onPasswordFocusChanged(view: View, hasFocus: Boolean) {
|
||||
if (!hasFocus) {
|
||||
hideKeyboard(view)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onEditorAction(textView: TextView, actionId: Int, keyEvent: KeyEvent?) =
|
||||
if (binding!!.loginButton.isEnabled && isTriggerAction(actionId, keyEvent)) {
|
||||
performLogin()
|
||||
true
|
||||
} else false
|
||||
|
||||
private fun isTriggerAction(actionId: Int, keyEvent: KeyEvent?) =
|
||||
actionId == EditorInfo.IME_ACTION_DONE || keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER
|
||||
|
||||
private fun skipLogin() {
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.skip_login_title)
|
||||
.setMessage(R.string.skip_login_message)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.yes) { dialog: DialogInterface, which: Int ->
|
||||
dialog.cancel()
|
||||
performSkipLogin()
|
||||
}
|
||||
.setNegativeButton(R.string.no) { dialog: DialogInterface, which: Int ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun forgotPassword() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
|
||||
|
||||
private fun onPrivacyPolicyClicked() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
|
||||
private fun signUp() =
|
||||
startActivity(Intent(this, SignupActivity::class.java))
|
||||
|
||||
@VisibleForTesting
|
||||
fun performLogin() {
|
||||
Timber.d("Login to start!")
|
||||
val username = binding!!.loginUsername.text.toString()
|
||||
val password = binding!!.loginPassword.text.toString()
|
||||
val twoFactorCode = binding!!.loginTwoFactor.text.toString()
|
||||
|
||||
showLoggingProgressBar()
|
||||
loginClient.doLogin(username,
|
||||
password,
|
||||
twoFactorCode,
|
||||
Locale.getDefault().language,
|
||||
object : LoginCallback {
|
||||
override fun success(loginResult: LoginResult) = runOnUiThread {
|
||||
Timber.d("Login Success")
|
||||
progressDialog!!.dismiss()
|
||||
onLoginSuccess(loginResult)
|
||||
}
|
||||
|
||||
override fun twoFactorPrompt(caught: Throwable, token: String?) = runOnUiThread {
|
||||
Timber.d("Requesting 2FA prompt")
|
||||
progressDialog!!.dismiss()
|
||||
askUserForTwoFactorAuth()
|
||||
}
|
||||
|
||||
override fun passwordResetPrompt(token: String?) = runOnUiThread {
|
||||
Timber.d("Showing password reset prompt")
|
||||
progressDialog!!.dismiss()
|
||||
showPasswordResetPrompt()
|
||||
}
|
||||
|
||||
override fun error(caught: Throwable) = runOnUiThread {
|
||||
Timber.e(caught)
|
||||
progressDialog!!.dismiss()
|
||||
showMessageAndCancelDialog(caught.localizedMessage ?: "")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showPasswordResetPrompt() =
|
||||
showMessageAndCancelDialog(getString(R.string.you_must_reset_your_passsword))
|
||||
|
||||
/**
|
||||
* This function is called when user skips the login.
|
||||
* It redirects the user to Explore Activity.
|
||||
*/
|
||||
private fun performSkipLogin() {
|
||||
applicationKvStore.putBoolean("login_skipped", true)
|
||||
MainActivity.startYourself(this)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun showLoggingProgressBar() {
|
||||
progressDialog = ProgressDialog(this).apply {
|
||||
isIndeterminate = true
|
||||
setTitle(getString(R.string.logging_in_title))
|
||||
setMessage(getString(R.string.logging_in_message))
|
||||
setCanceledOnTouchOutside(false)
|
||||
}
|
||||
progressDialog!!.show()
|
||||
}
|
||||
|
||||
private fun onLoginSuccess(loginResult: LoginResult) {
|
||||
compositeDisposable.clear()
|
||||
sessionManager.setUserLoggedIn(true)
|
||||
sessionManager.updateAccount(loginResult)
|
||||
progressDialog!!.dismiss()
|
||||
showSuccessAndDismissDialog()
|
||||
startMainActivity()
|
||||
}
|
||||
|
||||
override fun getMenuInflater(): MenuInflater =
|
||||
delegate.menuInflater
|
||||
|
||||
@VisibleForTesting
|
||||
fun askUserForTwoFactorAuth() {
|
||||
progressDialog!!.dismiss()
|
||||
with(binding!!) {
|
||||
twoFactorContainer.visibility = View.VISIBLE
|
||||
loginTwoFactor.visibility = View.VISIBLE
|
||||
loginTwoFactor.requestFocus()
|
||||
}
|
||||
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY)
|
||||
showMessageAndCancelDialog(R.string.login_failed_2fa_needed)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun showMessageAndCancelDialog(@StringRes resId: Int) {
|
||||
showMessage(resId, R.color.secondaryDarkColor)
|
||||
progressDialog?.cancel()
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun showMessageAndCancelDialog(error: String) {
|
||||
showMessage(error, R.color.secondaryDarkColor)
|
||||
progressDialog?.cancel()
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun showSuccessAndDismissDialog() {
|
||||
showMessage(R.string.login_success, R.color.primaryDarkColor)
|
||||
progressDialog!!.dismiss()
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
fun startMainActivity() {
|
||||
startActivityWithFlags(this, MainActivity::class.java, Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun showMessage(@StringRes resId: Int, @ColorRes colorResId: Int) = with(binding!!) {
|
||||
errorMessage.text = getString(resId)
|
||||
errorMessage.setTextColor(ContextCompat.getColor(this@LoginActivity, colorResId))
|
||||
errorMessageContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun showMessage(message: String?, @ColorRes colorResId: Int) = with(binding!!) {
|
||||
errorMessage.text = message
|
||||
errorMessage.setTextColor(ContextCompat.getColor(this@LoginActivity, colorResId))
|
||||
errorMessageContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun onTextChanged(text: String) {
|
||||
val enabled =
|
||||
binding!!.loginUsername.text!!.length != 0 && binding!!.loginPassword.text!!.length != 0 &&
|
||||
(BuildConfig.DEBUG || binding!!.loginTwoFactor.text!!.length != 0 || binding!!.loginTwoFactor.visibility != View.VISIBLE)
|
||||
binding!!.loginButton.isEnabled = enabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun startYourself(context: Context) =
|
||||
context.startActivity(Intent(context, LoginActivity::class.java))
|
||||
|
||||
const val saveProgressDailog: String = "ProgressDailog_state"
|
||||
const val saveErrorMessage: String = "errorMessage"
|
||||
const val saveUsername: String = "username"
|
||||
const val savePassword: String = "password"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.auth.login.LoginResult;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
|
||||
/**
|
||||
* Manage the current logged in user session.
|
||||
*/
|
||||
@Singleton
|
||||
public class SessionManager {
|
||||
private final Context context;
|
||||
private Account currentAccount; // Unlike a savings account... ;-)
|
||||
private JsonKvStore defaultKvStore;
|
||||
|
||||
@Inject
|
||||
public SessionManager(Context context,
|
||||
@Named("default_preferences") JsonKvStore defaultKvStore) {
|
||||
this.context = context;
|
||||
this.currentAccount = null;
|
||||
this.defaultKvStore = defaultKvStore;
|
||||
}
|
||||
|
||||
private boolean createAccount(@NonNull String userName, @NonNull String password) {
|
||||
Account account = getCurrentAccount();
|
||||
if (account == null || TextUtils.isEmpty(account.name) || !account.name.equals(userName)) {
|
||||
removeAccount();
|
||||
account = new Account(userName, BuildConfig.ACCOUNT_TYPE);
|
||||
return accountManager().addAccountExplicitly(account, password, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void removeAccount() {
|
||||
Account account = getCurrentAccount();
|
||||
if (account != null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
accountManager().removeAccountExplicitly(account);
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
accountManager().removeAccount(account, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAccount(LoginResult result) {
|
||||
boolean accountCreated = createAccount(result.getUserName(), result.getPassword());
|
||||
if (accountCreated) {
|
||||
setPassword(result.getPassword());
|
||||
}
|
||||
}
|
||||
|
||||
private void setPassword(@NonNull String password) {
|
||||
Account account = getCurrentAccount();
|
||||
if (account != null) {
|
||||
accountManager().setPassword(account, password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Account|null
|
||||
*/
|
||||
@Nullable
|
||||
public Account getCurrentAccount() {
|
||||
if (currentAccount == null) {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
Account[] allAccounts = accountManager.getAccountsByType(BuildConfig.ACCOUNT_TYPE);
|
||||
if (allAccounts.length != 0) {
|
||||
currentAccount = allAccounts[0];
|
||||
}
|
||||
}
|
||||
return currentAccount;
|
||||
}
|
||||
|
||||
public boolean doesAccountExist() {
|
||||
return getCurrentAccount() != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getUserName() {
|
||||
Account account = getCurrentAccount();
|
||||
return account == null ? null : account.name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getPassword() {
|
||||
Account account = getCurrentAccount();
|
||||
return account == null ? null : accountManager().getPassword(account);
|
||||
}
|
||||
|
||||
private AccountManager accountManager() {
|
||||
return AccountManager.get(context);
|
||||
}
|
||||
|
||||
public boolean isUserLoggedIn() {
|
||||
return defaultKvStore.getBoolean("isUserLoggedIn", false);
|
||||
}
|
||||
|
||||
void setUserLoggedIn(boolean isLoggedIn) {
|
||||
defaultKvStore.putBoolean("isUserLoggedIn", isLoggedIn);
|
||||
}
|
||||
|
||||
public void forceLogin(Context context) {
|
||||
if (context != null) {
|
||||
LoginActivity.startYourself(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Completable that clears existing accounts from account manager
|
||||
*/
|
||||
public Completable logout() {
|
||||
return Completable.fromObservable(
|
||||
Observable.empty()
|
||||
.doOnComplete(
|
||||
() -> {
|
||||
removeAccount();
|
||||
currentAccount = null;
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a corresponding boolean preference
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public boolean getPreference(String key) {
|
||||
return defaultKvStore.getBoolean(key);
|
||||
}
|
||||
}
|
||||
95
app/src/main/java/fr/free/nrw/commons/auth/SessionManager.kt
Normal file
95
app/src/main/java/fr/free/nrw/commons/auth/SessionManager.kt
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import fr.free.nrw.commons.BuildConfig.ACCOUNT_TYPE
|
||||
import fr.free.nrw.commons.auth.login.LoginResult
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Manage the current logged in user session.
|
||||
*/
|
||||
@Singleton
|
||||
class SessionManager @Inject constructor(
|
||||
private val context: Context,
|
||||
@param:Named("default_preferences") private val defaultKvStore: JsonKvStore
|
||||
) {
|
||||
private val accountManager: AccountManager get() = AccountManager.get(context)
|
||||
|
||||
private var _currentAccount: Account? = null // Unlike a savings account... ;-)
|
||||
val currentAccount: Account? get() {
|
||||
if (_currentAccount == null) {
|
||||
val allAccounts = AccountManager.get(context).getAccountsByType(ACCOUNT_TYPE)
|
||||
if (allAccounts.isNotEmpty()) {
|
||||
_currentAccount = allAccounts[0]
|
||||
}
|
||||
}
|
||||
return _currentAccount
|
||||
}
|
||||
|
||||
val userName: String?
|
||||
get() = currentAccount?.name
|
||||
|
||||
var password: String?
|
||||
get() = currentAccount?.let { accountManager.getPassword(it) }
|
||||
private set(value) {
|
||||
currentAccount?.let { accountManager.setPassword(it, value) }
|
||||
}
|
||||
|
||||
val isUserLoggedIn: Boolean
|
||||
get() = defaultKvStore.getBoolean("isUserLoggedIn", false)
|
||||
|
||||
fun updateAccount(result: LoginResult) {
|
||||
if (createAccount(result.userName!!, result.password!!)) {
|
||||
password = result.password
|
||||
}
|
||||
}
|
||||
|
||||
fun doesAccountExist(): Boolean =
|
||||
currentAccount != null
|
||||
|
||||
fun setUserLoggedIn(isLoggedIn: Boolean) =
|
||||
defaultKvStore.putBoolean("isUserLoggedIn", isLoggedIn)
|
||||
|
||||
fun forceLogin(context: Context?) =
|
||||
context?.let { LoginActivity.startYourself(it) }
|
||||
|
||||
fun getPreference(key: String?): Boolean =
|
||||
defaultKvStore.getBoolean(key)
|
||||
|
||||
fun logout(): Completable = Completable.fromObservable(
|
||||
Observable.empty<Any>()
|
||||
.doOnComplete {
|
||||
removeAccount()
|
||||
_currentAccount = null
|
||||
}
|
||||
)
|
||||
|
||||
private fun createAccount(userName: String, password: String): Boolean {
|
||||
var account = currentAccount
|
||||
if (account == null || TextUtils.isEmpty(account.name) || account.name != userName) {
|
||||
removeAccount()
|
||||
account = Account(userName, ACCOUNT_TYPE)
|
||||
return accountManager.addAccountExplicitly(account, password, null)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun removeAccount() {
|
||||
currentAccount?.let {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
accountManager.removeAccountExplicitly(it)
|
||||
} else {
|
||||
accountManager.removeAccount(it, null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Toast;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class SignupActivity extends BaseActivity {
|
||||
|
||||
private WebView webView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Timber.d("Signup Activity started");
|
||||
|
||||
webView = new WebView(this);
|
||||
setContentView(webView);
|
||||
|
||||
webView.setWebViewClient(new MyWebViewClient());
|
||||
WebSettings webSettings = webView.getSettings();
|
||||
/*Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can
|
||||
trust Wikimedia's site... right?*/
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
|
||||
webView.loadUrl(BuildConfig.SIGNUP_LANDING_URL);
|
||||
}
|
||||
|
||||
private class MyWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if (url.equals(BuildConfig.SIGNUP_SUCCESS_REDIRECTION_URL)) {
|
||||
//Signup success, so clear cookies, notify user, and load LoginActivity again
|
||||
Timber.d("Overriding URL %s", url);
|
||||
|
||||
Toast toast = Toast.makeText(SignupActivity.this,
|
||||
R.string.account_created, Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
// terminate on task completion.
|
||||
finish();
|
||||
return true;
|
||||
} else {
|
||||
//If user clicks any other links in the webview
|
||||
Timber.d("Not overriding URL, URL is: %s", url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (webView.canGoBack()) {
|
||||
webView.goBack();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Known bug in androidx.appcompat library version 1.1.0 being tracked here
|
||||
* https://issuetracker.google.com/issues/141132133
|
||||
* App tries to put light/dark theme to webview and crashes in the process
|
||||
* This code tries to prevent applying the theme when sdk is between api 21 to 25
|
||||
* @param overrideConfiguration
|
||||
*/
|
||||
@Override
|
||||
public void applyOverrideConfiguration(final Configuration overrideConfiguration) {
|
||||
if (Build.VERSION.SDK_INT <= 25 &&
|
||||
(getResources().getConfiguration().uiMode == getApplicationContext().getResources().getConfiguration().uiMode)) {
|
||||
return;
|
||||
}
|
||||
super.applyOverrideConfiguration(overrideConfiguration);
|
||||
}
|
||||
}
|
||||
75
app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt
Normal file
75
app/src/main/java/fr/free/nrw/commons/auth/SignupActivity.kt
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Configuration
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.widget.Toast
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import timber.log.Timber
|
||||
|
||||
class SignupActivity : BaseActivity() {
|
||||
private var webView: WebView? = null
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.d("Signup Activity started")
|
||||
|
||||
webView = WebView(this)
|
||||
with(webView!!) {
|
||||
setContentView(this)
|
||||
webViewClient = MyWebViewClient()
|
||||
// Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can
|
||||
// trust Wikimedia's site... right?
|
||||
settings.javaScriptEnabled = true
|
||||
loadUrl(BuildConfig.SIGNUP_LANDING_URL)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (webView!!.canGoBack()) {
|
||||
webView!!.goBack()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Known bug in androidx.appcompat library version 1.1.0 being tracked here
|
||||
* https://issuetracker.google.com/issues/141132133
|
||||
* App tries to put light/dark theme to webview and crashes in the process
|
||||
* This code tries to prevent applying the theme when sdk is between api 21 to 25
|
||||
*/
|
||||
override fun applyOverrideConfiguration(overrideConfiguration: Configuration) {
|
||||
if (Build.VERSION.SDK_INT <= 25 &&
|
||||
(resources.configuration.uiMode == applicationContext.resources.configuration.uiMode)
|
||||
) return
|
||||
super.applyOverrideConfiguration(overrideConfiguration)
|
||||
}
|
||||
|
||||
private inner class MyWebViewClient : WebViewClient() {
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean =
|
||||
if (url == BuildConfig.SIGNUP_SUCCESS_REDIRECTION_URL) {
|
||||
//Signup success, so clear cookies, notify user, and load LoginActivity again
|
||||
Timber.d("Overriding URL %s", url)
|
||||
|
||||
Toast.makeText(
|
||||
this@SignupActivity, R.string.account_created, Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
// terminate on task completion.
|
||||
finish()
|
||||
true
|
||||
} else {
|
||||
//If user clicks any other links in the webview
|
||||
Timber.d("Not overriding URL, URL is: %s", url)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.NetworkErrorException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
|
||||
import static fr.free.nrw.commons.auth.AccountUtil.AUTH_TOKEN_TYPE;
|
||||
|
||||
/**
|
||||
* Handles WikiMedia commons account Authentication
|
||||
*/
|
||||
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
private static final String[] SYNC_AUTHORITIES = {BuildConfig.CONTRIBUTION_AUTHORITY, BuildConfig.MODIFICATION_AUTHORITY};
|
||||
|
||||
@NonNull
|
||||
private final Context context;
|
||||
|
||||
public WikiAccountAuthenticator(@NonNull Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Bundle with edited Account Properties
|
||||
*/
|
||||
@Override
|
||||
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", "editProperties");
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle addAccount(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull String accountType, @Nullable String authTokenType,
|
||||
@Nullable String[] requiredFeatures, @Nullable Bundle options)
|
||||
throws NetworkErrorException {
|
||||
// account type not supported returns bundle without loginActivity Intent, it just contains "test" key
|
||||
if (!supportedAccountType(accountType)) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", "addAccount");
|
||||
return bundle;
|
||||
}
|
||||
|
||||
return addAccount(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle confirmCredentials(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull Account account, @Nullable Bundle options)
|
||||
throws NetworkErrorException {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", "confirmCredentials");
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAuthToken(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull Account account, @NonNull String authTokenType,
|
||||
@Nullable Bundle options)
|
||||
throws NetworkErrorException {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", "getAuthToken");
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getAuthTokenLabel(@NonNull String authTokenType) {
|
||||
return supportedAccountType(authTokenType) ? AUTH_TOKEN_TYPE : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bundle updateCredentials(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull Account account, @Nullable String authTokenType,
|
||||
@Nullable Bundle options)
|
||||
throws NetworkErrorException {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("test", "updateCredentials");
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bundle hasFeatures(@NonNull AccountAuthenticatorResponse response,
|
||||
@NonNull Account account, @NonNull String[] features)
|
||||
throws NetworkErrorException {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
private boolean supportedAccountType(@Nullable String type) {
|
||||
return BuildConfig.ACCOUNT_TYPE.equals(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a bundle containing a Parcel
|
||||
* the Parcel packs an Intent with LoginActivity and Authenticator response (requires valid account type)
|
||||
*/
|
||||
private Bundle addAccount(AccountAuthenticatorResponse response) {
|
||||
Intent intent = new Intent(context, LoginActivity.class);
|
||||
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
|
||||
Account account) throws NetworkErrorException {
|
||||
Bundle result = super.getAccountRemovalAllowed(response, account);
|
||||
|
||||
if (result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
|
||||
&& !result.containsKey(AccountManager.KEY_INTENT)) {
|
||||
boolean allowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
|
||||
|
||||
if (allowed) {
|
||||
for (String auth : SYNC_AUTHORITIES) {
|
||||
ContentResolver.cancelSync(account, auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountAuthenticatorResponse
|
||||
import android.accounts.AccountManager
|
||||
import android.accounts.NetworkErrorException
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
|
||||
private val SYNC_AUTHORITIES = arrayOf(
|
||||
BuildConfig.CONTRIBUTION_AUTHORITY, BuildConfig.MODIFICATION_AUTHORITY
|
||||
)
|
||||
|
||||
/**
|
||||
* Handles WikiMedia commons account Authentication
|
||||
*/
|
||||
class WikiAccountAuthenticator(
|
||||
private val context: Context
|
||||
) : AbstractAccountAuthenticator(context) {
|
||||
/**
|
||||
* Provides Bundle with edited Account Properties
|
||||
*/
|
||||
override fun editProperties(
|
||||
response: AccountAuthenticatorResponse,
|
||||
accountType: String
|
||||
) = bundleOf("test" to "editProperties")
|
||||
|
||||
// account type not supported returns bundle without loginActivity Intent, it just contains "test" key
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun addAccount(
|
||||
response: AccountAuthenticatorResponse,
|
||||
accountType: String,
|
||||
authTokenType: String?,
|
||||
requiredFeatures: Array<String>?,
|
||||
options: Bundle?
|
||||
) = if (BuildConfig.ACCOUNT_TYPE == accountType) {
|
||||
addAccount(response)
|
||||
} else {
|
||||
bundleOf("test" to "addAccount")
|
||||
}
|
||||
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun confirmCredentials(
|
||||
response: AccountAuthenticatorResponse, account: Account, options: Bundle?
|
||||
) = bundleOf("test" to "confirmCredentials")
|
||||
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun getAuthToken(
|
||||
response: AccountAuthenticatorResponse,
|
||||
account: Account,
|
||||
authTokenType: String,
|
||||
options: Bundle?
|
||||
) = bundleOf("test" to "getAuthToken")
|
||||
|
||||
override fun getAuthTokenLabel(authTokenType: String) =
|
||||
if (BuildConfig.ACCOUNT_TYPE == authTokenType) AUTH_TOKEN_TYPE else null
|
||||
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun updateCredentials(
|
||||
response: AccountAuthenticatorResponse,
|
||||
account: Account,
|
||||
authTokenType: String?,
|
||||
options: Bundle?
|
||||
) = bundleOf("test" to "updateCredentials")
|
||||
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun hasFeatures(
|
||||
response: AccountAuthenticatorResponse,
|
||||
account: Account, features: Array<String>
|
||||
) = bundleOf(AccountManager.KEY_BOOLEAN_RESULT to false)
|
||||
|
||||
/**
|
||||
* Provides a bundle containing a Parcel
|
||||
* the Parcel packs an Intent with LoginActivity and Authenticator response (requires valid account type)
|
||||
*/
|
||||
private fun addAccount(response: AccountAuthenticatorResponse): Bundle {
|
||||
val intent = Intent(context, LoginActivity::class.java)
|
||||
.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response)
|
||||
return bundleOf(AccountManager.KEY_INTENT to intent)
|
||||
}
|
||||
|
||||
@Throws(NetworkErrorException::class)
|
||||
override fun getAccountRemovalAllowed(
|
||||
response: AccountAuthenticatorResponse?,
|
||||
account: Account?
|
||||
): Bundle {
|
||||
val result = super.getAccountRemovalAllowed(response, account)
|
||||
|
||||
if (result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
|
||||
&& !result.containsKey(AccountManager.KEY_INTENT)
|
||||
) {
|
||||
val allowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)
|
||||
|
||||
if (allowed) {
|
||||
for (auth in SYNC_AUTHORITIES) {
|
||||
ContentResolver.cancelSync(account, auth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.di.CommonsDaggerService;
|
||||
|
||||
/**
|
||||
* Handles the Auth service of the App, see AndroidManifests for details
|
||||
* (Uses Dagger 2 as injector)
|
||||
*/
|
||||
public class WikiAccountAuthenticatorService extends CommonsDaggerService {
|
||||
|
||||
@Nullable
|
||||
private AbstractAccountAuthenticator authenticator;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
authenticator = new WikiAccountAuthenticator(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return authenticator == null ? null : authenticator.getIBinder();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package fr.free.nrw.commons.auth
|
||||
|
||||
import android.accounts.AbstractAccountAuthenticator
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import fr.free.nrw.commons.di.CommonsDaggerService
|
||||
|
||||
/**
|
||||
* Handles the Auth service of the App, see AndroidManifests for details
|
||||
* (Uses Dagger 2 as injector)
|
||||
*/
|
||||
class WikiAccountAuthenticatorService : CommonsDaggerService() {
|
||||
private var authenticator: AbstractAccountAuthenticator? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
authenticator = WikiAccountAuthenticator(this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? =
|
||||
authenticator?.iBinder
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ import dagger.Module;
|
|||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao;
|
||||
|
|
@ -114,11 +113,6 @@ public class CommonsApplicationModule {
|
|||
return byName;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public AccountUtil providesAccountUtil(Context context) {
|
||||
return new AccountUtil();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an instance of CategoryContentProviderClient i.e. the categories
|
||||
* that are there in local storage
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package fr.free.nrw.commons.feedback;
|
|||
|
||||
import android.content.Context;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.auth.AccountUtilKt;
|
||||
import fr.free.nrw.commons.feedback.model.Feedback;
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils;
|
||||
import java.util.Locale;
|
||||
|
|
@ -43,7 +43,7 @@ public class FeedbackContentCreator {
|
|||
|
||||
sectionTitleBuilder = new StringBuilder();
|
||||
sectionTitleBuilder.append("Feedback from ");
|
||||
sectionTitleBuilder.append(AccountUtil.getUserName(context));
|
||||
sectionTitleBuilder.append(AccountUtilKt.getUserName(context));
|
||||
sectionTitleBuilder.append(" for version ");
|
||||
sectionTitleBuilder.append(feedback.getVersion());
|
||||
sectionTitleBuilder.append(" on ");
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ import fr.free.nrw.commons.MediaDataExtractor;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.actions.ThanksClient;
|
||||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.auth.AccountUtilKt;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException;
|
||||
import fr.free.nrw.commons.category.CategoryClient;
|
||||
|
|
@ -382,8 +382,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
enableProgressBar();
|
||||
}
|
||||
|
||||
if (AccountUtil.getUserName(getContext()) != null && media != null
|
||||
&& AccountUtil.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
if (AccountUtilKt.getUserName(getContext()) != null && media != null
|
||||
&& AccountUtilKt.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
binding.sendThanks.setVisibility(GONE);
|
||||
} else {
|
||||
binding.sendThanks.setVisibility(VISIBLE);
|
||||
|
|
@ -485,7 +485,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
}
|
||||
|
||||
private void onDeletionPageExists(Boolean deletionPageExists) {
|
||||
if (AccountUtil.getUserName(getContext()) == null && !AccountUtil.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
if (AccountUtilKt.getUserName(getContext()) == null && !AccountUtilKt.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
binding.nominateDeletion.setVisibility(GONE);
|
||||
binding.nominatedDeletionBanner.setVisibility(GONE);
|
||||
} else if (deletionPageExists) {
|
||||
|
|
@ -1079,7 +1079,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
public void onDeleteButtonClicked(){
|
||||
if (AccountUtil.getUserName(getContext()) != null && AccountUtil.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
if (AccountUtilKt.getUserName(getContext()) != null && AccountUtilKt.getUserName(getContext()).equals(media.getAuthor())) {
|
||||
final ArrayAdapter<String> languageAdapter = new ArrayAdapter<>(getActivity(),
|
||||
R.layout.simple_spinner_dropdown_list, reasonList);
|
||||
final Spinner spinner = new Spinner(getActivity());
|
||||
|
|
@ -1105,7 +1105,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
|
|||
//Reviewer correct me if i have misunderstood something over here
|
||||
//But how does this if (delete.getVisibility() == View.VISIBLE) {
|
||||
// enableDeleteButton(true); makes sense ?
|
||||
else if (AccountUtil.getUserName(getContext()) != null) {
|
||||
else if (AccountUtilKt.getUserName(getContext()) != null) {
|
||||
final EditText input = new EditText(getActivity());
|
||||
input.requestFocus();
|
||||
AlertDialog d = DialogUtil.showAlertDialog(getActivity(),
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class NotificationActivity : BaseActivity() {
|
|||
}
|
||||
}, { throwable ->
|
||||
if (throwable is InvalidLoginTokenException) {
|
||||
val username = sessionManager.getUserName()
|
||||
val username = sessionManager.userName
|
||||
val logoutListener = CommonsApplication.BaseLogoutListener(
|
||||
this,
|
||||
getString(R.string.invalid_login_message),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import android.view.MotionEvent
|
|||
import android.view.View
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.auth.AccountUtil
|
||||
import fr.free.nrw.commons.auth.getUserName
|
||||
import fr.free.nrw.commons.databinding.ActivityReviewBinding
|
||||
import fr.free.nrw.commons.delete.DeleteHelper
|
||||
import fr.free.nrw.commons.media.MediaDetailFragment
|
||||
|
|
@ -183,7 +183,7 @@ class ReviewActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
//If The Media User and Current Session Username is same then Skip the Image
|
||||
if (media.user == AccountUtil.getUserName(applicationContext)) {
|
||||
if (media.user == getUserName(applicationContext)) {
|
||||
runRandomizer()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class FailedUploadsFragment :
|
|||
}
|
||||
|
||||
if (StringUtils.isEmpty(userName)) {
|
||||
userName = sessionManager.getUserName()
|
||||
userName = sessionManager.userName
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class AbstractTextWatcher(
|
|||
// No-op
|
||||
}
|
||||
|
||||
interface TextChange {
|
||||
fun interface TextChange {
|
||||
fun onTextChanged(value: String)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import android.content.Context
|
|||
import androidx.collection.LruCache
|
||||
import com.google.gson.Gson
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import fr.free.nrw.commons.auth.AccountUtil
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.di.CommonsApplicationComponent
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
|
|
@ -41,7 +40,6 @@ class TestCommonsApplication : Application() {
|
|||
class MockCommonsApplicationModule(
|
||||
appContext: Context,
|
||||
) : CommonsApplicationModule(appContext) {
|
||||
val accountUtil: AccountUtil = mock()
|
||||
val defaultSharedPreferences: JsonKvStore = mock()
|
||||
val locationServiceManager: LocationServiceManager = mock()
|
||||
val mockDbOpenHelper: DBOpenHelper = mock()
|
||||
|
|
@ -58,8 +56,6 @@ class MockCommonsApplicationModule(
|
|||
|
||||
override fun provideModificationContentProviderClient(context: Context?): ContentProviderClient = modificationClient
|
||||
|
||||
override fun providesAccountUtil(context: Context): AccountUtil = accountUtil
|
||||
|
||||
override fun providesDefaultKvStore(
|
||||
context: Context,
|
||||
gson: Gson,
|
||||
|
|
|
|||
|
|
@ -15,25 +15,17 @@ import org.robolectric.annotation.Config
|
|||
@Config(sdk = [21], application = TestCommonsApplication::class)
|
||||
class AccountUtilUnitTest {
|
||||
private lateinit var context: FakeContextWrapper
|
||||
private lateinit var accountUtil: AccountUtil
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
context = FakeContextWrapper(ApplicationProvider.getApplicationContext())
|
||||
accountUtil = AccountUtil()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun checkNotNull() {
|
||||
Assert.assertNotNull(accountUtil)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testGetUserName() {
|
||||
Assert.assertEquals(AccountUtil.getUserName(context), "test@example.com")
|
||||
Assert.assertEquals(getUserName(context), "test@example.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -41,13 +33,13 @@ class AccountUtilUnitTest {
|
|||
fun testGetUserNameWithException() {
|
||||
val context =
|
||||
FakeContextWrapperWithException(ApplicationProvider.getApplicationContext())
|
||||
Assert.assertEquals(AccountUtil.getUserName(context), null)
|
||||
Assert.assertEquals(getUserName(context), null)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testAccount() {
|
||||
Assert.assertEquals(AccountUtil.account(context)?.name, "test@example.com")
|
||||
Assert.assertEquals(account(context)?.name, "test@example.com")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -55,6 +47,6 @@ class AccountUtilUnitTest {
|
|||
fun testAccountWithException() {
|
||||
val context =
|
||||
FakeContextWrapperWithException(ApplicationProvider.getApplicationContext())
|
||||
Assert.assertEquals(AccountUtil.account(context), null)
|
||||
Assert.assertEquals(account(context), null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,17 +218,6 @@ class LoginActivityUnitTests {
|
|||
method.invoke(activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testHideProgress() {
|
||||
val method: Method =
|
||||
LoginActivity::class.java.getDeclaredMethod(
|
||||
"hideProgress",
|
||||
)
|
||||
method.isAccessible = true
|
||||
method.invoke(activity)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOnResume() {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,13 @@ package fr.free.nrw.commons.auth
|
|||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.lang.reflect.Field
|
||||
|
||||
class WikiAccountAuthenticatorServiceUnitTest {
|
||||
private lateinit var service: WikiAccountAuthenticatorService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
service = WikiAccountAuthenticatorService()
|
||||
service.onBind(null)
|
||||
}
|
||||
private val service = WikiAccountAuthenticatorService()
|
||||
|
||||
@Test
|
||||
fun checkNotNull() {
|
||||
|
|
@ -23,10 +18,9 @@ class WikiAccountAuthenticatorServiceUnitTest {
|
|||
|
||||
@Test
|
||||
fun testOnBindCaseNull() {
|
||||
val field: Field =
|
||||
WikiAccountAuthenticatorService::class.java.getDeclaredField("authenticator")
|
||||
val field: Field = WikiAccountAuthenticatorService::class.java.getDeclaredField("authenticator")
|
||||
field.isAccessible = true
|
||||
field.set(service, null)
|
||||
Assert.assertEquals(service.onBind(null), null)
|
||||
Assert.assertEquals(service.onBind(mock()), null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,10 +86,7 @@ class WikiAccountAuthenticatorUnitTest {
|
|||
|
||||
@Test
|
||||
fun testGetAuthTokenLabelCaseNonNull() {
|
||||
Assert.assertEquals(
|
||||
authenticator.getAuthTokenLabel(BuildConfig.ACCOUNT_TYPE),
|
||||
AccountUtil.AUTH_TOKEN_TYPE,
|
||||
)
|
||||
Assert.assertEquals(authenticator.getAuthTokenLabel(BuildConfig.ACCOUNT_TYPE), AUTH_TOKEN_TYPE)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue