Skip to content

Instantly share code, notes, and snippets.

@iffa
Last active May 12, 2020 09:03
Show Gist options
  • Save iffa/07fbf982d13a585b3c728123cb282f4a to your computer and use it in GitHub Desktop.
Save iffa/07fbf982d13a585b3c728123cb282f4a to your computer and use it in GitHub Desktop.
AccountManager wrapper with an easier API and RxJava support
/**
* Wrapper for {@link AccountManager} with improved functionality and RxJava support.
*
* Originally found from <a href="https://github.com/novoda/spikes/tree/master/gertherb/android/src/main/java/com/gertherb/authentication">github.com/spikes/gertherb</a>,
* and modified to work with a newer RxJava version, using {@link Single} instead of {@link rx.Observable}.
*
* @author Ataul Munim
* @author Santeri Elo
*/
public class BetterAccountManager {
private final AccountManager accountManager;
private final Activity activity;
@Inject public BetterAccountManager(Activity activity) {
this.activity = activity;
this.accountManager = AccountManager.get(activity.getApplicationContext());
}
/**
* Starts the login flow and gets the new user's access token.
*
* @param options {@link Bundle} may be empty or null (not that important)
* @return Single emitting a {@link Token}
*/
public Single<Token> addAccountAndGetToken(final Bundle options) {
return addAccount(options)
.flatMap(s -> getSessionKey(options))
.compose(applySchedulers());
}
/**
* Gets the current user's access token. If no user is logged in, normal login flow is started.
*
* @param options {@link Bundle} may be empty or null (not that important)
* @return Single emitting a {@link Token}
*/
public Single<Token> getSessionKeyOrAddAccount(Bundle options) {
return getSessionKey(options)
.onErrorResumeNext(addNewAccount(options))
.compose(applySchedulers());
}
private Single<Token> getSessionKey(final Bundle options) {
return Single.create(new Single.OnSubscribe<Token>() {
@Override
public void call(SingleSubscriber<? super Token> subscriber) {
Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
if (accounts.length == 0) {
subscriber.onError(new NoAccountException());
return;
}
Bundle result;
try {
result = getAuthToken(accounts[0], options);
} catch (AuthenticatorException | OperationCanceledException | IOException e) {
subscriber.onError(e);
return;
}
subscriber.onSuccess(new Token(accounts[0].name, result.getString(AccountManager.KEY_AUTHTOKEN)));
}
});
}
private Bundle getAuthToken(Account account, Bundle options) throws AuthenticatorException, OperationCanceledException, IOException {
return accountManager.getAuthToken(account, AUTHTOKEN_TYPE, options, activity, null, null).getResult();
}
private Func1<Throwable, Single<? extends Token>> addNewAccount(final Bundle options) {
return onErrorThrowable -> {
if (onErrorThrowable.getCause() instanceof NoAccountException) {
return addAccountAndGetToken(options);
}
return Single.error(onErrorThrowable.getCause());
};
}
private Single<String> addAccount(final Bundle options) {
return Single.create(new Single.OnSubscribe<String>() {
@Override
public void call(SingleSubscriber<? super String> subscriber) {
Bundle result;
try {
result = accountManager.addAccount(ACCOUNT_TYPE, AUTHTOKEN_TYPE, null, options, activity, null, null).getResult();
} catch (OperationCanceledException | AuthenticatorException | IOException e) {
subscriber.onError(e);
return;
}
subscriber.onSuccess(result.getString(AccountManager.KEY_ACCOUNT_NAME));
}
});
}
private <T> Single.Transformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
}
@iffa
Copy link
Author

iffa commented Sep 18, 2019

This gist is for RxJava v1?

Yes, but I don't see why this couldn't be adapted for RxJava2+ with small changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment