Home | History | Annotate | Download | only in accounts
      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