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