• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.settings.accounts;
18 
19 import static android.app.Activity.RESULT_CANCELED;
20 import static android.app.Activity.RESULT_OK;
21 import static android.content.Intent.EXTRA_USER;
22 
23 import android.accounts.AccountManager;
24 import android.accounts.AuthenticatorDescription;
25 import android.app.Activity;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.SyncAdapterType;
30 import android.content.pm.PackageManager;
31 import android.content.res.Resources;
32 import android.graphics.drawable.Drawable;
33 import android.os.UserHandle;
34 import android.util.Log;
35 
36 import androidx.annotation.VisibleForTesting;
37 import androidx.preference.Preference;
38 import androidx.preference.PreferenceScreen;
39 
40 import com.android.settings.core.BasePreferenceController;
41 import com.android.settingslib.RestrictedLockUtils;
42 import com.android.settingslib.RestrictedLockUtilsInternal;
43 
44 import com.google.android.collect.Maps;
45 
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53 
54 /**
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  */
58 public 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 
ChooseAccountPreferenceController(Context context, String preferenceKey)74     public ChooseAccountPreferenceController(Context context, String preferenceKey) {
75         super(context, preferenceKey);
76 
77         mProviderList = new ArrayList<>();
78         mTypeToAuthDescription = new HashMap<>();
79     }
80 
initialize(String[] authorities, String[] accountTypesFilter, UserHandle userHandle, Activity activity)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
getAvailabilityStatus()96     public int getAvailabilityStatus() {
97         return AVAILABLE;
98     }
99 
100     @Override
displayPreference(PreferenceScreen screen)101     public void displayPreference(PreferenceScreen screen) {
102         super.displayPreference(screen);
103         mScreen = screen;
104         updateAuthDescriptions();
105     }
106 
107     @Override
handlePreferenceTreeClick(Preference preference)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      */
updateAuthDescriptions()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 
onAuthDescriptionsUpdated()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 =
171                     RestrictedLockUtilsInternal.checkIfAccountManagementDisabled(
172                             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 
getAuthoritiesForAccountType(String type)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
getDrawableForType(final String accountType)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
getLabelForType(final String accountType)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 
finishWithAccountType(String accountType)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 }
289