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