blob: 0f98700b703f13c38cba0d56ff28b93d0a661150 [file] [log] [blame]
Roshan Agrawal16bf44c2018-10-23 17:17:03 -07001/*
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
17package com.android.car.settings.accounts;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
Roshan Agrawala33c6122018-12-11 17:56:16 -080021import android.car.drivingstate.CarUxRestrictions;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070022import android.car.userlib.CarUserManagerHelper;
23import android.content.Context;
24import android.content.pm.UserInfo;
25import android.graphics.drawable.Drawable;
26import android.os.UserHandle;
27
28import androidx.collection.ArrayMap;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070029import androidx.preference.Preference;
30import androidx.preference.PreferenceCategory;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070031
32import com.android.car.settings.R;
33import com.android.car.settings.common.FragmentController;
Roshan Agrawala33c6122018-12-11 17:56:16 -080034import com.android.car.settings.common.PreferenceController;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070035import com.android.settingslib.accounts.AuthenticatorHelper;
36
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.Collections;
40import java.util.Comparator;
Roshan Agrawala33c6122018-12-11 17:56:16 -080041import java.util.HashSet;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070042import java.util.List;
Roshan Agrawala33c6122018-12-11 17:56:16 -080043import java.util.Set;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070044
45/**
46 * Controller for listing accounts.
47 *
48 * <p>Largely derived from {@link com.android.settings.accounts.AccountPreferenceController}
49 */
Roshan Agrawala33c6122018-12-11 17:56:16 -080050public class AccountListPreferenceController extends
51 PreferenceController<PreferenceCategory> implements
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070052 AuthenticatorHelper.OnAccountsUpdateListener,
Roshan Agrawala33c6122018-12-11 17:56:16 -080053 CarUserManagerHelper.OnUsersUpdateListener {
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070054 private static final String NO_ACCOUNT_PREF_KEY = "no_accounts_added";
55
56 private final UserInfo mUserInfo;
57 private final CarUserManagerHelper mCarUserManagerHelper;
58 private final ArrayMap<String, Preference> mPreferences = new ArrayMap<>();
59 private AuthenticatorHelper mAuthenticatorHelper;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070060 private String[] mAuthorities;
61
62 public AccountListPreferenceController(Context context, String preferenceKey,
Roshan Agrawala33c6122018-12-11 17:56:16 -080063 FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
64 super(context, preferenceKey, fragmentController, uxRestrictions);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070065 mCarUserManagerHelper = new CarUserManagerHelper(context);
66 mUserInfo = mCarUserManagerHelper.getCurrentProcessUserInfo();
67 mAuthenticatorHelper = new AuthenticatorHelper(context,
68 mUserInfo.getUserHandle(), /* listener= */ this);
69 }
70
71 /** Sets the account authorities that are available. */
72 public void setAuthorities(String[] authorities) {
73 mAuthorities = authorities;
74 }
75
76 @Override
Roshan Agrawala33c6122018-12-11 17:56:16 -080077 protected Class<PreferenceCategory> getPreferenceType() {
78 return PreferenceCategory.class;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070079 }
80
81 @Override
Roshan Agrawala33c6122018-12-11 17:56:16 -080082 protected void updateState(PreferenceCategory preference) {
83 forceUpdateAccountsCategory();
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070084 }
85
86 @Override
Roshan Agrawala33c6122018-12-11 17:56:16 -080087 protected int getAvailabilityStatus() {
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070088 return mCarUserManagerHelper.canCurrentProcessModifyAccounts() ? AVAILABLE
89 : DISABLED_FOR_USER;
90 }
91
92 /**
93 * Registers the account update and user update callbacks.
94 */
Roshan Agrawala33c6122018-12-11 17:56:16 -080095 @Override
96 protected void onStartInternal() {
Roshan Agrawal16bf44c2018-10-23 17:17:03 -070097 mAuthenticatorHelper.listenToAccountUpdates();
98 mCarUserManagerHelper.registerOnUsersUpdateListener(this);
99 }
100
101 /**
102 * Unregisters the account update and user update callbacks.
103 */
Roshan Agrawala33c6122018-12-11 17:56:16 -0800104 @Override
105 protected void onStopInternal() {
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700106 mAuthenticatorHelper.stopListeningToAccountUpdates();
107 mCarUserManagerHelper.unregisterOnUsersUpdateListener(this);
108 }
109
110 @Override
111 public void onAccountsUpdate(UserHandle userHandle) {
112 if (userHandle.equals(mUserInfo.getUserHandle())) {
113 forceUpdateAccountsCategory();
114 }
115 }
116
117 @Override
118 public void onUsersUpdate() {
119 forceUpdateAccountsCategory();
120 }
121
Roshan Agrawala33c6122018-12-11 17:56:16 -0800122 private boolean onAccountPreferenceClicked(AccountPreference preference) {
123 // Show the account's details when an account is clicked on.
124 getFragmentController().launchFragment(AccountDetailsFragment.newInstance(
125 preference.getAccount(), preference.getLabel(), mUserInfo));
126 return true;
127 }
128
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700129 /** Forces a refresh of the account preferences. */
130 private void forceUpdateAccountsCategory() {
131 // Set the category title and include the user's name
Roshan Agrawala33c6122018-12-11 17:56:16 -0800132 getPreference().setTitle(
133 getContext().getString(R.string.account_list_title, mUserInfo.name));
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700134
135 // Recreate the authentication helper to refresh the list of enabled accounts
Roshan Agrawala33c6122018-12-11 17:56:16 -0800136 mAuthenticatorHelper = new AuthenticatorHelper(getContext(), mUserInfo.getUserHandle(),
137 this);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700138
Roshan Agrawala33c6122018-12-11 17:56:16 -0800139 Set<String> preferencesToRemove = new HashSet<>(mPreferences.keySet());
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700140 List<? extends Preference> preferences = getAccountPreferences(preferencesToRemove);
141 // Add all preferences that aren't already shown. Manually set the order so that existing
142 // preferences are reordered correctly.
143 for (int i = 0; i < preferences.size(); i++) {
Roshan Agrawala33c6122018-12-11 17:56:16 -0800144 Preference pref = preferences.get(i);
145 pref.setOrder(i);
146 mPreferences.put(pref.getKey(), pref);
147 getPreference().addPreference(pref);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700148 }
149
Roshan Agrawala33c6122018-12-11 17:56:16 -0800150 for (String key : preferencesToRemove) {
151 getPreference().removePreference(mPreferences.get(key));
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700152 mPreferences.remove(key);
153 }
154 }
155
156 /**
157 * Returns a list of preferences corresponding to the accounts for the current user.
158 *
159 * <p> Derived from
160 * {@link com.android.settings.accounts.AccountPreferenceController#getAccountTypePreferences}
161 *
162 * @param preferencesToRemove the current preferences shown; only preferences to be removed will
163 * remain after method execution
164 */
165 private List<? extends Preference> getAccountPreferences(
Roshan Agrawala33c6122018-12-11 17:56:16 -0800166 Set<String> preferencesToRemove) {
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700167 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
168 ArrayList<AccountPreference> accountPreferences =
169 new ArrayList<>(accountTypes.length);
170
171 for (int i = 0; i < accountTypes.length; i++) {
172 String accountType = accountTypes[i];
173 // Skip showing any account that does not have any of the requested authorities
174 if (!accountTypeHasAnyRequestedAuthorities(accountType)) {
175 continue;
176 }
Roshan Agrawala33c6122018-12-11 17:56:16 -0800177 CharSequence label = mAuthenticatorHelper.getLabelForType(getContext(), accountType);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700178 if (label == null) {
179 continue;
180 }
181
Roshan Agrawala33c6122018-12-11 17:56:16 -0800182 Account[] accounts = AccountManager.get(getContext())
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700183 .getAccountsByTypeAsUser(accountType, mUserInfo.getUserHandle());
Roshan Agrawala33c6122018-12-11 17:56:16 -0800184 Drawable icon = mAuthenticatorHelper.getDrawableForType(getContext(), accountType);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700185
186 // Add a preference row for each individual account
187 for (Account account : accounts) {
Roshan Agrawala33c6122018-12-11 17:56:16 -0800188 String key = AccountPreference.buildKey(account);
189 AccountPreference preference = (AccountPreference) mPreferences.getOrDefault(key,
190 new AccountPreference(getContext(), account, label, icon));
191 preference.setOnPreferenceClickListener(
192 (Preference pref) -> onAccountPreferenceClicked((AccountPreference) pref));
193
194 accountPreferences.add(preference);
195 preferencesToRemove.remove(key);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700196 }
Roshan Agrawala33c6122018-12-11 17:56:16 -0800197 mAuthenticatorHelper.preloadDrawableForType(getContext(), accountType);
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700198 }
199
200 // If there are no accounts, return the "no account added" preference.
201 if (accountPreferences.isEmpty()) {
202 preferencesToRemove.remove(NO_ACCOUNT_PREF_KEY);
Roshan Agrawala33c6122018-12-11 17:56:16 -0800203 return Arrays.asList(mPreferences.getOrDefault(NO_ACCOUNT_PREF_KEY,
204 createNoAccountsAddedPreference()));
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700205 }
206
207 Collections.sort(accountPreferences, Comparator.comparing(
208 (AccountPreference a) -> a.getSummary().toString())
209 .thenComparing((AccountPreference a) -> a.getTitle().toString()));
210
211 return accountPreferences;
212 }
213
214 private Preference createNoAccountsAddedPreference() {
Roshan Agrawala33c6122018-12-11 17:56:16 -0800215 Preference emptyPreference = new Preference(getContext());
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700216 emptyPreference.setTitle(R.string.no_accounts_added);
217 emptyPreference.setKey(NO_ACCOUNT_PREF_KEY);
218 emptyPreference.setSelectable(false);
219
220 return emptyPreference;
221 }
222
223 /**
224 * Returns whether the account type has any of the authorities requested by the caller.
225 *
226 * <p> Derived from {@link AccountPreferenceController#accountTypeHasAnyRequestedAuthorities}
227 */
228 private boolean accountTypeHasAnyRequestedAuthorities(String accountType) {
229 if (mAuthorities == null || mAuthorities.length == 0) {
230 // No authorities required
231 return true;
232 }
233 ArrayList<String> authoritiesForType =
234 mAuthenticatorHelper.getAuthoritiesForAccountType(accountType);
235 if (authoritiesForType == null) {
236 return false;
237 }
238 for (int j = 0; j < mAuthorities.length; j++) {
239 if (authoritiesForType.contains(mAuthorities[j])) {
240 return true;
241 }
242 }
243 return false;
244 }
245
246 private static class AccountPreference extends Preference {
247 /** Account that this Preference represents. */
248 private final Account mAccount;
Roshan Agrawala2df3f12018-11-06 13:52:37 -0800249 private final CharSequence mLabel;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700250
251 private AccountPreference(Context context, Account account, CharSequence label,
252 Drawable icon) {
253 super(context);
254 mAccount = account;
Roshan Agrawala2df3f12018-11-06 13:52:37 -0800255 mLabel = label;
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700256
257 setKey(buildKey(account));
258 setTitle(account.name);
259 setSummary(label);
260 setIcon(icon);
261 }
262
263 /**
264 * Build a unique preference key based on the account.
265 */
266 public static String buildKey(Account account) {
267 return String.valueOf(account.hashCode());
268 }
269
270 public Account getAccount() {
271 return mAccount;
272 }
Roshan Agrawala2df3f12018-11-06 13:52:37 -0800273
274 public CharSequence getLabel() {
275 return mLabel;
276 }
Roshan Agrawal16bf44c2018-10-23 17:17:03 -0700277 }
278}