/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.apps.iosched.util;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import com.google.android.apps.iosched.gcm.ServerUtilities;
import com.google.android.apps.iosched.provider.ScheduleContract;
import com.google.android.apps.iosched.ui.AccountActivity;
import com.google.android.gms.auth.*;
import com.google.android.gms.common.Scopes;
import java.io.IOException;
import static com.google.android.apps.iosched.util.LogUtils.*;
public class AccountUtils {
private static final String TAG = makeLogTag(AccountUtils.class);
private static final String PREF_CHOSEN_ACCOUNT = "chosen_account";
private static final String PREF_AUTH_TOKEN = "auth_token";
private static final String PREF_PLUS_PROFILE_ID = "plus_profile_id";
public static final String AUTH_SCOPES[] = {
Scopes.PLUS_LOGIN,
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/developerssite" };
static final String AUTH_TOKEN_TYPE;
static {
StringBuilder sb = new StringBuilder();
sb.append("oauth2:");
for (String scope : AUTH_SCOPES) {
sb.append(scope);
sb.append(" ");
}
AUTH_TOKEN_TYPE = sb.toString();
}
public static boolean isAuthenticated(final Context context) {
return !TextUtils.isEmpty(getChosenAccountName(context));
}
public static String getChosenAccountName(final Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getString(PREF_CHOSEN_ACCOUNT, null);
}
public static Account getChosenAccount(final Context context) {
String account = getChosenAccountName(context);
if (account != null) {
return new Account(account, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
} else {
return null;
}
}
static void setChosenAccountName(final Context context, final String accountName) {
LOGD(TAG, "Chose account " + accountName);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putString(PREF_CHOSEN_ACCOUNT, accountName).commit();
}
public static String getAuthToken(final Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getString(PREF_AUTH_TOKEN, null);
}
private static void setAuthToken(final Context context, final String authToken) {
LOGI(TAG, "Auth token of length "
+ (TextUtils.isEmpty(authToken) ? 0 : authToken.length()));
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putString(PREF_AUTH_TOKEN, authToken).commit();
LOGD(TAG, "Auth Token: " + authToken);
}
static void invalidateAuthToken(final Context context) {
GoogleAuthUtil.invalidateToken(context, getAuthToken(context));
setAuthToken(context, null);
}
public static void setPlusProfileId(final Context context, final String profileId) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().putString(PREF_PLUS_PROFILE_ID, profileId).commit();
}
public static String getPlusProfileId(final Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getString(PREF_PLUS_PROFILE_ID, null);
}
public static void refreshAuthToken(Context mContext) {
invalidateAuthToken(mContext);
tryAuthenticateWithErrorNotification(mContext, ScheduleContract.CONTENT_AUTHORITY,
getChosenAccountName(mContext));
}
public static interface AuthenticateCallback {
public boolean shouldCancelAuthentication();
public void onAuthTokenAvailable();
public void onRecoverableException(final int code);
public void onUnRecoverableException(final String errorMessage);
}
static void tryAuthenticateWithErrorNotification(Context context, String syncAuthority, String accountName) {
try {
LOGI(TAG, "Requesting new auth token (with notification)");
final String token = GoogleAuthUtil.getTokenWithNotification(context, accountName, AUTH_TOKEN_TYPE,
null, syncAuthority, null);
setAuthToken(context, token);
setChosenAccountName(context, accountName);
} catch (UserRecoverableNotifiedException e) {
// Notification has already been pushed.
LOGW(TAG, "User recoverable exception. Check notification.", e);
} catch (GoogleAuthException e) {
// This is likely unrecoverable.
LOGE(TAG, "Unrecoverable authentication exception: " + e.getMessage(), e);
} catch (IOException e) {
LOGE(TAG, "transient error encountered: " + e.getMessage());
}
}
public static void tryAuthenticate(final Activity activity, final AuthenticateCallback callback,
final String accountName, final int requestCode) {
(new GetTokenTask(activity, callback, accountName, requestCode)).execute();
}
private static class GetTokenTask extends AsyncTask <Void, Void, String> {
private String mAccountName;
private Activity mActivity;
private AuthenticateCallback mCallback;
private int mRequestCode;
public GetTokenTask(Activity activity, AuthenticateCallback callback, String name, int requestCode) {
mAccountName = name;
mActivity = activity;
mCallback = callback;
mRequestCode = requestCode;
}
@Override
protected String doInBackground(Void... params) {
try {
if (mCallback.shouldCancelAuthentication()) return null;
final String token = GoogleAuthUtil.getToken(mActivity, mAccountName, AUTH_TOKEN_TYPE);
// Persists auth token.
setAuthToken(mActivity, token);
setChosenAccountName(mActivity, mAccountName);
return token;
} catch (GooglePlayServicesAvailabilityException e) {
mCallback.onRecoverableException(e.getConnectionStatusCode());
} catch (UserRecoverableAuthException e) {
mActivity.startActivityForResult(e.getIntent(), mRequestCode);
} catch (IOException e) {
LOGE(TAG, "transient error encountered: " + e.getMessage());
mCallback.onUnRecoverableException(e.getMessage());
} catch (GoogleAuthException e) {
LOGE(TAG, "transient error encountered: " + e.getMessage());
mCallback.onUnRecoverableException(e.getMessage());
} catch (RuntimeException e) {
LOGE(TAG, "Error encountered: " + e.getMessage());
mCallback.onUnRecoverableException(e.getMessage());
}
return null;
}
@Override
protected void onPostExecute(String token) {
super.onPostExecute(token);
mCallback.onAuthTokenAvailable();
}
}
public static void signOut(final Context context) {
// Sign out of GCM message router
ServerUtilities.onSignOut(context);
// Destroy auth tokens
invalidateAuthToken(context);
// Remove remaining application state
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
sp.edit().clear().commit();
context.getContentResolver().delete(ScheduleContract.BASE_CONTENT_URI, null, null);
}
public static void startAuthenticationFlow(final Context context, final Intent finishIntent) {
Intent loginFlowIntent = new Intent(context, AccountActivity.class);
loginFlowIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
loginFlowIntent.putExtra(AccountActivity.EXTRA_FINISH_INTENT, finishIntent);
context.startActivity(loginFlowIntent);
}
}