• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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