/* * Copyright 2014 Google Inc. All rights reserved. * * 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.samples.apps.iosched.util; import android.accounts.Account; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.text.TextUtils; import com.google.samples.apps.iosched.provider.ScheduleContract; import com.google.android.gms.auth.*; import com.google.android.gms.common.Scopes; import java.io.IOException; import java.util.UUID; import static com.google.samples.apps.iosched.util.LogUtils.*; /** * Account and login utilities. This class manages a local shared preferences object * that stores which account is currently active, and can store associated information * such as Google+ profile info (name, image URL, cover URL) and also the auth token * associated with the account. */ public class AccountUtils { private static final String TAG = makeLogTag(AccountUtils.class); private static final String PREF_ACTIVE_ACCOUNT = "chosen_account"; // these names are are prefixes; the account is appended to them private static final String PREFIX_PREF_AUTH_TOKEN = "auth_token_"; private static final String PREFIX_PREF_PLUS_PROFILE_ID = "plus_profile_id_"; private static final String PREFIX_PREF_PLUS_NAME = "plus_name_"; private static final String PREFIX_PREF_PLUS_IMAGE_URL = "plus_image_url_"; private static final String PREFIX_PREF_PLUS_COVER_URL = "plus_cover_url_"; private static final String PREFIX_PREF_GCM_KEY = "gcm_key_"; public static final String AUTH_SCOPES[] = { Scopes.PLUS_LOGIN, Scopes.DRIVE_APPFOLDER, "https://www.googleapis.com/auth/userinfo.email"}; 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(); } private static SharedPreferences getSharedPreferences(final Context context) { return PreferenceManager.getDefaultSharedPreferences(context); } public static boolean hasActiveAccount(final Context context) { return !TextUtils.isEmpty(getActiveAccountName(context)); } public static String getActiveAccountName(final Context context) { SharedPreferences sp = getSharedPreferences(context); return sp.getString(PREF_ACTIVE_ACCOUNT, null); } public static Account getActiveAccount(final Context context) { String account = getActiveAccountName(context); if (account != null) { return new Account(account, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE); } else { return null; } } public static boolean setActiveAccount(final Context context, final String accountName) { LOGD(TAG, "Set active account to: " + accountName); SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(PREF_ACTIVE_ACCOUNT, accountName).commit(); return true; } private static String makeAccountSpecificPrefKey(Context ctx, String prefix) { return hasActiveAccount(ctx) ? makeAccountSpecificPrefKey(getActiveAccountName(ctx), prefix) : null; } private static String makeAccountSpecificPrefKey(String accountName, String prefix) { return prefix + accountName; } public static String getAuthToken(final Context context) { SharedPreferences sp = getSharedPreferences(context); return hasActiveAccount(context) ? sp.getString(makeAccountSpecificPrefKey(context, PREFIX_PREF_AUTH_TOKEN), null) : null; } public static void setAuthToken(final Context context, final String accountName, final String authToken) { LOGI(TAG, "Auth token of length " + (TextUtils.isEmpty(authToken) ? 0 : authToken.length()) + " for " + accountName); SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_AUTH_TOKEN), authToken).commit(); LOGV(TAG, "Auth Token: " + authToken); } public static void setAuthToken(final Context context, final String authToken) { if (hasActiveAccount(context)) { setAuthToken(context, getActiveAccountName(context), authToken); } else { LOGE(TAG, "Can't set auth token because there is no chosen account!"); } } static void invalidateAuthToken(final Context context) { GoogleAuthUtil.invalidateToken(context, getAuthToken(context)); setAuthToken(context, null); } public static void setPlusProfileId(final Context context, final String accountName, final String profileId) { SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_PLUS_PROFILE_ID), profileId).commit(); } public static String getPlusProfileId(final Context context) { SharedPreferences sp = getSharedPreferences(context); return hasActiveAccount(context) ? sp.getString(makeAccountSpecificPrefKey(context, PREFIX_PREF_PLUS_PROFILE_ID), null) : null; } public static boolean hasPlusInfo(final Context context, final String accountName) { SharedPreferences sp = getSharedPreferences(context); return !TextUtils.isEmpty(sp.getString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_PLUS_PROFILE_ID), null)); } public static boolean hasToken(final Context context, final String accountName) { SharedPreferences sp = getSharedPreferences(context); return !TextUtils.isEmpty(sp.getString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_AUTH_TOKEN), null)); } public static void setPlusName(final Context context, final String accountName, final String name) { SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_PLUS_NAME), name).commit(); } public static String getPlusName(final Context context) { SharedPreferences sp = getSharedPreferences(context); return hasActiveAccount(context) ? sp.getString(makeAccountSpecificPrefKey(context, PREFIX_PREF_PLUS_NAME), null) : null; } public static void setPlusImageUrl(final Context context, final String accountName, final String imageUrl) { SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_PLUS_IMAGE_URL), imageUrl).commit(); } public static String getPlusImageUrl(final Context context) { SharedPreferences sp = getSharedPreferences(context); return hasActiveAccount(context) ? sp.getString(makeAccountSpecificPrefKey(context, PREFIX_PREF_PLUS_IMAGE_URL), null) : null; } public static void refreshAuthToken(Context mContext) { invalidateAuthToken(mContext); tryAuthenticateWithErrorNotification(mContext, ScheduleContract.CONTENT_AUTHORITY); } public static void setPlusCoverUrl(final Context context, final String accountName, String coverPhotoUrl) { SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_PLUS_COVER_URL), coverPhotoUrl).commit(); } public static String getPlusCoverUrl(final Context context) { SharedPreferences sp = getSharedPreferences(context); return hasActiveAccount(context) ? sp.getString(makeAccountSpecificPrefKey(context, PREFIX_PREF_PLUS_COVER_URL), null) : null; } static void tryAuthenticateWithErrorNotification(Context context, String syncAuthority) { try { String accountName = getActiveAccountName(context); if (accountName != null) { LOGI(TAG, "Requesting new auth token (with notification)"); final String token = GoogleAuthUtil.getTokenWithNotification(context, accountName, AUTH_TOKEN_TYPE, null, syncAuthority, null); setAuthToken(context, token); } else { LOGE(TAG, "Can't try authentication because no account is chosen."); } } 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 setGcmKey(final Context context, final String accountName, final String gcmKey) { SharedPreferences sp = getSharedPreferences(context); sp.edit().putString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_GCM_KEY), gcmKey).commit(); LOGD(TAG, "GCM key of account " + accountName + " set to: " + sanitizeGcmKey(gcmKey)); } public static String getGcmKey(final Context context, final String accountName) { SharedPreferences sp = getSharedPreferences(context); String gcmKey = sp.getString(makeAccountSpecificPrefKey(accountName, PREFIX_PREF_GCM_KEY), null); // if there is no current GCM key, generate a new random one if (TextUtils.isEmpty(gcmKey)) { gcmKey = UUID.randomUUID().toString(); LOGD(TAG, "No GCM key on account " + accountName + ". Generating random one: " + sanitizeGcmKey(gcmKey)); setGcmKey(context, accountName, gcmKey); } return gcmKey; } public static String sanitizeGcmKey(String key) { if (key == null) { return "(null)"; } else if (key.length() > 8) { return key.substring(0, 4) + "........" + key.substring(key.length() - 4); } else { return "........"; } } }