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