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