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 17 package com.android.settings.accounts; 18 19 import android.accounts.AccountManager; 20 import android.accounts.AuthenticatorDescription; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.SyncAdapterType; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.graphics.drawable.Drawable; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.preference.Preference; 32 import android.preference.PreferenceActivity; 33 import android.preference.PreferenceGroup; 34 import android.preference.PreferenceScreen; 35 import android.util.Log; 36 37 import com.android.internal.util.CharSequences; 38 import com.android.settings.R; 39 import com.android.settings.Utils; 40 41 import com.google.android.collect.Maps; 42 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.Map; 48 49 import static android.content.Intent.EXTRA_USER; 50 51 /** 52 * Activity asking a user to select an account to be set up. 53 * 54 * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for 55 * which the action needs to be performed is different to the one the Settings App will run in. 56 */ 57 public class ChooseAccountActivity extends PreferenceActivity { 58 59 private static final String TAG = "ChooseAccountActivity"; 60 private String[] mAuthorities; 61 private PreferenceGroup mAddAccountGroup; 62 private final ArrayList<ProviderEntry> mProviderList = new ArrayList<ProviderEntry>(); 63 public HashSet<String> mAccountTypesFilter; 64 private AuthenticatorDescription[] mAuthDescs; 65 private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null; 66 private Map<String, AuthenticatorDescription> mTypeToAuthDescription 67 = new HashMap<String, AuthenticatorDescription>(); 68 // The UserHandle of the user we are choosing an account for 69 private UserHandle mUserHandle; 70 private UserManager mUm; 71 72 private static class ProviderEntry implements Comparable<ProviderEntry> { 73 private final CharSequence name; 74 private final String type; 75 ProviderEntry(CharSequence providerName, String accountType) { 76 name = providerName; 77 type = accountType; 78 } 79 80 public int compareTo(ProviderEntry another) { 81 if (name == null) { 82 return -1; 83 } 84 if (another.name == null) { 85 return +1; 86 } 87 return CharSequences.compareToIgnoreCase(name, another.name); 88 } 89 } 90 91 @Override 92 protected void onCreate(Bundle icicle) { 93 super.onCreate(icicle); 94 95 setContentView(R.layout.add_account_screen); 96 addPreferencesFromResource(R.xml.add_account_settings); 97 mAuthorities = getIntent().getStringArrayExtra( 98 AccountPreferenceBase.AUTHORITIES_FILTER_KEY); 99 String[] accountTypesFilter = getIntent().getStringArrayExtra( 100 AccountPreferenceBase.ACCOUNT_TYPES_FILTER_KEY); 101 if (accountTypesFilter != null) { 102 mAccountTypesFilter = new HashSet<String>(); 103 for (String accountType : accountTypesFilter) { 104 mAccountTypesFilter.add(accountType); 105 } 106 } 107 mAddAccountGroup = getPreferenceScreen(); 108 mUm = UserManager.get(this); 109 mUserHandle = Utils.getSecureTargetUser(getActivityToken(), mUm, null /* arguments */, 110 getIntent().getExtras()); 111 updateAuthDescriptions(); 112 } 113 114 /** 115 * Updates provider icons. Subclasses should call this in onCreate() 116 * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated(). 117 */ 118 private void updateAuthDescriptions() { 119 mAuthDescs = AccountManager.get(this).getAuthenticatorTypesAsUser( 120 mUserHandle.getIdentifier()); 121 for (int i = 0; i < mAuthDescs.length; i++) { 122 mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); 123 } 124 onAuthDescriptionsUpdated(); 125 } 126 127 private void onAuthDescriptionsUpdated() { 128 // Create list of providers to show on preference screen 129 for (int i = 0; i < mAuthDescs.length; i++) { 130 String accountType = mAuthDescs[i].type; 131 CharSequence providerName = getLabelForType(accountType); 132 133 // Skip preferences for authorities not specified. If no authorities specified, 134 // then include them all. 135 ArrayList<String> accountAuths = getAuthoritiesForAccountType(accountType); 136 boolean addAccountPref = true; 137 if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) { 138 addAccountPref = false; 139 for (int k = 0; k < mAuthorities.length; k++) { 140 if (accountAuths.contains(mAuthorities[k])) { 141 addAccountPref = true; 142 break; 143 } 144 } 145 } 146 if (addAccountPref && mAccountTypesFilter != null 147 && !mAccountTypesFilter.contains(accountType)) { 148 addAccountPref = false; 149 } 150 if (addAccountPref) { 151 mProviderList.add(new ProviderEntry(providerName, accountType)); 152 } else { 153 if (Log.isLoggable(TAG, Log.VERBOSE)) { 154 Log.v(TAG, "Skipped pref " + providerName + ": has no authority we need"); 155 } 156 } 157 } 158 159 if (mProviderList.size() == 1) { 160 // If there's only one provider that matches, just run it. 161 finishWithAccountType(mProviderList.get(0).type); 162 } else if (mProviderList.size() > 0) { 163 Collections.sort(mProviderList); 164 mAddAccountGroup.removeAll(); 165 for (ProviderEntry pref : mProviderList) { 166 Drawable drawable = getDrawableForType(pref.type); 167 ProviderPreference p = 168 new ProviderPreference(this, pref.type, drawable, pref.name); 169 mAddAccountGroup.addPreference(p); 170 } 171 } else { 172 if (Log.isLoggable(TAG, Log.VERBOSE)) { 173 final StringBuilder auths = new StringBuilder(); 174 for (String a : mAuthorities) { 175 auths.append(a); 176 auths.append(' '); 177 } 178 Log.v(TAG, "No providers found for authorities: " + auths); 179 } 180 setResult(RESULT_CANCELED); 181 finish(); 182 } 183 } 184 185 public ArrayList<String> getAuthoritiesForAccountType(String type) { 186 if (mAccountTypeToAuthorities == null) { 187 mAccountTypeToAuthorities = Maps.newHashMap(); 188 SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser( 189 mUserHandle.getIdentifier()); 190 for (int i = 0, n = syncAdapters.length; i < n; i++) { 191 final SyncAdapterType sa = syncAdapters[i]; 192 ArrayList<String> authorities = mAccountTypeToAuthorities.get(sa.accountType); 193 if (authorities == null) { 194 authorities = new ArrayList<String>(); 195 mAccountTypeToAuthorities.put(sa.accountType, authorities); 196 } 197 if (Log.isLoggable(TAG, Log.VERBOSE)) { 198 Log.d(TAG, "added authority " + sa.authority + " to accountType " 199 + sa.accountType); 200 } 201 authorities.add(sa.authority); 202 } 203 } 204 return mAccountTypeToAuthorities.get(type); 205 } 206 207 /** 208 * Gets an icon associated with a particular account type. If none found, return null. 209 * @param accountType the type of account 210 * @return a drawable for the icon or null if one cannot be found. 211 */ 212 protected Drawable getDrawableForType(final String accountType) { 213 Drawable icon = null; 214 if (mTypeToAuthDescription.containsKey(accountType)) { 215 try { 216 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); 217 Context authContext = createPackageContextAsUser(desc.packageName, 0, mUserHandle); 218 icon = getPackageManager().getUserBadgedIcon( 219 authContext.getResources().getDrawable(desc.iconId), mUserHandle); 220 } catch (PackageManager.NameNotFoundException e) { 221 // TODO: place holder icon for missing account icons? 222 Log.w(TAG, "No icon name for account type " + accountType); 223 } catch (Resources.NotFoundException e) { 224 // TODO: place holder icon for missing account icons? 225 Log.w(TAG, "No icon resource for account type " + accountType); 226 } 227 } 228 return icon; 229 } 230 231 /** 232 * Gets the label associated with a particular account type. If none found, return null. 233 * @param accountType the type of account 234 * @return a CharSequence for the label or null if one cannot be found. 235 */ 236 protected CharSequence getLabelForType(final String accountType) { 237 CharSequence label = null; 238 if (mTypeToAuthDescription.containsKey(accountType)) { 239 try { 240 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); 241 Context authContext = createPackageContextAsUser(desc.packageName, 0, mUserHandle); 242 label = authContext.getResources().getText(desc.labelId); 243 } catch (PackageManager.NameNotFoundException e) { 244 Log.w(TAG, "No label name for account type " + accountType); 245 } catch (Resources.NotFoundException e) { 246 Log.w(TAG, "No label resource for account type " + accountType); 247 } 248 } 249 return label; 250 } 251 252 @Override 253 public boolean onPreferenceTreeClick(PreferenceScreen preferences, Preference preference) { 254 if (preference instanceof ProviderPreference) { 255 ProviderPreference pref = (ProviderPreference) preference; 256 if (Log.isLoggable(TAG, Log.VERBOSE)) { 257 Log.v(TAG, "Attempting to add account of type " + pref.getAccountType()); 258 } 259 finishWithAccountType(pref.getAccountType()); 260 } 261 return true; 262 } 263 264 private void finishWithAccountType(String accountType) { 265 Intent intent = new Intent(); 266 intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType); 267 intent.putExtra(EXTRA_USER, mUserHandle); 268 setResult(RESULT_OK, intent); 269 finish(); 270 } 271 } 272