1 /* 2 * Copyright (C) 2019 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.content.Context; 22 import android.content.pm.PackageManager; 23 import android.graphics.drawable.Drawable; 24 import android.os.UserHandle; 25 26 import androidx.annotation.VisibleForTesting; 27 28 import com.android.settingslib.accounts.AuthenticatorHelper; 29 30 import java.util.Collections; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Objects; 34 import java.util.Set; 35 36 /** 37 * Utility that maintains a set of authorized account types. 38 */ 39 public class AccountTypesHelper { 40 /** Callback invoked when the set of authorized account types changes. */ 41 public interface OnChangeListener { 42 /** Called when the set of authorized account types changes. */ onAuthorizedAccountTypesChanged()43 void onAuthorizedAccountTypesChanged(); 44 } 45 46 private final Context mContext; 47 private UserHandle mUserHandle; 48 private AuthenticatorHelper mAuthenticatorHelper; 49 private List<String> mAuthorities; 50 private Set<String> mAccountTypesFilter; 51 private Set<String> mAccountTypesExclusionFilter; 52 private Set<String> mAuthorizedAccountTypes; 53 private OnChangeListener mListener; 54 AccountTypesHelper(Context context)55 public AccountTypesHelper(Context context) { 56 mContext = context; 57 58 // Default to hardcoded Bluetooth account type. 59 mAccountTypesExclusionFilter = new HashSet<>(); 60 mAccountTypesExclusionFilter.add("com.android.bluetooth.pbapsink"); 61 setAccountTypesExclusionFilter(mAccountTypesExclusionFilter); 62 63 mUserHandle = UserHandle.of(UserHandle.myUserId()); 64 mAuthenticatorHelper = new AuthenticatorHelper(mContext, mUserHandle, 65 userHandle -> { 66 // Only force a refresh if accounts have changed for the current user. 67 if (userHandle.equals(mUserHandle)) { 68 updateAuthorizedAccountTypes(false /* isForced */); 69 } 70 }); 71 } 72 73 /** Sets the authorities that the user has. */ setAuthorities(List<String> authorities)74 public void setAuthorities(List<String> authorities) { 75 mAuthorities = authorities; 76 } 77 78 /** Sets the filter for accounts that should be shown. */ setAccountTypesFilter(Set<String> accountTypesFilter)79 public void setAccountTypesFilter(Set<String> accountTypesFilter) { 80 mAccountTypesFilter = accountTypesFilter; 81 } 82 83 /** Sets the filter for accounts that should NOT be shown. */ setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilter)84 protected void setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilter) { 85 mAccountTypesExclusionFilter = accountTypesExclusionFilter; 86 } 87 88 /** Sets the callback invoked when the set of authorized account types changes. */ setOnChangeListener(OnChangeListener listener)89 public void setOnChangeListener(OnChangeListener listener) { 90 mListener = listener; 91 } 92 93 /** 94 * Updates the set of authorized account types. 95 * 96 * <p>Derived from 97 * {@link com.android.settings.accounts.ChooseAccountActivity#onAuthDescriptionsUpdated} 98 */ 99 @VisibleForTesting updateAuthorizedAccountTypes(boolean isForced)100 void updateAuthorizedAccountTypes(boolean isForced) { 101 AccountManager accountManager = AccountManager.get(mContext); 102 AuthenticatorDescription[] authenticatorDescriptions = 103 accountManager.getAuthenticatorTypesAsUser(mUserHandle.getIdentifier()); 104 105 Set<String> authorizedAccountTypes = new HashSet<>(); 106 for (AuthenticatorDescription authenticatorDescription : authenticatorDescriptions) { 107 String accountType = authenticatorDescription.type; 108 109 List<String> accountAuthorities = 110 mAuthenticatorHelper.getAuthoritiesForAccountType(accountType); 111 112 // If there are specific authorities required, we need to check whether they are 113 // included in the account type. 114 boolean authorized = 115 (mAuthorities == null || mAuthorities.isEmpty() || accountAuthorities == null 116 || !Collections.disjoint(accountAuthorities, mAuthorities)); 117 118 // If there is an account type filter, make sure this account type is included. 119 authorized = authorized && (mAccountTypesFilter == null 120 || mAccountTypesFilter.contains(accountType)); 121 122 // Check if the account type is in the exclusion list. 123 authorized = authorized && (mAccountTypesExclusionFilter == null 124 || !mAccountTypesExclusionFilter.contains(accountType)); 125 126 if (authorized) { 127 authorizedAccountTypes.add(accountType); 128 } 129 } 130 131 if (isForced || !Objects.equals(mAuthorizedAccountTypes, authorizedAccountTypes)) { 132 mAuthorizedAccountTypes = authorizedAccountTypes; 133 if (mListener != null) { 134 mListener.onAuthorizedAccountTypesChanged(); 135 } 136 } 137 } 138 139 /** Returns the set of authorized account types, initializing the set first if necessary. */ getAuthorizedAccountTypes()140 public Set<String> getAuthorizedAccountTypes() { 141 if (mAuthorizedAccountTypes == null) { 142 updateAuthorizedAccountTypes(false /* isForced */); 143 } 144 return mAuthorizedAccountTypes; 145 } 146 147 /** Forces an update of the authorized account types. */ forceUpdate()148 public void forceUpdate() { 149 updateAuthorizedAccountTypes(true /* isForced */); 150 } 151 152 /** Starts listening for account updates. */ listenToAccountUpdates()153 public void listenToAccountUpdates() { 154 mAuthenticatorHelper.listenToAccountUpdates(); 155 } 156 157 /** Stops listening for account updates. */ stopListeningToAccountUpdates()158 public void stopListeningToAccountUpdates() { 159 mAuthenticatorHelper.stopListeningToAccountUpdates(); 160 } 161 162 /** 163 * Gets the label associated with a particular account type. Returns {@code null} if none found. 164 * 165 * @param accountType the type of account 166 */ getLabelForType(String accountType)167 public CharSequence getLabelForType(String accountType) { 168 return mAuthenticatorHelper.getLabelForType(mContext, accountType); 169 } 170 171 /** 172 * Gets an icon associated with a particular account type. Returns a default icon if none found. 173 * 174 * @param accountType the type of account 175 * @return a drawable for the icon or a default icon returned by 176 * {@link PackageManager#getDefaultActivityIcon} if one cannot be found. 177 */ getDrawableForType(String accountType)178 public Drawable getDrawableForType(String accountType) { 179 return mAuthenticatorHelper.getDrawableForType(mContext, accountType); 180 } 181 182 /** Used for testing to trigger an account update. */ 183 @VisibleForTesting getAuthenticatorHelper()184 AuthenticatorHelper getAuthenticatorHelper() { 185 return mAuthenticatorHelper; 186 } 187 188 @VisibleForTesting setAuthenticatorHelper(AuthenticatorHelper helper)189 void setAuthenticatorHelper(AuthenticatorHelper helper) { 190 mAuthenticatorHelper = helper; 191 } 192 } 193