1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.accounts; 18 19import android.accounts.AccountManager; 20import android.accounts.AuthenticatorDescription; 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.SyncAdapterType; 25import android.content.pm.PackageManager; 26import android.content.res.Resources; 27import android.graphics.drawable.Drawable; 28import android.os.Bundle; 29import android.preference.Preference; 30import android.preference.PreferenceActivity; 31import android.preference.PreferenceGroup; 32import android.preference.PreferenceScreen; 33import android.util.Log; 34import com.android.internal.util.CharSequences; 35import com.android.settings.R; 36import com.google.android.collect.Maps; 37 38import java.util.ArrayList; 39import java.util.Collections; 40import java.util.HashMap; 41import java.util.HashSet; 42import java.util.Map; 43 44/** 45 * Activity asking a user to select an account to be set up. 46 */ 47public class ChooseAccountActivity extends PreferenceActivity { 48 49 private static final String TAG = "ChooseAccountActivity"; 50 private String[] mAuthorities; 51 private PreferenceGroup mAddAccountGroup; 52 private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>(); 53 public HashSet<String> mAccountTypesFilter; 54 private AuthenticatorDescription[] mAuthDescs; 55 private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null; 56 private Map<String, AuthenticatorDescription> mTypeToAuthDescription 57 = new HashMap<String, AuthenticatorDescription>(); 58 59 private static class ProviderEntry implements Comparable<ProviderEntry> { 60 private final CharSequence name; 61 private final String type; 62 ProviderEntry(CharSequence providerName, String accountType) { 63 name = providerName; 64 type = accountType; 65 } 66 67 public int compareTo(ProviderEntry another) { 68 if (name == null) { 69 return -1; 70 } 71 if (another.name == null) { 72 return +1; 73 } 74 return CharSequences.compareToIgnoreCase(name, another.name); 75 } 76 } 77 78 @Override 79 protected void onCreate(Bundle icicle) { 80 super.onCreate(icicle); 81 82 setContentView(R.layout.add_account_screen); 83 addPreferencesFromResource(R.xml.add_account_settings); 84 mAuthorities = getIntent().getStringArrayExtra( 85 AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 86 String[] accountTypesFilter = getIntent().getStringArrayExtra( 87 AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 88 if (accountTypesFilter != null) { 89 mAccountTypesFilter = new HashSet<String>(); 90 for (String accountType : accountTypesFilter) { 91 mAccountTypesFilter.add(accountType); 92 } 93 } 94 mAddAccountGroup = getPreferenceScreen(); 95 updateAuthDescriptions(); 96 } 97 98 /** 99 * Updates provider icons. Subclasses should call this in onCreate() 100 * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated(). 101 */ 102 private void updateAuthDescriptions() { 103 mAuthDescs = AccountManager.get(this).getAuthenticatorTypes(); 104 for (int i = 0; i < mAuthDescs.length; i++) { 105 mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); 106 } 107 onAuthDescriptionsUpdated(); 108 } 109 110 private void onAuthDescriptionsUpdated() { 111 // Create list of providers to show on preference screen 112 for (int i = 0; i < mAuthDescs.length; i++) { 113 String accountType = mAuthDescs[i].type; 114 CharSequence providerName = getLabelForType(accountType); 115 116 // Skip preferences for authorities not specified. If no authorities specified, 117 // then include them all. 118 ArrayList<String> accountAuths = getAuthoritiesForAccountType(accountType); 119 boolean addAccountPref = true; 120 if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) { 121 addAccountPref = false; 122 for (int k = 0; k < mAuthorities.length; k++) { 123 if (accountAuths.contains(mAuthorities[k])) { 124 addAccountPref = true; 125 break; 126 } 127 } 128 } 129 if (addAccountPref && mAccountTypesFilter != null 130 && !mAccountTypesFilter.contains(accountType)) { 131 addAccountPref = false; 132 } 133 if (addAccountPref) { 134 mProviderList.add(new ProviderEntry(providerName, accountType)); 135 } else { 136 if (Log.isLoggable(TAG, Log.VERBOSE)) { 137 Log.v(TAG, "Skipped pref " + providerName + ": has no authority we need"); 138 } 139 } 140 } 141 142 if (mProviderList.size() == 1) { 143 // If there's only one provider that matches, just run it. 144 finishWithAccountType(mProviderList.get(0).type); 145 } else if (mProviderList.size() > 0) { 146 Collections.sort(mProviderList); 147 mAddAccountGroup.removeAll(); 148 for (ProviderEntry pref : mProviderList) { 149 Drawable drawable = getDrawableForType(pref.type); 150 ProviderPreference p = 151 new ProviderPreference(this, pref.type, drawable, pref.name); 152 mAddAccountGroup.addPreference(p); 153 } 154 } else { 155 if (Log.isLoggable(TAG, Log.VERBOSE)) { 156 final StringBuilder auths = new StringBuilder(); 157 for (String a : mAuthorities) { 158 auths.append(a); 159 auths.append(' '); 160 } 161 Log.v(TAG, "No providers found for authorities: " + auths); 162 } 163 setResult(RESULT_CANCELED); 164 finish(); 165 } 166 } 167 168 public ArrayList<String> getAuthoritiesForAccountType(String type) { 169 if (mAccountTypeToAuthorities == null) { 170 mAccountTypeToAuthorities = Maps.newHashMap(); 171 SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypes(); 172 for (int i = 0, n = syncAdapters.length; i < n; i++) { 173 final SyncAdapterType sa = syncAdapters[i]; 174 ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType); 175 if (authorities == null) { 176 authorities = new ArrayList<String>(); 177 mAccountTypeToAuthorities.put(sa.accountType, authorities); 178 } 179 if (Log.isLoggable(TAG, Log.VERBOSE)) { 180 Log.d(TAG, "added authority " + sa.authority + " to accountType " 181 + sa.accountType); 182 } 183 authorities.add(sa.authority); 184 } 185 } 186 return mAccountTypeToAuthorities.get(type); 187 } 188 189 /** 190 * Gets an icon associated with a particular account type. If none found, return null. 191 * @param accountType the type of account 192 * @return a drawable for the icon or null if one cannot be found. 193 */ 194 protected Drawable getDrawableForType(final String accountType) { 195 Drawable icon = null; 196 if (mTypeToAuthDescription.containsKey(accountType)) { 197 try { 198 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); 199 Context authContext = createPackageContext(desc.packageName, 0); 200 icon = authContext.getResources().getDrawable(desc.iconId); 201 } catch (PackageManager.NameNotFoundException e) { 202 // TODO: place holder icon for missing account icons? 203 Log.w(TAG, "No icon name for account type " + accountType); 204 } catch (Resources.NotFoundException e) { 205 // TODO: place holder icon for missing account icons? 206 Log.w(TAG, "No icon resource for account type " + accountType); 207 } 208 } 209 return icon; 210 } 211 212 /** 213 * Gets the label associated with a particular account type. If none found, return null. 214 * @param accountType the type of account 215 * @return a CharSequence for the label or null if one cannot be found. 216 */ 217 protected CharSequence getLabelForType(final String accountType) { 218 CharSequence label = null; 219 if (mTypeToAuthDescription.containsKey(accountType)) { 220 try { 221 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); 222 Context authContext = createPackageContext(desc.packageName, 0); 223 label = authContext.getResources().getText(desc.labelId); 224 } catch (PackageManager.NameNotFoundException e) { 225 Log.w(TAG, "No label name for account type " + accountType); 226 } catch (Resources.NotFoundException e) { 227 Log.w(TAG, "No label resource for account type " + accountType); 228 } 229 } 230 return label; 231 } 232 233 @Override 234 public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) { 235 if (preference instanceof ProviderPreference) { 236 ProviderPreference pref = (ProviderPreference) preference; 237 if (Log.isLoggable(TAG, Log.VERBOSE)) { 238 Log.v(TAG, "Attempting to add account of type " + pref.getAccountType()); 239 } 240 finishWithAccountType(pref.getAccountType()); 241 } 242 return true; 243 } 244 245 private void finishWithAccountType(String accountType) { 246 Intent intent = new Intent(); 247 intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType); 248 setResult(RESULT_OK, intent); 249 finish(); 250 } 251} 252