Home | History | Annotate | Download | only in accounts
      1 /*
      2  * Copyright (C) 2018 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.car.settings.accounts;
     18 
     19 import android.accounts.AccountManager;
     20 import android.accounts.AuthenticatorDescription;
     21 import android.app.ActivityManager;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.SyncAdapterType;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.UserInfo;
     28 import android.content.res.Resources;
     29 import android.graphics.drawable.Drawable;
     30 import android.os.UserHandle;
     31 import android.os.UserManager;
     32 
     33 import androidx.car.widget.ListItem;
     34 import androidx.car.widget.ListItemProvider;
     35 import androidx.car.widget.TextListItem;
     36 
     37 import com.android.car.settings.common.Logger;
     38 import com.android.internal.util.CharSequences;
     39 import com.android.settingslib.accounts.AuthenticatorHelper;
     40 
     41 import libcore.util.Nullable;
     42 
     43 import java.util.ArrayList;
     44 import java.util.Collections;
     45 import java.util.HashMap;
     46 import java.util.List;
     47 import java.util.Map;
     48 
     49 /**
     50  * Implementation of ListItemProvider for the ChooseAccountFragment.
     51  */
     52 class ChooseAccountItemProvider extends ListItemProvider {
     53     private static final Logger LOG = new Logger(ChooseAccountItemProvider.class);
     54     private static final String AUTHORITIES_FILTER_KEY = "authorities";
     55 
     56     private final List<ListItem> mItems = new ArrayList<>();
     57     private final Context mContext;
     58     private final ChooseAccountFragment mFragment;
     59     private final UserManager mUserManager;
     60     private final ArrayList<ProviderEntry> mProviderList = new ArrayList<>();
     61     @Nullable private AuthenticatorDescription[] mAuthDescs;
     62     @Nullable private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities;
     63     private Map<String, AuthenticatorDescription> mTypeToAuthDescription = new HashMap<>();
     64     private final AuthenticatorHelper mAuthenticatorHelper;
     65 
     66     // The UserHandle of the user we are choosing an account for
     67     private final UserHandle mUserHandle;
     68     private final String[] mAuthorities;
     69 
     70     ChooseAccountItemProvider(Context context, ChooseAccountFragment fragment) {
     71         mContext = context;
     72         mFragment = fragment;
     73         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
     74         mUserHandle =
     75                 mUserManager.getUserInfo(ActivityManager.getCurrentUser()).getUserHandle();
     76 
     77         mAuthorities = fragment.getActivity().getIntent().getStringArrayExtra(
     78                 AUTHORITIES_FILTER_KEY);
     79 
     80         // create auth helper
     81         UserInfo currUserInfo = mUserManager.getUserInfo(ActivityManager.getCurrentUser());
     82         mAuthenticatorHelper =
     83                 new AuthenticatorHelper(mContext, currUserInfo.getUserHandle(), mFragment);
     84 
     85         updateAuthDescriptions();
     86         refreshItems();
     87     }
     88 
     89     @Override
     90     public ListItem get(int position) {
     91         return mItems.get(position);
     92     }
     93 
     94     @Override
     95     public int size() {
     96         return mItems.size();
     97     }
     98 
     99     /**
    100      * Clears the existing item list and rebuilds it with new data.
    101      */
    102     public void refreshItems() {
    103         mItems.clear();
    104 
    105         for (int i = 0; i < mProviderList.size(); i++) {
    106             String accountType = mProviderList.get(i).type;
    107             Drawable icon = mAuthenticatorHelper.getDrawableForType(mContext, accountType);
    108 
    109             TextListItem item = new TextListItem(mContext);
    110             item.setPrimaryActionIcon(icon, /* useLargeIcon= */ false);
    111             item.setTitle(mProviderList.get(i).name.toString());
    112             item.setOnClickListener(v -> onItemSelected(accountType));
    113             mItems.add(item);
    114         }
    115     }
    116 
    117     // Starts a AddAccountActivity for the accountType that was clicked on.
    118     private void onItemSelected(String accountType) {
    119         Intent intent = new Intent(mContext, AddAccountActivity.class);
    120         intent.putExtra(AddAccountActivity.EXTRA_SELECTED_ACCOUNT, accountType);
    121         mContext.startActivity(intent);
    122     }
    123 
    124     /**
    125      * Updates provider icons.
    126      */
    127     private void updateAuthDescriptions() {
    128         mAuthDescs = AccountManager.get(mContext).getAuthenticatorTypesAsUser(
    129                 mUserHandle.getIdentifier());
    130         for (int i = 0; i < mAuthDescs.length; i++) {
    131             mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
    132         }
    133         onAuthDescriptionsUpdated();
    134     }
    135 
    136     private void onAuthDescriptionsUpdated() {
    137         // Create list of providers to show on page.
    138         for (int i = 0; i < mAuthDescs.length; i++) {
    139             String accountType = mAuthDescs[i].type;
    140             CharSequence providerName = getLabelForType(accountType);
    141 
    142             // Get the account authorities implemented by the account type.
    143             ArrayList<String> accountAuths = getAuthoritiesForAccountType(accountType);
    144 
    145             // If there are specific authorities required, we need to check whether it's
    146             // included in the account type.
    147             if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) {
    148                 for (int k = 0; k < mAuthorities.length; k++) {
    149                     if (accountAuths.contains(mAuthorities[k])) {
    150                         mProviderList.add(
    151                                 new ProviderEntry(providerName, accountType));
    152                         break;
    153                     }
    154                 }
    155             } else {
    156                 mProviderList.add(
    157                         new ProviderEntry(providerName, accountType));
    158             }
    159         }
    160         Collections.sort(mProviderList);
    161     }
    162 
    163     private ArrayList<String> getAuthoritiesForAccountType(String type) {
    164         if (mAccountTypeToAuthorities == null) {
    165             mAccountTypeToAuthorities = new HashMap<>();
    166             SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
    167                     mUserHandle.getIdentifier());
    168             for (int i = 0, n = syncAdapters.length; i < n; i++) {
    169                 final SyncAdapterType adapterType = syncAdapters[i];
    170                 ArrayList<String> authorities =
    171                         mAccountTypeToAuthorities.get(adapterType.accountType);
    172                 if (authorities == null) {
    173                     authorities = new ArrayList<String>();
    174                     mAccountTypeToAuthorities.put(adapterType.accountType, authorities);
    175                 }
    176                 LOG.v("added authority " + adapterType.authority + " to accountType "
    177                         + adapterType.accountType);
    178                 authorities.add(adapterType.authority);
    179             }
    180         }
    181         return mAccountTypeToAuthorities.get(type);
    182     }
    183 
    184     /**
    185      * Gets the label associated with a particular account type. If none found, return null.
    186      *
    187      * @param accountType the type of account
    188      * @return a CharSequence for the label or null if one cannot be found.
    189      */
    190     private CharSequence getLabelForType(final String accountType) {
    191         CharSequence label = null;
    192         if (mTypeToAuthDescription.containsKey(accountType)) {
    193             try {
    194                 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
    195                 Context authContext = mFragment.getActivity().createPackageContextAsUser(
    196                         desc.packageName, 0 /* flags */, mUserHandle);
    197                 label = authContext.getResources().getText(desc.labelId);
    198             } catch (PackageManager.NameNotFoundException e) {
    199                 LOG.w("No label name for account type " + accountType);
    200             } catch (Resources.NotFoundException e) {
    201                 LOG.w("No label resource for account type " + accountType);
    202             }
    203         }
    204         return label;
    205     }
    206 
    207     private static class ProviderEntry implements Comparable<ProviderEntry> {
    208         private final CharSequence name;
    209         private final String type;
    210         ProviderEntry(CharSequence providerName, String accountType) {
    211             name = providerName;
    212             type = accountType;
    213         }
    214 
    215         @Override
    216         public int compareTo(ProviderEntry another) {
    217             if (name == null) {
    218                 return -1;
    219             }
    220             if (another.name == null) {
    221                 return 1;
    222             }
    223             return CharSequences.compareToIgnoreCase(name, another.name);
    224         }
    225     }
    226 }
    227