11package fr .free .nrw .commons .auth ;
22
3- import android .accounts .Account ;
43import android .accounts .AccountAuthenticatorActivity ;
54import android .accounts .AccountAuthenticatorResponse ;
65import android .accounts .AccountManager ;
2221
2322import com .google .android .material .textfield .TextInputLayout ;
2423
25- import java .io .IOException ;
26- import java .util .Locale ;
24+ import org .wikipedia .AppAdapter ;
25+ import org .wikipedia .dataclient .WikiSite ;
26+ import org .wikipedia .login .LoginClient ;
27+ import org .wikipedia .login .LoginResult ;
2728
2829import javax .inject .Inject ;
2930import javax .inject .Named ;
3031
3132import androidx .annotation .ColorRes ;
3233import androidx .annotation .NonNull ;
34+ import androidx .annotation .Nullable ;
3335import androidx .annotation .StringRes ;
3436import androidx .appcompat .app .AlertDialog ;
3537import androidx .appcompat .app .AppCompatDelegate ;
5254import fr .free .nrw .commons .theme .NavigationBaseActivity ;
5355import fr .free .nrw .commons .utils .ConfigUtils ;
5456import fr .free .nrw .commons .utils .ViewUtil ;
55- import io .reactivex .Observable ;
56- import io .reactivex .android .schedulers .AndroidSchedulers ;
5757import io .reactivex .disposables .CompositeDisposable ;
58- import io .reactivex .schedulers .Schedulers ;
5958import timber .log .Timber ;
6059
6160import static android .view .KeyEvent .KEYCODE_ENTER ;
6261import static android .view .View .VISIBLE ;
6362import static android .view .inputmethod .EditorInfo .IME_ACTION_DONE ;
64- import static fr .free .nrw .commons .auth .AccountUtil .AUTH_TOKEN_TYPE ;
6563
6664public class LoginActivity extends AccountAuthenticatorActivity {
6765
@@ -99,18 +97,15 @@ public class LoginActivity extends AccountAuthenticatorActivity {
9997 @ BindView (R .id .two_factor_container )
10098 TextInputLayout twoFactorContainer ;
10199
100+ private LoginClient loginClient ;
101+ @ Nullable
102+ private String firstStepToken ;
103+
102104 ProgressDialog progressDialog ;
103105 private AppCompatDelegate delegate ;
104106 private LoginTextWatcher textWatcher = new LoginTextWatcher ();
105107 private CompositeDisposable compositeDisposable = new CompositeDisposable ();
106108
107- private Boolean loginCurrentlyInProgress = false ;
108- private Boolean errorMessageShown = false ;
109- private String resultantError ;
110- private static final String RESULTANT_ERROR = "resultantError" ;
111- private static final String ERROR_MESSAGE_SHOWN = "errorMessageShown" ;
112- private static final String LOGGING_IN = "loggingIn" ;
113-
114109 @ Override
115110 public void onCreate (Bundle savedInstanceState ) {
116111 super .onCreate (savedInstanceState );
@@ -244,33 +239,87 @@ protected void onDestroy() {
244239
245240 @ OnClick (R .id .login_button )
246241 public void performLogin () {
247- loginCurrentlyInProgress = true ;
248242 Timber .d ("Login to start!" );
249243 final String username = usernameEdit .getText ().toString ();
250244 final String rawUsername = usernameEdit .getText ().toString ().trim ();
251245 final String password = passwordEdit .getText ().toString ();
252246 String twoFactorCode = twoFactorEdit .getText ().toString ();
253247
254248 showLoggingProgressBar ();
255- compositeDisposable .add (Observable .fromCallable (() -> login (username , password , twoFactorCode ))
256- .subscribeOn (Schedulers .io ())
257- .observeOn (AndroidSchedulers .mainThread ())
258- .subscribe (result -> handleLogin (username , rawUsername , password , result )));
249+ doLogin (username , password , twoFactorCode );
259250 }
260251
261- private String login (String username , String password , String twoFactorCode ) {
262- try {
263- if (twoFactorCode .isEmpty ()) {
264- return mwApi .login (username , password );
265- } else {
266- return mwApi .login (username , password , twoFactorCode );
252+ private void doLogin (String username , String password , String twoFactorCode ) {
253+ if (loginClient == null ) {
254+ loginClient = new LoginClient ();
255+ }
256+ progressDialog .show ();
257+
258+ WikiSite wiki = new WikiSite (Uri .parse (AppAdapter .get ().getMediaWikiBaseUrl ()));
259+ if (!twoFactorCode .isEmpty ()) {
260+ try {
261+ loginClient .login (wiki ,
262+ username , password , null , twoFactorCode , firstStepToken , getCallback ());
263+ } catch (Throwable throwable ) {
264+ throwable .printStackTrace ();
267265 }
268- } catch (IOException e ) {
269- // Do something better!
270- return "NetworkFailure" ;
266+ } else {
267+ loginClient .request (wiki , username , password , getCallback ());
271268 }
272269 }
273270
271+ private LoginClient .LoginCallback getCallback () {
272+ return new LoginClient .LoginCallback () {
273+ @ Override
274+ public void success (@ NonNull LoginResult result ) {
275+ if (!progressDialog .isShowing ()) {
276+ // no longer attached to activity!
277+ return ;
278+ }
279+ progressDialog .dismiss ();
280+ if (result .pass ()) {
281+
282+ Bundle extras = getIntent ().getExtras ();
283+ AccountAuthenticatorResponse response = extras == null ? null
284+ : extras .getParcelable (AccountManager .KEY_ACCOUNT_AUTHENTICATOR_RESPONSE );
285+
286+ sessionManager .updateAccount (response , result );
287+
288+ onLoginSuccess ();
289+
290+ } else if (result .fail ()) {
291+ showMessageAndCancelDialog (result .getMessage ());
292+ }
293+ }
294+
295+ @ Override
296+ public void twoFactorPrompt (@ NonNull Throwable caught , @ Nullable String token ) {
297+ if (!progressDialog .isShowing ()) {
298+ // no longer attached to activity!
299+ return ;
300+ }
301+ progressDialog .dismiss ();
302+ firstStepToken = token ;
303+ askUserForTwoFactorAuth ();
304+ }
305+
306+ @ Override
307+ public void passwordResetPrompt (@ Nullable String token ) {
308+ forgotPassword ();
309+ }
310+
311+ @ Override
312+ public void error (@ NonNull Throwable caught ) {
313+ if (!progressDialog .isShowing ()) {
314+ // no longer attached to activity!
315+ return ;
316+ }
317+ progressDialog .dismiss ();
318+ showMessageAndCancelDialog (R .string .error_occurred );
319+ }
320+ };
321+ }
322+
274323 /**
275324 * This function is called when user skips the login.
276325 * It redirects the user to Explore Activity.
@@ -281,18 +330,6 @@ private void performSkipLogin() {
281330 finish ();
282331 }
283332
284- private void handleLogin (String username , String rawUsername , String password , String result ) {
285- Timber .d ("Login done!" );
286- if (result .equals ("PASS" )) {
287- handlePassResult (username , rawUsername , password );
288- } else {
289- loginCurrentlyInProgress = false ;
290- errorMessageShown = true ;
291- resultantError = result ;
292- handleOtherResults (result );
293- }
294- }
295-
296333 private void showLoggingProgressBar () {
297334 progressDialog = new ProgressDialog (this );
298335 progressDialog .setIndeterminate (true );
@@ -302,67 +339,11 @@ private void showLoggingProgressBar() {
302339 progressDialog .show ();
303340 }
304341
305- private void handlePassResult ( String username , String rawUsername , String password ) {
342+ private void onLoginSuccess ( ) {
306343 showSuccessAndDismissDialog ();
307- requestAuthToken ();
308- AccountAuthenticatorResponse response = null ;
309-
310- Bundle extras = getIntent ().getExtras ();
311- if (extras != null ) {
312- Timber .d ("Bundle of extras: %s" , extras );
313- response = extras .getParcelable (AccountManager .KEY_ACCOUNT_AUTHENTICATOR_RESPONSE );
314- if (response != null ) {
315- Bundle authResult = new Bundle ();
316- authResult .putString (AccountManager .KEY_ACCOUNT_NAME , username );
317- authResult .putString (AccountManager .KEY_ACCOUNT_TYPE , BuildConfig .ACCOUNT_TYPE );
318- response .onResult (authResult );
319- }
320- }
321-
322- sessionManager .createAccount (response , username , rawUsername , password );
323344 startMainActivity ();
324345 }
325346
326- protected void requestAuthToken () {
327- AccountManager accountManager = AccountManager .get (this );
328- Account curAccount = sessionManager .getCurrentAccount ();
329- if (curAccount != null ) {
330- accountManager .setAuthToken (curAccount , AUTH_TOKEN_TYPE , mwApi .getAuthCookie ());
331- }
332- }
333-
334- /**
335- * Match known failure message codes and provide messages.
336- *
337- * @param result String
338- */
339- private void handleOtherResults (String result ) {
340- if (result .equals ("NetworkFailure" )) {
341- // Matches NetworkFailure which is created by the doInBackground method
342- showMessageAndCancelDialog (R .string .login_failed_network );
343- } else if (result .toLowerCase (Locale .getDefault ()).contains ("nosuchuser" .toLowerCase ()) || result .toLowerCase ().contains ("noname" .toLowerCase ())) {
344- // Matches nosuchuser, nosuchusershort, noname
345- showMessageAndCancelDialog (R .string .login_failed_wrong_credentials );
346- emptySensitiveEditFields ();
347- } else if (result .toLowerCase (Locale .getDefault ()).contains ("wrongpassword" .toLowerCase ())) {
348- // Matches wrongpassword, wrongpasswordempty
349- showMessageAndCancelDialog (R .string .login_failed_wrong_credentials );
350- emptySensitiveEditFields ();
351- } else if (result .toLowerCase (Locale .getDefault ()).contains ("throttle" .toLowerCase ())) {
352- // Matches unknown throttle error codes
353- showMessageAndCancelDialog (R .string .login_failed_throttled );
354- } else if (result .toLowerCase (Locale .getDefault ()).contains ("userblocked" .toLowerCase ())) {
355- // Matches login-userblocked
356- showMessageAndCancelDialog (R .string .login_failed_blocked );
357- } else if (result .equals ("2FA" )) {
358- askUserForTwoFactorAuth ();
359- } else {
360- // Occurs with unhandled login failure codes
361- Timber .d ("Login failed with reason: %s" , result );
362- showMessageAndCancelDialog (R .string .login_failed_generic );
363- }
364- }
365-
366347 @ Override
367348 protected void onStart () {
368349 super .onStart ();
@@ -402,30 +383,6 @@ public MenuInflater getMenuInflater() {
402383 return getDelegate ().getMenuInflater ();
403384 }
404385
405- @ Override
406- protected void onSaveInstanceState (Bundle outState ) {
407- super .onSaveInstanceState (outState );
408- outState .putBoolean (LOGGING_IN , loginCurrentlyInProgress );
409- outState .putBoolean (ERROR_MESSAGE_SHOWN , errorMessageShown );
410- outState .putString (RESULTANT_ERROR , resultantError );
411- }
412-
413- @ Override
414- protected void onRestoreInstanceState (Bundle savedInstanceState ) {
415- super .onRestoreInstanceState (savedInstanceState );
416- loginCurrentlyInProgress = savedInstanceState .getBoolean (LOGGING_IN , false );
417- errorMessageShown = savedInstanceState .getBoolean (ERROR_MESSAGE_SHOWN , false );
418- if (loginCurrentlyInProgress ) {
419- performLogin ();
420- }
421- if (errorMessageShown ) {
422- resultantError = savedInstanceState .getString (RESULTANT_ERROR );
423- if (resultantError != null ) {
424- handleOtherResults (resultantError );
425- }
426- }
427- }
428-
429386 public void askUserForTwoFactorAuth () {
430387 progressDialog .dismiss ();
431388 twoFactorContainer .setVisibility (VISIBLE );
@@ -440,16 +397,18 @@ public void showMessageAndCancelDialog(@StringRes int resId) {
440397 }
441398 }
442399
400+ public void showMessageAndCancelDialog (String error ) {
401+ showMessage (error , R .color .secondaryDarkColor );
402+ if (progressDialog != null ) {
403+ progressDialog .cancel ();
404+ }
405+ }
406+
443407 public void showSuccessAndDismissDialog () {
444408 showMessage (R .string .login_success , R .color .primaryDarkColor );
445409 progressDialog .dismiss ();
446410 }
447411
448- public void emptySensitiveEditFields () {
449- passwordEdit .setText ("" );
450- twoFactorEdit .setText ("" );
451- }
452-
453412 public void startMainActivity () {
454413 NavigationBaseActivity .startActivityWithFlags (this , MainActivity .class , Intent .FLAG_ACTIVITY_SINGLE_TOP );
455414 finish ();
@@ -461,6 +420,12 @@ private void showMessage(@StringRes int resId, @ColorRes int colorResId) {
461420 errorMessageContainer .setVisibility (VISIBLE );
462421 }
463422
423+ private void showMessage (String message , @ ColorRes int colorResId ) {
424+ errorMessage .setText (message );
425+ errorMessage .setTextColor (ContextCompat .getColor (this , colorResId ));
426+ errorMessageContainer .setVisibility (VISIBLE );
427+ }
428+
464429 private AppCompatDelegate getDelegate () {
465430 if (delegate == null ) {
466431 delegate = AppCompatDelegate .create (this , null );
0 commit comments