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 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