blob: a217f017f99c0f4aec8b2823f0d5242e2b6eea41 [file] [log] [blame]
Emily Chuangb279b1c2018-04-12 10:12:28 +08001/*
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.settings.accounts;
18
19import static android.app.Activity.RESULT_CANCELED;
20import static android.app.Activity.RESULT_OK;
21import static android.content.Intent.EXTRA_USER;
22
23import android.accounts.AccountManager;
24import android.accounts.AuthenticatorDescription;
25import android.app.Activity;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.SyncAdapterType;
30import android.content.pm.PackageManager;
31import android.content.res.Resources;
32import android.graphics.drawable.Drawable;
33import android.os.UserHandle;
34import android.util.Log;
35
Fan Zhang23f8d592018-08-28 15:11:40 -070036import androidx.annotation.VisibleForTesting;
37import androidx.preference.Preference;
38import androidx.preference.PreferenceScreen;
39
Emily Chuangb279b1c2018-04-12 10:12:28 +080040import com.android.settings.core.BasePreferenceController;
41import com.android.settingslib.RestrictedLockUtils;
Philip P. Moltmanne3f72112018-08-28 15:01:43 -070042import com.android.settingslib.RestrictedLockUtilsInternal;
Emily Chuangb279b1c2018-04-12 10:12:28 +080043
44import com.google.android.collect.Maps;
45
46import java.util.ArrayList;
47import java.util.Collections;
48import java.util.HashMap;
49import java.util.HashSet;
50import java.util.List;
51import java.util.Map;
52import java.util.Set;
53
Emily Chuangb279b1c2018-04-12 10:12:28 +080054/**
55 * An extra {@link UserHandle} can be specified in the intent as {@link EXTRA_USER}, if the user for
56 * which the action needs to be performed is different to the one the Settings App will run in.
57 */
58public class ChooseAccountPreferenceController extends BasePreferenceController {
59
60 private static final String TAG = "ChooseAccountPrefCtrler";
61
62 private final List<ProviderEntry> mProviderList;
63 private final Map<String, AuthenticatorDescription> mTypeToAuthDescription;
64
65 private String[] mAuthorities;
66 private Set<String> mAccountTypesFilter;
67 private AuthenticatorDescription[] mAuthDescs;
68 private Map<String, List<String>> mAccountTypeToAuthorities;
69 // The UserHandle of the user we are choosing an account for
70 private UserHandle mUserHandle;
71 private Activity mActivity;
72 private PreferenceScreen mScreen;
73
74 public ChooseAccountPreferenceController(Context context, String preferenceKey) {
75 super(context, preferenceKey);
76
77 mProviderList = new ArrayList<>();
78 mTypeToAuthDescription = new HashMap<>();
79 }
80
81 public void initialize(String[] authorities, String[] accountTypesFilter, UserHandle userHandle,
82 Activity activity) {
83 mActivity = activity;
84 mAuthorities = authorities;
85 mUserHandle = userHandle;
86
87 if (accountTypesFilter != null) {
88 mAccountTypesFilter = new HashSet<>();
89 for (String accountType : accountTypesFilter) {
90 mAccountTypesFilter.add(accountType);
91 }
92 }
93 }
94
95 @Override
96 public int getAvailabilityStatus() {
97 return AVAILABLE;
98 }
99
100 @Override
101 public void displayPreference(PreferenceScreen screen) {
102 super.displayPreference(screen);
103 mScreen = screen;
104 updateAuthDescriptions();
105 }
106
107 @Override
108 public boolean handlePreferenceTreeClick(Preference preference) {
109 if (!(preference instanceof ProviderPreference)) {
110 return false;
111 }
112
113 ProviderPreference pref = (ProviderPreference) preference;
114 if (Log.isLoggable(TAG, Log.VERBOSE)) {
115 Log.v(TAG, "Attempting to add account of type " + pref.getAccountType());
116 }
117 finishWithAccountType(pref.getAccountType());
118 return true;
119 }
120
121 /**
122 * Updates provider icons. Subclasses should call this in onCreate()
123 * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
124 */
125 private void updateAuthDescriptions() {
126 mAuthDescs = AccountManager.get(mContext).getAuthenticatorTypesAsUser(
127 mUserHandle.getIdentifier());
128 for (int i = 0; i < mAuthDescs.length; i++) {
129 mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
130 }
131 onAuthDescriptionsUpdated();
132 }
133
134 private void onAuthDescriptionsUpdated() {
135 // Create list of providers to show on preference screen
136 for (int i = 0; i < mAuthDescs.length; i++) {
137 final String accountType = mAuthDescs[i].type;
138 final CharSequence providerName = getLabelForType(accountType);
139
140 // Skip preferences for authorities not specified. If no authorities specified,
141 // then include them all.
142 final List<String> accountAuths = getAuthoritiesForAccountType(accountType);
143 boolean addAccountPref = true;
144 if (mAuthorities != null && mAuthorities.length > 0 && accountAuths != null) {
145 addAccountPref = false;
146 for (int k = 0; k < mAuthorities.length; k++) {
147 if (accountAuths.contains(mAuthorities[k])) {
148 addAccountPref = true;
149 break;
150 }
151 }
152 }
153 if (addAccountPref && mAccountTypesFilter != null
154 && !mAccountTypesFilter.contains(accountType)) {
155 addAccountPref = false;
156 }
157 if (addAccountPref) {
158 mProviderList.add(
159 new ProviderEntry(providerName, accountType));
160 } else {
161 if (Log.isLoggable(TAG, Log.VERBOSE)) {
162 Log.v(TAG, "Skipped pref " + providerName + ": has no authority we need");
163 }
164 }
165 }
166 final Context context = mScreen.getContext();
167 if (mProviderList.size() == 1) {
168 // There's only one provider that matches. If it is disabled by admin show the
169 // support dialog otherwise run it.
170 final RestrictedLockUtils.EnforcedAdmin admin =
Philip P. Moltmanne3f72112018-08-28 15:01:43 -0700171 RestrictedLockUtilsInternal.checkIfAccountManagementDisabled(
Emily Chuangb279b1c2018-04-12 10:12:28 +0800172 context, mProviderList.get(0).getType(), mUserHandle.getIdentifier());
173 if (admin != null) {
174 mActivity.setResult(RESULT_CANCELED,
175 RestrictedLockUtils.getShowAdminSupportDetailsIntent(
176 context, admin));
177 mActivity.finish();
178 } else {
179 finishWithAccountType(mProviderList.get(0).getType());
180 }
181 } else if (mProviderList.size() > 0) {
182 Collections.sort(mProviderList);
183 for (ProviderEntry pref : mProviderList) {
184 final Drawable drawable = getDrawableForType(pref.getType());
185 final ProviderPreference p = new ProviderPreference(context,
186 pref.getType(), drawable, pref.getName());
187 p.setKey(pref.getType().toString());
188 p.checkAccountManagementAndSetDisabled(mUserHandle.getIdentifier());
189 mScreen.addPreference(p);
190 }
191 } else {
192 if (Log.isLoggable(TAG, Log.VERBOSE)) {
193 final StringBuilder auths = new StringBuilder();
194 for (String a : mAuthorities) {
195 auths.append(a);
196 auths.append(' ');
197 }
198 Log.v(TAG, "No providers found for authorities: " + auths);
199 }
200 mActivity.setResult(RESULT_CANCELED);
201 mActivity.finish();
202 }
203 }
204
205 private List<String> getAuthoritiesForAccountType(String type) {
206 if (mAccountTypeToAuthorities == null) {
207 mAccountTypeToAuthorities = Maps.newHashMap();
208 final SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
209 mUserHandle.getIdentifier());
210 for (int i = 0, n = syncAdapters.length; i < n; i++) {
211 final SyncAdapterType sa = syncAdapters[i];
212 List<String> authorities = mAccountTypeToAuthorities.get(sa.accountType);
213 if (authorities == null) {
214 authorities = new ArrayList<>();
215 mAccountTypeToAuthorities.put(sa.accountType, authorities);
216 }
217 if (Log.isLoggable(TAG, Log.VERBOSE)) {
218 Log.v(TAG, "added authority " + sa.authority + " to accountType "
219 + sa.accountType);
220 }
221 authorities.add(sa.authority);
222 }
223 }
224 return mAccountTypeToAuthorities.get(type);
225 }
226
227 /**
228 * Gets an icon associated with a particular account type. If none found, return null.
229 *
230 * @param accountType the type of account
231 * @return a drawable for the icon or a default icon returned by
232 * {@link PackageManager#getDefaultActivityIcon} if one cannot be found.
233 */
234 @VisibleForTesting
235 Drawable getDrawableForType(final String accountType) {
236 Drawable icon = null;
237 if (mTypeToAuthDescription.containsKey(accountType)) {
238 try {
239 final AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
240 final Context authContext = mActivity
241 .createPackageContextAsUser(desc.packageName, 0, mUserHandle);
242 icon = mContext.getPackageManager().getUserBadgedIcon(
243 authContext.getDrawable(desc.iconId), mUserHandle);
244 } catch (PackageManager.NameNotFoundException e) {
245 Log.w(TAG, "No icon name for account type " + accountType);
246 } catch (Resources.NotFoundException e) {
247 Log.w(TAG, "No icon resource for account type " + accountType);
248 }
249 }
250 if (icon != null) {
251 return icon;
252 } else {
253 return mContext.getPackageManager().getDefaultActivityIcon();
254 }
255 }
256
257 /**
258 * Gets the label associated with a particular account type. If none found, return null.
259 *
260 * @param accountType the type of account
261 * @return a CharSequence for the label or null if one cannot be found.
262 */
263 @VisibleForTesting
264 CharSequence getLabelForType(final String accountType) {
265 CharSequence label = null;
266 if (mTypeToAuthDescription.containsKey(accountType)) {
267 try {
268 final AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
269 final Context authContext = mActivity
270 .createPackageContextAsUser(desc.packageName, 0, mUserHandle);
271 label = authContext.getResources().getText(desc.labelId);
272 } catch (PackageManager.NameNotFoundException e) {
273 Log.w(TAG, "No label name for account type " + accountType);
274 } catch (Resources.NotFoundException e) {
275 Log.w(TAG, "No label resource for account type " + accountType);
276 }
277 }
278 return label;
279 }
280
281 private void finishWithAccountType(String accountType) {
282 Intent intent = new Intent();
283 intent.putExtra(AddAccountSettings.EXTRA_SELECTED_ACCOUNT, accountType);
284 intent.putExtra(EXTRA_USER, mUserHandle);
285 mActivity.setResult(RESULT_OK, intent);
286 mActivity.finish();
287 }
288}