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