package com.gertherb.authentication;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.os.Bundle;
import java.io.IOException;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.exceptions.OnErrorThrowable;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public class BetterAccountManager {
private final AccountManager accountManager;
private final Activity activity;
private final String accountType;
private final String tokenType;
private final String[] requiredFeatures;
public BetterAccountManager(Activity activity, String accountType, String tokenType, String[] requiredFeatures) {
this.activity = activity;
this.accountType = accountType;
this.tokenType = tokenType;
this.requiredFeatures = requiredFeatures;
this.accountManager = AccountManager.get(activity.getApplicationContext());
}
public Observable<Token> addAccountAndGetToken(final Bundle options) {
return withDefaultSchedulers(addAccount(options).flatMap(new Func1<String, Observable<? extends Token>>() {
@Override
public Observable<? extends Token> call(String s) {
return getSessionKey(options);
}
}));
}
public Observable<Token> getSessionKeyOrAddAccount(Bundle options) {
return withDefaultSchedulers(getSessionKey(options).onErrorFlatMap(addNewAccount(options)));
}
private Observable<Token> getSessionKey(final Bundle options) {
return Observable.create(new Observable.OnSubscribe<Token>() {
@Override
public void call(Subscriber<? super Token> subscriber) {
Account[] accounts = accountManager.getAccountsByType(accountType);
if (accounts.length == 0) {
subscriber.onError(new NoAccountException());
return;
}
Bundle result;
try {
result = getAuthToken(accounts[0], options);
} catch (AuthenticatorException e) {
subscriber.onError(e);
return;
} catch (OperationCanceledException e) {
subscriber.onError(e);
return;
} catch (IOException e) {
subscriber.onError(e);
return;
}
subscriber.onNext(new Token(accounts[0].name, result.getString(AccountManager.KEY_AUTHTOKEN)));
subscriber.onCompleted();
}
});
}
private Bundle getAuthToken(Account account, Bundle options) throws AuthenticatorException, OperationCanceledException, IOException {
return accountManager.getAuthToken(account, tokenType, options, activity, null, null).getResult();
}
private Func1<OnErrorThrowable, Observable<? extends Token>> addNewAccount(final Bundle options) {
return new Func1<OnErrorThrowable, Observable<? extends Token>>() {
@Override
public Observable<? extends Token> call(OnErrorThrowable onErrorThrowable) {
if (onErrorThrowable.getCause() instanceof NoAccountException) {
return addAccountAndGetToken(options);
}
return Observable.error(onErrorThrowable.getCause());
}
};
}
private Observable<String> addAccount(final Bundle options) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
Bundle result;
try {
result = accountManager.addAccount(accountType, tokenType, requiredFeatures, options, activity, null, null).getResult();
} catch (OperationCanceledException e) {
subscriber.onError(e);
return;
} catch (IOException e) {
subscriber.onError(e);
return;
} catch (AuthenticatorException e) {
subscriber.onError(e);
return;
}
subscriber.onNext(result.getString(AccountManager.KEY_ACCOUNT_NAME));
subscriber.onCompleted();
}
});
}
private <T> Observable<T> withDefaultSchedulers(Observable<T> observable) {
return observable.subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread());
}
}