• 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.tv.settings.accounts;
18 
19 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
20 
21 import android.accounts.Account;
22 import android.accounts.AccountManager;
23 import android.accounts.AuthenticatorDescription;
24 import android.app.tvsettings.TvSettingsEnums;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.res.Resources;
30 import android.graphics.drawable.Drawable;
31 import android.os.Bundle;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.text.TextUtils;
35 import android.util.ArraySet;
36 import android.util.Log;
37 
38 import androidx.annotation.Keep;
39 import androidx.preference.Preference;
40 import androidx.preference.PreferenceScreen;
41 
42 import com.android.settingslib.accounts.AuthenticatorHelper;
43 import com.android.tv.settings.R;
44 import com.android.tv.settings.SettingsPreferenceFragment;
45 import com.android.tv.settings.library.overlay.FlavorUtils;
46 import com.android.tv.settings.system.SecurityFragment;
47 
48 import java.util.ArrayList;
49 import java.util.Set;
50 
51 /**
52  * The "Accounts and Sign in" screen in TV settings.
53  */
54 @Keep
55 public class AccountsFragment extends SettingsPreferenceFragment {
56     private static final String TAG = "AccountsFragment";
57     private static final String KEY_ADD_ACCOUNT = "add_account";
58     private static final String KEY_DEVICE_OWNER_FOOTER = "do_org_footer";
59     private static final int ORDER_ADD_ACCOUNT = Integer.MAX_VALUE - 2;
60     private static final int ORDER_FOOTER = Integer.MAX_VALUE - 1;
61     private AuthenticatorHelper mAuthenticatorHelper;
62 
63     @Override
onCreate(Bundle savedInstanceState)64     public void onCreate(Bundle savedInstanceState) {
65         mAuthenticatorHelper = new AuthenticatorHelper(getContext(),
66                 new UserHandle(UserHandle.myUserId()), userHandle -> updateAccounts());
67         super.onCreate(savedInstanceState);
68     }
69 
70     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)71     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
72         setPreferencesFromResource(R.xml.accounts, null);
73     }
74 
75     @Override
onStart()76     public void onStart() {
77         super.onStart();
78         mAuthenticatorHelper.listenToAccountUpdates();
79     }
80 
81     @Override
onStop()82     public void onStop() {
83         super.onStop();
84         mAuthenticatorHelper.stopListeningToAccountUpdates();
85     }
86 
87     @Override
onResume()88     public void onResume() {
89         super.onResume();
90         updateAccounts();
91     }
92 
updateAccounts()93     private void updateAccounts() {
94         PreferenceScreen prefScreen = getPreferenceScreen();
95         final Set<String> touchedAccounts = new ArraySet<>(prefScreen.getPreferenceCount());
96 
97         final AccountManager am = AccountManager.get(getContext());
98         final AuthenticatorDescription[] authTypes = am.getAuthenticatorTypes();
99         final ArrayList<String> allowableAccountTypes = new ArrayList<>(authTypes.length);
100         final Context themedContext = getPreferenceManager().getContext();
101 
102         for (AuthenticatorDescription authDesc : authTypes) {
103             Context targetContext = getTargetContext(getContext(), authDesc);
104             if (targetContext == null) {
105                 continue;
106             }
107 
108             String authTitle = getAuthTitle(targetContext, authDesc);
109 
110 
111             Account[] accounts = am.getAccountsByType(authDesc.type);
112             if (accounts == null || accounts.length == 0) {
113                 continue;  // No point in continuing; there aren't any accounts to show.
114             }
115 
116             Drawable authImage = getAuthImage(targetContext, authDesc);
117 
118             // Display an entry for each installed account we have.
119             for (final Account account : accounts) {
120                 final String key = "account_pref:" + account.type + ":" + account.name;
121                 Preference preference = findPreference(key);
122                 if (preference == null) {
123                     preference = new Preference(themedContext);
124                 }
125                 preference.setTitle(authTitle != null ? authTitle : account.name);
126                 preference.setIcon(authImage);
127                 preference.setSummary(authTitle != null ? account.name : null);
128                 preference.setFragment(AccountSyncFragment.class.getName());
129                 AccountSyncFragment.prepareArgs(preference.getExtras(), account);
130 
131                 touchedAccounts.add(key);
132                 preference.setKey(key);
133 
134                 prefScreen.addPreference(preference);
135             }
136         }
137 
138         for (int i = 0; i < prefScreen.getPreferenceCount();) {
139             final Preference preference = prefScreen.getPreference(i);
140             final String key = preference.getKey();
141             if (touchedAccounts.contains(key) || TextUtils.equals(KEY_ADD_ACCOUNT, key)
142                     || TextUtils.equals(KEY_DEVICE_OWNER_FOOTER, key)) {
143                 i++;
144             } else {
145                 prefScreen.removePreference(preference);
146             }
147         }
148 
149         // Never allow restricted profile to add accounts.
150         final Preference addAccountPref = findPreference(KEY_ADD_ACCOUNT);
151         if (addAccountPref != null) {
152             addAccountPref.setOrder(ORDER_ADD_ACCOUNT);
153             if (!AccountsUtil.isAdminRestricted(getContext())) {
154                 if (isRestricted()) {
155                     addAccountPref.setVisible(false);
156                 } else {
157                     setUpAddAccountPrefIntent(addAccountPref, getContext());
158                 }
159             }
160         }
161 
162         // Show device managed footer information if DO active
163         final Preference footerPref = findPreference(KEY_DEVICE_OWNER_FOOTER);
164         if (footerPref != null) {
165             final CharSequence deviceOwnerDisclosure = FlavorUtils.getFeatureFactory(
166                     getContext()).getEnterprisePrivacyFeatureProvider(
167                     getContext()).getDeviceOwnerDisclosure();
168             footerPref.setTitle(deviceOwnerDisclosure);
169             footerPref.setOrder(ORDER_FOOTER);
170             footerPref.setVisible(deviceOwnerDisclosure != null);
171         }
172     }
173 
isRestricted()174     private boolean isRestricted() {
175         return SecurityFragment.isRestrictedProfileInEffect(getContext()) || UserManager.get(
176                 getContext()).hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS);
177     }
178 
179     /**
180      * Set up the intent and visibility for the given preference based on the information from
181      * AccountManager.
182      */
setUpAddAccountPrefIntent(Preference preference, Context context)183     public static void setUpAddAccountPrefIntent(Preference preference, Context context) {
184         final AccountManager am = AccountManager.get(context);
185         final AuthenticatorDescription[] authTypes = am.getAuthenticatorTypes();
186         final ArrayList<String> allowableAccountTypes = new ArrayList<>(authTypes.length);
187         for (AuthenticatorDescription authDesc : authTypes) {
188             final Context targetContext = getTargetContext(context, authDesc);
189             if (targetContext == null) {
190                 continue;
191             }
192             String authTitle = getAuthTitle(targetContext, authDesc);
193             if (authTitle != null || authDesc.iconId != 0) {
194                 allowableAccountTypes.add(authDesc.type);
195             }
196         }
197 
198         Intent i = new Intent().setComponent(new ComponentName("com.android.tv.settings",
199                 "com.android.tv.settings.accounts.AddAccountWithTypeActivity"));
200         i.putExtra(AddAccountWithTypeActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
201                 allowableAccountTypes.toArray(new String[allowableAccountTypes.size()]));
202 
203                 // If there are available account types, show the "add account" button.
204         preference.setVisible(!allowableAccountTypes.isEmpty());
205         preference.setIntent(i);
206         preference.setOnPreferenceClickListener(
207                 preference1 -> {
208                     logEntrySelected(TvSettingsEnums.ACCOUNT_CLASSIC_ADD_ACCOUNT);
209                     return false;
210                 });
211     }
212 
getTargetContext(Context context, AuthenticatorDescription authDesc)213     private static Context getTargetContext(Context context, AuthenticatorDescription authDesc) {
214         Context targetContext = null;
215         try {
216             targetContext = context.createPackageContext(authDesc.packageName, 0);
217         } catch (PackageManager.NameNotFoundException e) {
218             Log.e(TAG, "Authenticator description with bad package name", e);
219         } catch (SecurityException e) {
220             Log.e(TAG, "Security exception loading package resources", e);
221         }
222         return targetContext;
223     }
224 
getAuthTitle(Context targetContext, AuthenticatorDescription authDesc)225     private static String getAuthTitle(Context targetContext, AuthenticatorDescription authDesc) {
226         // Main title text comes from the authenticator description (e.g. "Google").
227         String authTitle = null;
228         try {
229             authTitle = targetContext.getString(authDesc.labelId);
230             if (TextUtils.isEmpty(authTitle)) {
231                 authTitle = null;  // Handled later when we add the row.
232             }
233         } catch (Resources.NotFoundException e) {
234             Log.e(TAG, "Authenticator description with bad label id", e);
235         }
236         return authTitle;
237     }
238 
getAuthImage(Context targetContext, AuthenticatorDescription authDesc)239     private static Drawable getAuthImage(Context targetContext, AuthenticatorDescription authDesc) {
240         // Icon URI to be displayed for each account is based on the type of authenticator.
241         Drawable authImage = null;
242         try {
243             authImage = targetContext.getDrawable(authDesc.iconId);
244         } catch (Resources.NotFoundException e) {
245             Log.e(TAG, "Authenticator has bad resources", e);
246         }
247         return authImage;
248     }
249 
250 }
251