diff --git a/app/src/main/java/fi/bitrite/android/ws/auth/AccountManager.java b/app/src/main/java/fi/bitrite/android/ws/auth/AccountManager.java index d37c7f21..8adbbebf 100644 --- a/app/src/main/java/fi/bitrite/android/ws/auth/AccountManager.java +++ b/app/src/main/java/fi/bitrite/android/ws/auth/AccountManager.java @@ -19,6 +19,8 @@ import fi.bitrite.android.ws.di.AppScope; import fi.bitrite.android.ws.ui.MainActivity; import fi.bitrite.android.ws.util.MaybeNull; +import io.reactivex.Maybe; +import io.reactivex.MaybeObserver; import io.reactivex.Observable; import io.reactivex.Single; import io.reactivex.functions.Function; @@ -45,7 +47,7 @@ public class AccountManager { private final Observable mCurrentUserId; private Activity mMainActivity = null; - private Intent mCreateOrAuthAccountIntent = null; + private EventuallyCreateOrAuth mEventuallyCreateOrAuth = null; @Inject AccountManager(WarmshowersWebservice generalWebservice, @@ -86,7 +88,7 @@ public class AccountManager { }); } else { // We have an account and therefore no longer need to create a new one. - mCreateOrAuthAccountIntent = null; + dismissEventuallyCreateOrAuth(); } }); @@ -122,11 +124,13 @@ public Observable getCurrentUserId() { */ public void setMainActivity(MainActivity mainActivity) { mMainActivity = mainActivity; - if (mCreateOrAuthAccountIntent != null) { + if (mEventuallyCreateOrAuth != null) { // The main activity was not around when we wanted to show the account creation screen. // Do it now. - mMainActivity.startActivity(mCreateOrAuthAccountIntent); - mCreateOrAuthAccountIntent = null; + final Intent intent = mEventuallyCreateOrAuth.intent; + final MaybeObserver observer = mEventuallyCreateOrAuth.observer; + mEventuallyCreateOrAuth = null; + startActivityForResult(intent, observer); } } @@ -144,22 +148,17 @@ public Single createNewAccount() { try { Bundle result = accountManagerFuture.getResult(); - boolean containsIntent = handleIntentInBundle(result); - if (containsIntent) { - // FIXME(saemy): This callback does not get called a second time when the - // login completed. So we need to find a way to call the below onSuccess() - // method... - return; - } - - String name = - result.getString(android.accounts.AccountManager.KEY_ACCOUNT_NAME); - String type = - result.getString(android.accounts.AccountManager.KEY_ACCOUNT_TYPE); + handleIntentInBundle(result) + .subscribe(result2 -> { + final String name = result2.getString( + android.accounts.AccountManager.KEY_ACCOUNT_NAME); + final String type = result2.getString( + android.accounts.AccountManager.KEY_ACCOUNT_TYPE); - // TODO(saemy): Mark this account as the active one. + // TODO(saemy): Mark this account as the active one. - emitter.onSuccess(new Account(name, type)); + emitter.onSuccess(new Account(name, type)); + }, emitter::onError); } catch (Exception e) { emitter.onError(e); } @@ -203,19 +202,14 @@ public Single getAuthToken(@NonNull Account account) { try { Bundle result = tokenFuture.getResult(); - boolean containsIntent = handleIntentInBundle(result); - if (containsIntent) { - // FIXME(saemy): This callback does not get called a second time when the - // login completed. So we need to find a way to call the below onSuccess() - // method... - return; - } + handleIntentInBundle(result) + .subscribe(result2 -> { + String authTokenStr = result2.getString( + android.accounts.AccountManager.KEY_AUTHTOKEN); + AuthToken authToken = AuthToken.fromString(authTokenStr); - String authTokenStr = - result.getString(android.accounts.AccountManager.KEY_AUTHTOKEN); - AuthToken authToken = AuthToken.fromString(authTokenStr); - - emitter.onSuccess(authToken); + emitter.onSuccess(authToken); + }, emitter::onError); } catch (Exception e) { emitter.onError(e); } @@ -235,18 +229,46 @@ public Single getAuthToken(@NonNull Account account) { }); } - private boolean handleIntentInBundle(Bundle result) { + /** + * Calls the intent that is stored in the given bundle. If no main activity is started yet, the + * intent is saved for later usage. If no intent is saved in the bundle nothing is done. + * + * @return + * The single that is triggered as soon as the final bundle is available. That is the + * one given in case no intent is in it or the one that is eventually returned from the + * started activity. + */ + private Maybe handleIntentInBundle(Bundle result) { if (!result.containsKey(android.accounts.AccountManager.KEY_INTENT)) { - return false; + return Maybe.just(result); } - final Intent intent = result.getParcelable(android.accounts.AccountManager.KEY_INTENT); - if (mMainActivity != null) { - mMainActivity.startActivity(intent); + return new Maybe() { + @Override + protected void subscribeActual(MaybeObserver observer) { + Intent intent = result.getParcelable(android.accounts.AccountManager.KEY_INTENT); + startActivityForResult(intent, observer); + } + }; + } + + private void startActivityForResult(Intent intent, MaybeObserver observer){ + MainActivity mainActivity = (MainActivity) mMainActivity; + if (mainActivity != null) { + mainActivity.startActivityForResultRx(intent) + .subscribe(intent2 -> observer.onSuccess(intent2.getExtras()), + observer::onError); } else { - mCreateOrAuthAccountIntent = intent; + dismissEventuallyCreateOrAuth(); + mEventuallyCreateOrAuth = new EventuallyCreateOrAuth(intent, observer); + } + } + + private void dismissEventuallyCreateOrAuth() { + if (mEventuallyCreateOrAuth != null) { + mEventuallyCreateOrAuth.observer.onComplete(); + mEventuallyCreateOrAuth = null; } - return true; } /** @@ -306,6 +328,13 @@ public Observable login(String username, String password) { public int getUserId(@NonNull Account account) { return executeWithReadLock(v -> { + // Migration from version <2.0.0. + String oldUserIdStr = mAndroidAccountManager.getUserData(account,"userid"); + if (oldUserIdStr != null) { + mAndroidAccountManager.setUserData(account, KEY_USER_ID, oldUserIdStr); + mAndroidAccountManager.setUserData(account, "userid", null); + } + String userIdStr = mAndroidAccountManager.getUserData(account, KEY_USER_ID); return userIdStr != null ? Integer.parseInt(userIdStr) @@ -394,4 +423,14 @@ public AuthData authData() { return mAuthData; } } + + class EventuallyCreateOrAuth { + final Intent intent; + final MaybeObserver observer; + + EventuallyCreateOrAuth(Intent intent, MaybeObserver observer) { + this.intent = intent; + this.observer = observer; + } + } } diff --git a/app/src/main/java/fi/bitrite/android/ws/ui/AuthenticatorActivity.java b/app/src/main/java/fi/bitrite/android/ws/ui/AuthenticatorActivity.java index d477cf0f..741cb756 100644 --- a/app/src/main/java/fi/bitrite/android/ws/ui/AuthenticatorActivity.java +++ b/app/src/main/java/fi/bitrite/android/ws/ui/AuthenticatorActivity.java @@ -164,6 +164,11 @@ public void login() { setAccountAuthenticatorResult(response); + // The following we add for usage in MainActivity::onActivityResult(). + Intent intent = new Intent(); + intent.putExtras(response); + setResult(0, intent); + finish(); } else { mProgressDisposable.dispose(); diff --git a/app/src/main/java/fi/bitrite/android/ws/ui/MainActivity.java b/app/src/main/java/fi/bitrite/android/ws/ui/MainActivity.java index 0a2a15d1..07ed00a7 100644 --- a/app/src/main/java/fi/bitrite/android/ws/ui/MainActivity.java +++ b/app/src/main/java/fi/bitrite/android/ws/ui/MainActivity.java @@ -14,6 +14,7 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; +import android.util.SparseArray; import android.view.Gravity; import android.widget.ImageView; import android.widget.ListView; @@ -47,6 +48,8 @@ import fi.bitrite.android.ws.ui.util.NavigationController; import fi.bitrite.android.ws.util.LoggedInUserHelper; import io.reactivex.Observable; +import io.reactivex.Single; +import io.reactivex.SingleObserver; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; @@ -310,6 +313,30 @@ public NavigationController getNavigationController() { return mNavigationController; } + private int mNextRequestCode = 0; + private final SparseArray> mActivityResultReactors = + new SparseArray<>(); + public Single startActivityForResultRx(Intent intent) { + return new Single() { + @Override + protected void subscribeActual(SingleObserver observer) { + int requestCode = mNextRequestCode++; + mActivityResultReactors.append(requestCode, observer); + MainActivity.super.startActivityForResult(intent, requestCode); + } + }; + } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + SingleObserver observer = mActivityResultReactors.get(requestCode); + if (observer != null) { + mActivityResultReactors.remove(requestCode); + observer.onSuccess(data); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + /** * This class can inject itself into an accountComponent. This is the bridge to get access to * the account scope from the appScoped {@link MainActivity}.