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