1 /* 2 * Copyright (C) 2015 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; 18 19 import static com.android.tv.settings.accounts.AccountsUtil.ACCOUNTS_BASIC_MODE_FRAGMENT; 20 import static com.android.tv.settings.accounts.AccountsUtil.ACCOUNTS_FRAGMENT_DEFAULT; 21 import static com.android.tv.settings.accounts.AccountsUtil.ACCOUNTS_FRAGMENT_RESTRICTED; 22 import static com.android.tv.settings.accounts.AccountsUtil.ACCOUNTS_SLICE_FRAGMENT; 23 import static com.android.tv.settings.accounts.AccountsUtil.ACCOUNTS_SYSTEM_INTENT; 24 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 25 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_TWO_PANEL; 26 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_VENDOR; 27 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_X; 28 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 29 import static com.android.tv.settings.util.InstrumentationUtils.logPageFocused; 30 31 import android.accounts.Account; 32 import android.accounts.AccountManager; 33 import android.app.tvsettings.TvSettingsEnums; 34 import android.bluetooth.BluetoothAdapter; 35 import android.bluetooth.BluetoothDevice; 36 import android.content.BroadcastReceiver; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.IntentFilter; 40 import android.content.pm.ApplicationInfo; 41 import android.content.pm.PackageManager; 42 import android.content.pm.ProviderInfo; 43 import android.content.pm.ResolveInfo; 44 import android.content.res.Resources; 45 import android.graphics.drawable.Drawable; 46 import android.net.Uri; 47 import android.os.Bundle; 48 import android.service.settings.suggestions.Suggestion; 49 import android.telephony.CellSignalStrength; 50 import android.text.TextUtils; 51 import android.util.Log; 52 import android.view.LayoutInflater; 53 import android.view.View; 54 import android.view.ViewGroup; 55 56 import androidx.annotation.Keep; 57 import androidx.annotation.VisibleForTesting; 58 import androidx.preference.Preference; 59 import androidx.preference.PreferenceCategory; 60 61 import com.android.settingslib.core.AbstractPreferenceController; 62 import com.android.settingslib.suggestions.SuggestionControllerMixinCompat; 63 import com.android.tv.settings.HotwordSwitchController.HotwordStateListener; 64 import com.android.tv.settings.accounts.AccountsFragment; 65 import com.android.tv.settings.accounts.AccountsUtil; 66 import com.android.tv.settings.connectivity.ConnectivityListener; 67 import com.android.tv.settings.overlay.FlavorUtils; 68 import com.android.tv.settings.suggestions.SuggestionPreference; 69 import com.android.tv.settings.system.SecurityFragment; 70 import com.android.tv.settings.util.SliceUtils; 71 import com.android.tv.twopanelsettings.TwoPanelSettingsFragment; 72 import com.android.tv.twopanelsettings.slices.SlicePreference; 73 74 import java.util.List; 75 import java.util.Set; 76 77 /** 78 * The fragment where all good things begin. Evil is handled elsewhere. 79 */ 80 @Keep 81 public class MainFragment extends PreferenceControllerFragment implements 82 SuggestionControllerMixinCompat.SuggestionControllerHost, SuggestionPreference.Callback, 83 HotwordStateListener { 84 85 private static final String TAG = "MainFragment"; 86 private static final String KEY_BASIC_MODE_SUGGESTION = "basic_mode_suggestion"; 87 private static final String KEY_BASIC_MODE_EXIT = "basic_mode_exit"; 88 @VisibleForTesting 89 static final String KEY_ACCOUNTS_AND_SIGN_IN = "accounts_and_sign_in"; 90 @VisibleForTesting 91 static final String KEY_ACCOUNTS_AND_SIGN_IN_SLICE = "accounts_and_sign_in_slice"; 92 @VisibleForTesting 93 static final String KEY_ACCOUNTS_AND_SIGN_IN_BASIC_MODE = "accounts_and_sign_in_basic_mode"; 94 private static final String KEY_APPLICATIONS = "applications"; 95 @VisibleForTesting 96 static final String KEY_ACCESSORIES = "remotes_and_accessories"; 97 @VisibleForTesting 98 static final String KEY_CONNECTED_DEVICES = "connected_devices"; 99 private static final String KEY_CONNECTED_DEVICES_SLICE = "connected_devices_slice"; 100 @VisibleForTesting 101 static final String KEY_NETWORK = "network"; 102 @VisibleForTesting 103 static final String KEY_SOUND = "sound"; 104 public static final String ACTION_SOUND = "com.android.tv.settings.SOUND"; 105 @VisibleForTesting 106 static final String ACTION_CONNECTED_DEVICES = "com.android.tv.settings.CONNECTED_DEVICES"; 107 @VisibleForTesting 108 static final String KEY_PRIVACY = "privacy"; 109 @VisibleForTesting 110 static final String KEY_DISPLAY_AND_SOUND = "display_and_sound"; 111 private static final String KEY_CHANNELS_AND_INPUTS = "channels_and_inputs"; 112 113 private static final String ACTION_ACCOUNTS = "com.android.tv.settings.ACCOUNTS"; 114 @VisibleForTesting 115 ConnectivityListener mConnectivityListener; 116 @VisibleForTesting 117 BluetoothAdapter mBtAdapter; 118 @VisibleForTesting 119 boolean mHasBtAccessories; 120 @VisibleForTesting 121 boolean mHasAccounts; 122 123 private SuggestionQuickSettingPrefsContainer mSuggestionQuickSettingPrefsContainer; 124 125 private final BroadcastReceiver mBCMReceiver = new BroadcastReceiver() { 126 @Override 127 public void onReceive(Context context, Intent intent) { 128 updateAccessoryPref(); 129 } 130 }; 131 newInstance()132 public static MainFragment newInstance() { 133 return new MainFragment(); 134 } 135 136 @Override getPreferenceScreenResId()137 protected int getPreferenceScreenResId() { 138 switch (FlavorUtils.getFlavor(getContext())) { 139 case FLAVOR_CLASSIC: 140 case FLAVOR_TWO_PANEL: 141 return R.xml.main_prefs; 142 case FLAVOR_X: 143 return R.xml.main_prefs_x; 144 case FLAVOR_VENDOR: 145 return R.xml.main_prefs_vendor; 146 default: 147 return R.xml.main_prefs; 148 } 149 } 150 151 @Override onAttach(Context context)152 public void onAttach(Context context) { 153 mSuggestionQuickSettingPrefsContainer = new SuggestionQuickSettingPrefsContainer(this); 154 super.onAttach(context); 155 } 156 157 @Override onCreate(Bundle savedInstanceState)158 public void onCreate(Bundle savedInstanceState) { 159 mSuggestionQuickSettingPrefsContainer.onCreate(); 160 mConnectivityListener = new ConnectivityListener(getContext(), this::updateConnectivity, 161 getSettingsLifecycle()); 162 mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 163 super.onCreate(savedInstanceState); 164 // This is to record the initial start of Settings root in two panel settings case, as the 165 // MainFragment is the left-most pane and will not be slided in from preview pane. For 166 // classic settings case, the event will be recorded in onResume() as this is an instance 167 // of SettingsPreferenceFragment. 168 if (getCallbackFragment() instanceof TwoPanelSettingsFragment) { 169 logPageFocused(getPageId(), true); 170 } 171 } 172 173 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)174 public View onCreateView(LayoutInflater inflater, ViewGroup container, 175 Bundle savedInstanceState) { 176 mSuggestionQuickSettingPrefsContainer.showOrHideQuickSettings(); 177 updateAccountPref(); 178 updateAccessoryPref(); 179 updateConnectivity(); 180 updateBasicModeSuggestion(); 181 return super.onCreateView(inflater, container, savedInstanceState); 182 } 183 184 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)185 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 186 setPreferencesFromResource(getPreferenceScreenResId(), null); 187 if (isRestricted()) { 188 Preference appPref = findPreference(KEY_APPLICATIONS); 189 if (appPref != null) { 190 appPref.setVisible(false); 191 } 192 Preference accountsPref = findPreference(KEY_ACCOUNTS_AND_SIGN_IN); 193 if (accountsPref != null) { 194 accountsPref.setVisible(false); 195 } 196 } 197 if (!supportBluetooth()) { 198 Preference accessoryPreference = findPreference(KEY_ACCESSORIES); 199 if (accessoryPreference != null) { 200 accessoryPreference.setVisible(false); 201 } 202 } 203 if (FlavorUtils.isTwoPanel(getContext())) { 204 Preference displaySoundPref = findPreference(KEY_DISPLAY_AND_SOUND); 205 if (displaySoundPref != null) { 206 displaySoundPref.setVisible(true); 207 } 208 Preference privacyPref = findPreference(KEY_PRIVACY); 209 if (privacyPref != null) { 210 privacyPref.setVisible(true); 211 } 212 } 213 mSuggestionQuickSettingPrefsContainer.onCreatePreferences(); 214 updateSoundSettings(); 215 } 216 217 @VisibleForTesting updateConnectivity()218 void updateConnectivity() { 219 final Preference networkPref = findPreference(KEY_NETWORK); 220 if (networkPref == null) { 221 return; 222 } 223 224 if (mConnectivityListener.isCellConnected()) { 225 final int signal = mConnectivityListener.getCellSignalStrength(); 226 switch (signal) { 227 case CellSignalStrength.SIGNAL_STRENGTH_GREAT: 228 networkPref.setIcon(R.drawable.ic_cell_signal_4_white); 229 break; 230 case CellSignalStrength.SIGNAL_STRENGTH_GOOD: 231 networkPref.setIcon(R.drawable.ic_cell_signal_3_white); 232 break; 233 case CellSignalStrength.SIGNAL_STRENGTH_MODERATE: 234 networkPref.setIcon(R.drawable.ic_cell_signal_2_white); 235 break; 236 case CellSignalStrength.SIGNAL_STRENGTH_POOR: 237 networkPref.setIcon(R.drawable.ic_cell_signal_1_white); 238 break; 239 case CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN: 240 default: 241 networkPref.setIcon(R.drawable.ic_cell_signal_0_white); 242 break; 243 } 244 } else if (mConnectivityListener.isEthernetConnected()) { 245 networkPref.setIcon(R.drawable.ic_ethernet_white); 246 networkPref.setSummary(R.string.connectivity_summary_ethernet_connected); 247 } else if (mConnectivityListener.isWifiEnabledOrEnabling()) { 248 if (mConnectivityListener.isWifiConnected()) { 249 final int signal = mConnectivityListener.getWifiSignalStrength(5); 250 switch (signal) { 251 case 4: 252 networkPref.setIcon(R.drawable.ic_wifi_signal_4_white); 253 break; 254 case 3: 255 networkPref.setIcon(R.drawable.ic_wifi_signal_3_white); 256 break; 257 case 2: 258 networkPref.setIcon(R.drawable.ic_wifi_signal_2_white); 259 break; 260 case 1: 261 networkPref.setIcon(R.drawable.ic_wifi_signal_1_white); 262 break; 263 case 0: 264 default: 265 networkPref.setIcon(R.drawable.ic_wifi_signal_0_white); 266 break; 267 } 268 networkPref.setSummary(mConnectivityListener.getSsid()); 269 } else { 270 networkPref.setIcon(R.drawable.ic_wifi_not_connected); 271 networkPref.setSummary(R.string.connectivity_summary_no_network_connected); 272 } 273 } else { 274 networkPref.setIcon(R.drawable.ic_wifi_signal_off_white); 275 networkPref.setSummary(R.string.connectivity_summary_wifi_disabled); 276 } 277 } 278 279 @VisibleForTesting updateSoundSettings()280 void updateSoundSettings() { 281 final Preference soundPref = findPreference(KEY_SOUND); 282 if (soundPref != null) { 283 Intent soundIntent = new Intent(ACTION_SOUND); 284 final ResolveInfo info = systemIntentIsHandled(getContext(), soundIntent); 285 soundPref.setVisible(info != null); 286 if (info != null && info.activityInfo != null) { 287 String pkgName = info.activityInfo.packageName; 288 Drawable icon = getDrawableResource(pkgName, "sound_icon"); 289 if (icon != null) { 290 soundPref.setIcon(icon); 291 } 292 String title = getStringResource(pkgName, "sound_pref_title"); 293 if (!TextUtils.isEmpty(title)) { 294 soundPref.setTitle(title); 295 } 296 String summary = getStringResource(pkgName, "sound_pref_summary"); 297 if (!TextUtils.isEmpty(summary)) { 298 soundPref.setSummary(summary); 299 } 300 } 301 } 302 } 303 304 /** 305 * Extracts a string resource from a given package. 306 * 307 * @param pkgName the package name 308 * @param resource name, e.g. "my_string_name" 309 */ getStringResource(String pkgName, String resourceName)310 private String getStringResource(String pkgName, String resourceName) { 311 try { 312 Context targetContext = getContext().createPackageContext(pkgName, 0); 313 int resId = targetContext.getResources().getIdentifier( 314 pkgName + ":string/" + resourceName, null, null); 315 if (resId != 0) { 316 return targetContext.getResources().getString(resId); 317 } 318 } catch (Resources.NotFoundException | PackageManager.NameNotFoundException 319 | SecurityException e) { 320 Log.w(TAG, "Unable to get string resource " + resourceName, e); 321 } 322 return null; 323 } 324 325 /** 326 * Extracts an drawable resource from a given package. 327 * 328 * @param pkgName the package name 329 * @param resource name, e.g. "my_icon_name" 330 */ getDrawableResource(String pkgName, String resourceName)331 private Drawable getDrawableResource(String pkgName, String resourceName) { 332 try { 333 Context targetContext = getContext().createPackageContext(pkgName, 0); 334 int resId = targetContext.getResources().getIdentifier( 335 pkgName + ":drawable/" + resourceName, null, null); 336 if (resId != 0) { 337 return targetContext.getResources().getDrawable(resId); 338 } 339 } catch (Resources.NotFoundException | PackageManager.NameNotFoundException 340 | SecurityException e) { 341 Log.w(TAG, "Unable to get drawable resource " + resourceName, e); 342 } 343 return null; 344 } 345 346 /** 347 * Returns the ResolveInfo for the system activity that matches given intent filter or null if 348 * no such activity exists. 349 * 350 * @param context Context of the caller 351 * @param intent The intent matching the desired system app 352 * @return ResolveInfo of the matching activity or null if no match exists 353 */ systemIntentIsHandled(Context context, Intent intent)354 public static ResolveInfo systemIntentIsHandled(Context context, Intent intent) { 355 if (intent == null) { 356 return null; 357 } 358 359 final PackageManager pm = context.getPackageManager(); 360 for (ResolveInfo info : pm.queryIntentActivities(intent, 0)) { 361 if (info.activityInfo != null 362 && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 363 == ApplicationInfo.FLAG_SYSTEM) { 364 return info; 365 } 366 } 367 return null; 368 } 369 getProviderInfo(Context context, String authority)370 private static ProviderInfo getProviderInfo(Context context, String authority) { 371 return context.getPackageManager().resolveContentProvider(authority, 0); 372 } 373 isRestricted()374 private boolean isRestricted() { 375 return SecurityFragment.isRestrictedProfileInEffect(getContext()); 376 } 377 378 @VisibleForTesting updateAccessoryPref()379 void updateAccessoryPref() { 380 SlicePreference connectedDevicesSlicePreference = 381 (SlicePreference) findPreference(KEY_CONNECTED_DEVICES_SLICE); 382 Preference accessoryPreference = findPreference(KEY_ACCESSORIES); 383 Preference connectedDevicesPreference = findPreference(KEY_CONNECTED_DEVICES); 384 if (connectedDevicesSlicePreference != null 385 && FlavorUtils.isTwoPanel(getContext()) 386 && SliceUtils.isSliceProviderValid( 387 getContext(), connectedDevicesSlicePreference.getUri())) { 388 connectedDevicesSlicePreference.setVisible(true); 389 connectedDevicesPreference.setVisible(false); 390 accessoryPreference.setVisible(false); 391 ProviderInfo pkgInfo = getProviderInfo(getContext(), 392 Uri.parse(connectedDevicesSlicePreference.getUri()).getAuthority()); 393 if (pkgInfo != null) { 394 updateConnectedDevicePref(pkgInfo.packageName, connectedDevicesSlicePreference); 395 } 396 return; 397 } 398 399 if (connectedDevicesSlicePreference != null) { 400 connectedDevicesSlicePreference.setVisible(false); 401 } 402 403 if (connectedDevicesPreference != null) { 404 Intent intent = new Intent(ACTION_CONNECTED_DEVICES); 405 ResolveInfo info = systemIntentIsHandled(getContext(), intent); 406 connectedDevicesPreference.setVisible(info != null); 407 accessoryPreference.setVisible(info == null); 408 if (info != null) { 409 updateConnectedDevicePref( 410 info.activityInfo.packageName, connectedDevicesPreference); 411 return; 412 } 413 } 414 if (mBtAdapter == null || accessoryPreference == null) { 415 return; 416 } 417 418 final Set<BluetoothDevice> bondedDevices = mBtAdapter.getBondedDevices(); 419 mHasBtAccessories = bondedDevices.size() != 0; 420 } 421 422 @VisibleForTesting updateAccountPref()423 void updateAccountPref() { 424 Preference accountsPref = findPreference(KEY_ACCOUNTS_AND_SIGN_IN); 425 SlicePreference accountsSlicePref = 426 (SlicePreference) findPreference(KEY_ACCOUNTS_AND_SIGN_IN_SLICE); 427 Preference accountsBasicMode = findPreference(KEY_ACCOUNTS_AND_SIGN_IN_BASIC_MODE); 428 Intent intent = new Intent(ACTION_ACCOUNTS); 429 430 switch(AccountsUtil.getAccountsFragmentToLaunch(getContext())) { 431 case ACCOUNTS_FRAGMENT_RESTRICTED: { 432 // Use the bundled AccountsFragment if restriction active 433 if (accountsBasicMode != null) { 434 accountsBasicMode.setVisible(false); 435 } 436 if (accountsSlicePref != null) { 437 accountsSlicePref.setVisible(false); 438 } 439 if (accountsPref != null) { 440 accountsPref.setVisible(true); 441 } 442 return; 443 } 444 case ACCOUNTS_BASIC_MODE_FRAGMENT: { 445 if (accountsBasicMode != null) { 446 accountsBasicMode.setVisible(true); 447 } 448 if (accountsPref != null) { 449 accountsPref.setVisible(false); 450 } 451 if (accountsSlicePref != null) { 452 accountsSlicePref.setVisible(false); 453 } 454 return; 455 } 456 case ACCOUNTS_SYSTEM_INTENT: { 457 if (accountsPref != null) { 458 accountsPref.setVisible(true); 459 accountsPref.setFragment(null); 460 accountsPref.setIntent(intent); 461 } 462 if (accountsSlicePref != null) { 463 accountsSlicePref.setVisible(false); 464 } 465 if (accountsBasicMode != null) { 466 accountsBasicMode.setVisible(false); 467 } 468 return; 469 } 470 case ACCOUNTS_SLICE_FRAGMENT: { 471 // If a slice is available, use it to display the accounts settings, otherwise 472 // fall back to use AccountsFragment. 473 if (accountsPref != null) { 474 accountsPref.setVisible(false); 475 } 476 if (accountsSlicePref != null) { 477 accountsSlicePref.setVisible(true); 478 } 479 if (accountsBasicMode != null) { 480 accountsBasicMode.setVisible(false); 481 } 482 return; 483 } 484 case ACCOUNTS_FRAGMENT_DEFAULT: 485 default: { 486 if (accountsPref != null) { 487 accountsPref.setVisible(true); 488 updateAccountPrefInfo(); 489 } 490 if (accountsSlicePref != null) { 491 accountsSlicePref.setVisible(false); 492 } 493 if (accountsBasicMode != null) { 494 accountsBasicMode.setVisible(false); 495 } 496 } 497 } 498 } 499 500 @VisibleForTesting updateAccountPrefInfo()501 void updateAccountPrefInfo() { 502 Preference accountsPref = findPreference(KEY_ACCOUNTS_AND_SIGN_IN); 503 if (accountsPref != null && accountsPref.isVisible()) { 504 final AccountManager am = AccountManager.get(getContext()); 505 Account[] accounts = am.getAccounts(); 506 if (accounts.length == 0) { 507 mHasAccounts = false; 508 accountsPref.setIcon(R.drawable.ic_add_an_account); 509 accountsPref.setSummary(R.string.accounts_category_summary_no_account); 510 AccountsFragment.setUpAddAccountPrefIntent(accountsPref, getContext()); 511 } else { 512 mHasAccounts = true; 513 accountsPref.setIcon(R.drawable.ic_accounts_and_sign_in); 514 if (accounts.length == 1) { 515 accountsPref.setSummary(accounts[0].name); 516 } else { 517 accountsPref.setSummary(getResources().getQuantityString( 518 R.plurals.accounts_category_summary, accounts.length, accounts.length)); 519 } 520 } 521 } 522 } 523 524 @VisibleForTesting updateBasicModeSuggestion()525 void updateBasicModeSuggestion() { 526 PreferenceCategory basicModeSuggestion = findPreference(KEY_BASIC_MODE_SUGGESTION); 527 if (basicModeSuggestion == null) { 528 return; 529 } 530 if (FlavorUtils.getFeatureFactory(getContext()) 531 .getBasicModeFeatureProvider().isBasicMode(getContext())) { 532 basicModeSuggestion.setVisible(true); 533 } else { 534 basicModeSuggestion.setVisible(false); 535 } 536 } 537 538 @Override onStart()539 public void onStart() { 540 super.onStart(); 541 IntentFilter btChangeFilter = new IntentFilter(); 542 btChangeFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 543 btChangeFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 544 btChangeFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 545 getContext().registerReceiver(mBCMReceiver, btChangeFilter); 546 } 547 548 @Override onStop()549 public void onStop() { 550 super.onStop(); 551 getContext().unregisterReceiver(mBCMReceiver); 552 } 553 554 @Override onPreferenceTreeClick(Preference preference)555 public boolean onPreferenceTreeClick(Preference preference) { 556 if ((preference.getKey().equals(KEY_ACCOUNTS_AND_SIGN_IN) && !mHasAccounts 557 && !AccountsUtil.isAdminRestricted(getContext())) 558 || (preference.getKey().equals(KEY_ACCESSORIES) && !mHasBtAccessories) 559 || (preference.getKey().equals(KEY_DISPLAY_AND_SOUND) 560 && preference.getIntent() != null) 561 || (preference.getKey().equals(KEY_CHANNELS_AND_INPUTS) 562 && preference.getIntent() != null)) { 563 getContext().startActivity(preference.getIntent()); 564 return true; 565 } else if (preference.getKey().equals(KEY_BASIC_MODE_EXIT) 566 && FlavorUtils.getFeatureFactory(getContext()) 567 .getBasicModeFeatureProvider().isBasicMode(getContext())) { 568 if (getActivity() != null) { 569 FlavorUtils.getFeatureFactory(getContext()) 570 .getBasicModeFeatureProvider().startBasicModeExitActivity(getActivity()); 571 } 572 return true; 573 } else { 574 return super.onPreferenceTreeClick(preference); 575 } 576 } 577 578 @Override onDestroy()579 public void onDestroy() { 580 mSuggestionQuickSettingPrefsContainer.onDestroy(); 581 super.onDestroy(); 582 } 583 584 @Override onCreatePreferenceControllers(Context context)585 protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) { 586 return mSuggestionQuickSettingPrefsContainer.onCreatePreferenceControllers(context); 587 } 588 589 @Override onSuggestionClosed(Preference preference)590 public void onSuggestionClosed(Preference preference) { 591 mSuggestionQuickSettingPrefsContainer.onSuggestionClosed(preference); 592 } 593 594 @Override onSuggestionReady(List<Suggestion> data)595 public void onSuggestionReady(List<Suggestion> data) { 596 mSuggestionQuickSettingPrefsContainer.onSuggestionReady(data); 597 } 598 599 @Override onHotwordStateChanged()600 public void onHotwordStateChanged() { 601 mSuggestionQuickSettingPrefsContainer.onHotwordStateChanged(); 602 } 603 604 @Override onHotwordEnable()605 public void onHotwordEnable() { 606 mSuggestionQuickSettingPrefsContainer.onHotwordEnable(); 607 } 608 609 @Override onHotwordDisable()610 public void onHotwordDisable() { 611 mSuggestionQuickSettingPrefsContainer.onHotwordDisable(); 612 } 613 supportBluetooth()614 private boolean supportBluetooth() { 615 return getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); 616 } 617 updateConnectedDevicePref(String pkgName, Preference pref)618 private void updateConnectedDevicePref(String pkgName, Preference pref) { 619 Drawable icon = getDrawableResource(pkgName, "connected_devices_pref_icon"); 620 if (icon != null) { 621 pref.setIcon(icon); 622 } 623 String title = 624 (pref instanceof SlicePreference) 625 ? getStringResource(pkgName, "connected_devices_slice_pref_title") 626 : getStringResource(pkgName, "connected_devices_pref_title"); 627 if (!TextUtils.isEmpty(title)) { 628 pref.setTitle(title); 629 } 630 String summary = getStringResource(pkgName, "connected_devices_pref_summary"); 631 if (!TextUtils.isEmpty(summary)) { 632 pref.setSummary(summary); 633 } 634 pref.setOnPreferenceClickListener( 635 preference -> { 636 logEntrySelected(TvSettingsEnums.CONNECTED_CLASSIC); 637 return false; 638 }); 639 } 640 641 @Override getPageId()642 protected int getPageId() { 643 return TvSettingsEnums.TV_SETTINGS_ROOT; 644 } 645 } 646