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