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