package io.codetail.client.auth; import android.accounts.AbstractAccountAuthenticator; import android.accounts.Account; import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; import android.accounts.NetworkErrorException; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import java.util.List; import hugo.weaving.DebugLog; import io.codetail.WatchMeApplication; import io.codetail.sources.Source; import static android.accounts.AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE; import static android.accounts.AccountManager.KEY_ACCOUNT_NAME; import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE; import static android.accounts.AccountManager.KEY_AUTHTOKEN; import static android.accounts.AccountManager.KEY_ERROR_CODE; import static android.accounts.AccountManager.KEY_INTENT; import static android.accounts.AccountManager.get; import static io.codetail.fragments.NavigationFragment.USER_PICTURE_URL; public class BasicAccountAuthenticator extends AbstractAccountAuthenticator{ public final static String ACCOUNT_TYPE = "codetail.auth.WATCH_ME"; public final static int ERROR_CODE_INVALID_USER_DATA = 100; /** * Given authTokenType is not found in {@link io.codetail.WatchMeApplication#getSources()} */ public final static int INVALID_AUTH_TOKEN_TYPE = 404; /** * Activity Action: Display form to authenticate user. Used by * {@link android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler)} * to authorize user into service */ public final static String ACTION_AUTHENTICATE = "codetail.intent.action.AUTHENTICATE"; /** * Activity Action: Provide user to edit account properties. Used by * {@link android.accounts.AccountManager#editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback, android.os.Handler)} */ public final static String ACTION_EDIT_ACCOUNT_PROPERTIES = "codetail.intent.action.EDIT_ACCOUNT_PROPERTIES"; /** * Activity Action: Let user check his credentials */ public final static String ACTION_CONFIRM_CREDENTIALS = "codetail.intent.action.CONFIRM_CREDENTIALS"; /** * Parcelable data {@link android.accounts.Account} */ public final static String EXTRA_ACCOUNT = "codetail.intent.extra.ACCOUNT"; /** * String data used with {@link #ACTION_AUTHENTICATE} contains authorize service name */ public final static String EXTRA_AUTH_TOKEN_TYPE = "codetail.intent.extra.AUTH_TOKEN_TYPE"; /** * a String array of authenticator specific features that added account must support * may be null */ public final static String EXTRA_REQUIRED_FEATURES = "codetail.intent.extra.REQUIRED_FEATURES"; AccountManager mAccountManager; Bundle mExtras; public BasicAccountAuthenticator(Context context) { super(context); mExtras = new Bundle(); mAccountManager = get(context); } @Override @DebugLog public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { mExtras.clear(); mExtras.putParcelable(KEY_INTENT, new Intent(ACTION_EDIT_ACCOUNT_PROPERTIES)); return mExtras; } @Override @DebugLog public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { mExtras.clear(); Intent intent = new Intent(ACTION_AUTHENTICATE) .putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) .putExtra(KEY_ACCOUNT_TYPE, accountType) .putExtra(EXTRA_AUTH_TOKEN_TYPE, authTokenType) .putExtra(EXTRA_REQUIRED_FEATURES, requiredFeatures) .putExtras(options); mExtras.putParcelable(KEY_INTENT, intent); return mExtras; } @Override @DebugLog public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { Intent intent = new Intent(ACTION_CONFIRM_CREDENTIALS) .putExtra(EXTRA_ACCOUNT, account) .putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) .putExtras(options); mExtras.clear(); mExtras.putParcelable(KEY_INTENT, intent); return mExtras; } @Override @DebugLog public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { mExtras.clear(); Source source = getSource(authTokenType); if(source == null){ mExtras.putInt(KEY_ERROR_CODE, INVALID_AUTH_TOKEN_TYPE); return mExtras; } AccountManager manager = mAccountManager; String authToken = mAccountManager.peekAuthToken(account, authTokenType); if(!source.isValidAuthToken(authToken)){ Bundle bundle = source.getAuthenticator().getAuthToken(manager, account); authToken = bundle.getString(KEY_AUTHTOKEN); if(bundle.containsKey(USER_PICTURE_URL)){ manager.setUserData(account, USER_PICTURE_URL, bundle.getString(USER_PICTURE_URL)); } } if(TextUtils.isEmpty(authToken)){ Intent intent = new Intent(ACTION_AUTHENTICATE) .putExtra(EXTRA_ACCOUNT, account) .putExtra(EXTRA_AUTH_TOKEN_TYPE, authTokenType) .putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) .putExtras(options); mExtras.putParcelable(KEY_INTENT, intent); }else{ mExtras.putString(KEY_AUTHTOKEN, authToken); mExtras.putString(KEY_ACCOUNT_NAME, account.name); mExtras.putString(KEY_ACCOUNT_TYPE, account.type); mExtras.putString(EXTRA_AUTH_TOKEN_TYPE, authTokenType); } return mExtras; } @Override @DebugLog public String getAuthTokenLabel(String authTokenType) { return getSource(authTokenType).getLabel(); } @Override @DebugLog public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { return null; } @Override @DebugLog public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { return null; } Source getSource(String accountType){ List<Source> sources = WatchMeApplication.getApplication().getSources(); for(Source source: sources){ if(source.getSourceId().equals(accountType)){ return source; } } return null; } }