Home | History | Annotate | Download | only in settings
      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 android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.bluetooth.BluetoothAdapter;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.content.ActivityNotFoundException;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.ApplicationInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.ResolveInfo;
     32 import android.os.Bundle;
     33 import android.service.settings.suggestions.Suggestion;
     34 import android.support.annotation.VisibleForTesting;
     35 import android.support.v14.preference.SwitchPreference;
     36 import android.support.v7.preference.Preference;
     37 import android.support.v7.preference.PreferenceCategory;
     38 import android.telephony.SignalStrength;
     39 import android.util.Log;
     40 
     41 import com.android.settingslib.core.AbstractPreferenceController;
     42 import com.android.settingslib.suggestions.SuggestionControllerMixin;
     43 import com.android.settingslib.utils.IconCache;
     44 import com.android.tv.settings.HotwordSwitchController.HotwordStateListener;
     45 import com.android.tv.settings.accounts.AccountsFragment;
     46 import com.android.tv.settings.connectivity.ConnectivityListener;
     47 import com.android.tv.settings.suggestions.SuggestionPreference;
     48 import com.android.tv.settings.system.SecurityFragment;
     49 
     50 import java.util.ArrayList;
     51 import java.util.List;
     52 import java.util.Set;
     53 
     54 /**
     55  * The fragment where all good things begin. Evil is handled elsewhere.
     56  */
     57 public class MainFragment extends PreferenceControllerFragment implements
     58         SuggestionControllerMixin.SuggestionControllerHost, SuggestionPreference.Callback,
     59         HotwordStateListener {
     60 
     61     private static final String TAG = "MainFragment";
     62     private static final String KEY_SUGGESTIONS_LIST = "suggestions";
     63     @VisibleForTesting
     64     static final String KEY_ACCOUNTS_AND_SIGN_IN = "accounts_and_sign_in";
     65     private static final String KEY_APPLICATIONS = "applications";
     66     @VisibleForTesting
     67     static final String KEY_ACCESSORIES = "remotes_and_accessories";
     68     @VisibleForTesting
     69     static final String KEY_NETWORK = "network";
     70 
     71     @VisibleForTesting
     72     static final String KEY_QUICK_SETTINGS = "quick_settings";
     73 
     74     @VisibleForTesting
     75     ConnectivityListener mConnectivityListener;
     76     @VisibleForTesting
     77     PreferenceCategory mSuggestionsList;
     78     private SuggestionControllerMixin mSuggestionControllerMixin;
     79     @VisibleForTesting
     80     IconCache mIconCache;
     81     @VisibleForTesting
     82     BluetoothAdapter mBtAdapter;
     83     @VisibleForTesting
     84     boolean mHasBtAccessories;
     85     @VisibleForTesting
     86     boolean mHasAccounts;
     87 
     88     /** Controllers for the Quick Settings section. */
     89     private List<AbstractPreferenceController> mPreferenceControllers;
     90     private HotwordSwitchController mHotwordSwitchController;
     91     private PreferenceCategory mQuickSettingsList;
     92     private SwitchPreference mHotwordSwitch;
     93 
     94     private final BroadcastReceiver mBCMReceiver = new BroadcastReceiver() {
     95         @Override
     96         public void onReceive(Context context, Intent intent) {
     97             updateAccessoryPref();
     98         }
     99     };
    100 
    101     public static MainFragment newInstance() {
    102         return new MainFragment();
    103     }
    104 
    105     @Override
    106     public int getMetricsCategory() {
    107         // Do not log visibility.
    108         return METRICS_CATEGORY_UNKNOWN;
    109     }
    110 
    111     @Override
    112     protected int getPreferenceScreenResId() {
    113         return R.xml.main_prefs;
    114     }
    115 
    116     @Override
    117     public void onCreate(Bundle savedInstanceState) {
    118         mIconCache = new IconCache(getContext());
    119         mConnectivityListener =
    120                 new ConnectivityListener(getContext(), this::updateWifi, getLifecycle());
    121         mBtAdapter = BluetoothAdapter.getDefaultAdapter();
    122         super.onCreate(savedInstanceState);
    123     }
    124 
    125     @Override
    126     public void onDestroy() {
    127         if (mHotwordSwitchController != null) {
    128             mHotwordSwitchController.unregister();
    129         }
    130         super.onDestroy();
    131     }
    132 
    133     /** @return true if there is at least one available item in quick settings. */
    134     private boolean shouldShowQuickSettings() {
    135         for (AbstractPreferenceController controller : mPreferenceControllers) {
    136             if (controller.isAvailable()) {
    137                 return true;
    138             }
    139         }
    140         return false;
    141     }
    142 
    143     private void showOrHideQuickSettings() {
    144         if (shouldShowQuickSettings()) {
    145             showQuickSettings();
    146         } else {
    147             hideQuickSettings();
    148         }
    149     }
    150 
    151     /** Creates the quick settings category and its children. */
    152     private void showQuickSettings() {
    153         if (mQuickSettingsList != null) {
    154             return;
    155         }
    156         mQuickSettingsList = new PreferenceCategory(this.getPreferenceManager().getContext());
    157         mQuickSettingsList.setKey(KEY_QUICK_SETTINGS);
    158         mQuickSettingsList.setTitle(R.string.header_category_quick_settings);
    159         mQuickSettingsList.setLayoutResource(R.layout.preference_category_compact_layout);
    160         mQuickSettingsList.setOrder(1); // at top, but below suggested settings
    161         getPreferenceScreen().addPreference(mQuickSettingsList);
    162         mHotwordSwitch = new SwitchPreference(this.getPreferenceManager().getContext());
    163         mHotwordSwitch.setKey(HotwordSwitchController.KEY_HOTWORD_SWITCH);
    164         mHotwordSwitchController.updateState(mHotwordSwitch);
    165         mQuickSettingsList.addPreference(mHotwordSwitch);
    166     }
    167 
    168     /** Removes the quick settings category and all its children. */
    169     private void hideQuickSettings() {
    170         Preference quickSettingsPref = findPreference(KEY_QUICK_SETTINGS);
    171         if (quickSettingsPref == null) {
    172             return;
    173         }
    174         mQuickSettingsList.removeAll();
    175         getPreferenceScreen().removePreference(mQuickSettingsList);
    176         mQuickSettingsList = null;
    177     }
    178 
    179     @Override
    180     public void onHotwordStateChanged() {
    181         if (mHotwordSwitch != null) {
    182             mHotwordSwitchController.updateState(mHotwordSwitch);
    183         }
    184         showOrHideQuickSettings();
    185     }
    186 
    187     @Override
    188     public void onHotwordEnable() {
    189         try {
    190             Intent intent = new Intent(HotwordSwitchController.ACTION_HOTWORD_ENABLE);
    191             intent.setPackage(HotwordSwitchController.ASSISTANT_PGK_NAME);
    192             startActivityForResult(intent, 0);
    193         } catch (ActivityNotFoundException e) {
    194             Log.w(TAG, "Unable to find hotwording activity.", e);
    195         }
    196     }
    197 
    198     @Override
    199     public void onHotwordDisable() {
    200         try {
    201             Intent intent = new Intent(HotwordSwitchController.ACTION_HOTWORD_DISABLE);
    202             intent.setPackage(HotwordSwitchController.ASSISTANT_PGK_NAME);
    203             startActivityForResult(intent, 0);
    204         } catch (ActivityNotFoundException e) {
    205             Log.w(TAG, "Unable to find hotwording activity.", e);
    206         }
    207     }
    208 
    209     @Override
    210     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    211         setPreferencesFromResource(R.xml.main_prefs, null);
    212         if (isRestricted()) {
    213             Preference appPref = findPreference(KEY_APPLICATIONS);
    214             if (appPref != null) {
    215                 appPref.setVisible(false);
    216             }
    217             Preference accountsPref = findPreference(KEY_ACCOUNTS_AND_SIGN_IN);
    218             if (accountsPref != null) {
    219                 accountsPref.setVisible(false);
    220             }
    221         }
    222         mHotwordSwitchController.init(this);
    223     }
    224 
    225     @Override
    226     protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) {
    227         mPreferenceControllers = new ArrayList<>(1);
    228         mHotwordSwitchController = new HotwordSwitchController(context);
    229         mPreferenceControllers.add(mHotwordSwitchController);
    230         return mPreferenceControllers;
    231     }
    232 
    233     @VisibleForTesting
    234     void updateWifi() {
    235         final Preference networkPref = findPreference(KEY_NETWORK);
    236         if (networkPref == null) {
    237             return;
    238         }
    239 
    240         if (mConnectivityListener.isCellConnected()) {
    241             final int signal = mConnectivityListener.getCellSignalStrength();
    242             switch (signal) {
    243                 case SignalStrength.SIGNAL_STRENGTH_GREAT:
    244                     networkPref.setIcon(R.drawable.ic_cell_signal_4_white);
    245                     break;
    246                 case SignalStrength.SIGNAL_STRENGTH_GOOD:
    247                     networkPref.setIcon(R.drawable.ic_cell_signal_3_white);
    248                     break;
    249                 case SignalStrength.SIGNAL_STRENGTH_MODERATE:
    250                     networkPref.setIcon(R.drawable.ic_cell_signal_2_white);
    251                     break;
    252                 case SignalStrength.SIGNAL_STRENGTH_POOR:
    253                     networkPref.setIcon(R.drawable.ic_cell_signal_1_white);
    254                     break;
    255                 case SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN:
    256                 default:
    257                     networkPref.setIcon(R.drawable.ic_cell_signal_0_white);
    258                     break;
    259             }
    260         } else if (mConnectivityListener.isEthernetConnected()) {
    261             networkPref.setIcon(R.drawable.ic_ethernet_white);
    262             networkPref.setSummary(R.string.connectivity_summary_ethernet_connected);
    263         } else if (mConnectivityListener.isWifiEnabledOrEnabling()) {
    264             if (mConnectivityListener.isWifiConnected()) {
    265                 final int signal = mConnectivityListener.getWifiSignalStrength(5);
    266                 switch (signal) {
    267                     case 4:
    268                         networkPref.setIcon(R.drawable.ic_wifi_signal_4_white);
    269                         break;
    270                     case 3:
    271                         networkPref.setIcon(R.drawable.ic_wifi_signal_3_white);
    272                         break;
    273                     case 2:
    274                         networkPref.setIcon(R.drawable.ic_wifi_signal_2_white);
    275                         break;
    276                     case 1:
    277                         networkPref.setIcon(R.drawable.ic_wifi_signal_1_white);
    278                         break;
    279                     case 0:
    280                     default:
    281                         networkPref.setIcon(R.drawable.ic_wifi_signal_0_white);
    282                         break;
    283                 }
    284                 networkPref.setSummary(mConnectivityListener.getSsid());
    285             } else {
    286                 networkPref.setIcon(R.drawable.ic_wifi_not_connected);
    287                 networkPref.setSummary(R.string.connectivity_summary_no_network_connected);
    288             }
    289         } else {
    290             networkPref.setIcon(R.drawable.ic_wifi_signal_off_white);
    291             networkPref.setSummary(R.string.connectivity_summary_wifi_disabled);
    292         }
    293     }
    294 
    295     /**
    296      * Returns the ResolveInfo for the system activity that matches given intent filter or null if
    297      * no such activity exists.
    298      * @param context Context of the caller
    299      * @param intent The intent matching the desired system app
    300      * @return ResolveInfo of the matching activity or null if no match exists
    301      */
    302     public static ResolveInfo systemIntentIsHandled(Context context, Intent intent) {
    303         if (intent == null) {
    304             return null;
    305         }
    306 
    307         final PackageManager pm = context.getPackageManager();
    308 
    309         for (ResolveInfo info : pm.queryIntentActivities(intent, 0)) {
    310             if (info.activityInfo != null
    311                     && (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
    312                     == ApplicationInfo.FLAG_SYSTEM) {
    313                 return info;
    314             }
    315         }
    316         return null;
    317     }
    318 
    319     @Override
    320     public void onSuggestionReady(List<Suggestion> data) {
    321         if (data == null || data.size() == 0) {
    322             if (mSuggestionsList != null) {
    323                 getPreferenceScreen().removePreference(mSuggestionsList);
    324                 mSuggestionsList = null;
    325             }
    326             return;
    327         }
    328 
    329         if (mSuggestionsList == null) {
    330             mSuggestionsList = new PreferenceCategory(this.getPreferenceManager().getContext());
    331             mSuggestionsList.setKey(KEY_SUGGESTIONS_LIST);
    332             mSuggestionsList.setTitle(R.string.header_category_suggestions);
    333             mSuggestionsList.setLayoutResource(R.layout.preference_category_compact_layout);
    334             mSuggestionsList.setOrder(0); // always at top
    335             getPreferenceScreen().addPreference(mSuggestionsList);
    336         }
    337         updateSuggestionList(data);
    338     }
    339 
    340     @VisibleForTesting
    341     void updateSuggestionList(List<Suggestion> suggestions) {
    342         // Remove suggestions that are not in the new list.
    343         for (int i = 0; i < mSuggestionsList.getPreferenceCount(); i++) {
    344             SuggestionPreference pref = (SuggestionPreference) mSuggestionsList.getPreference(i);
    345             boolean isInNewSuggestionList = false;
    346             for (Suggestion suggestion : suggestions) {
    347                 if (pref.getId().equals(suggestion.getId())) {
    348                     isInNewSuggestionList = true;
    349                     break;
    350                 }
    351             }
    352             if (!isInNewSuggestionList) {
    353                 mSuggestionsList.removePreference(pref);
    354             }
    355         }
    356 
    357         // Add suggestions that are not in the old list and update the existing suggestions.
    358         for (Suggestion suggestion : suggestions) {
    359             Preference curPref = findPreference(
    360                         SuggestionPreference.SUGGESTION_PREFERENCE_KEY + suggestion.getId());
    361             if (curPref == null) {
    362                 SuggestionPreference newSuggPref = new SuggestionPreference(
    363                         suggestion, this.getPreferenceManager().getContext(),
    364                         mSuggestionControllerMixin, this);
    365                 newSuggPref.setIcon(mIconCache.getIcon(suggestion.getIcon()));
    366                 newSuggPref.setTitle(suggestion.getTitle());
    367                 newSuggPref.setSummary(suggestion.getSummary());
    368                 mSuggestionsList.addPreference(newSuggPref);
    369             } else {
    370                 // Even though the id of suggestion might not change, the details could change.
    371                 // So we need to update icon, title and summary for the suggestions.
    372                 curPref.setIcon(mIconCache.getIcon(suggestion.getIcon()));
    373                 curPref.setTitle(suggestion.getTitle());
    374                 curPref.setSummary(suggestion.getSummary());
    375             }
    376         }
    377     }
    378 
    379     @Override
    380     public void onResume() {
    381         super.onResume();
    382         updateAccountPref();
    383         updateAccessoryPref();
    384     }
    385 
    386     private boolean isRestricted() {
    387         return SecurityFragment.isRestrictedProfileInEffect(getContext());
    388     }
    389 
    390     @VisibleForTesting
    391     void updateAccessoryPref() {
    392         Preference accessoryPreference = findPreference(KEY_ACCESSORIES);
    393         if (mBtAdapter == null || accessoryPreference == null) {
    394             return;
    395         }
    396 
    397         final Set<BluetoothDevice> bondedDevices = mBtAdapter.getBondedDevices();
    398         if (bondedDevices.size() == 0) {
    399             mHasBtAccessories = false;
    400         } else {
    401             mHasBtAccessories = true;
    402         }
    403     }
    404 
    405     @VisibleForTesting
    406     void updateAccountPref() {
    407         final Preference accountsPref = findPreference(KEY_ACCOUNTS_AND_SIGN_IN);
    408         if (accountsPref != null && accountsPref.isVisible()) {
    409             final AccountManager am = AccountManager.get(getContext());
    410             Account[] accounts = am.getAccounts();
    411             if (accounts.length == 0) {
    412                 mHasAccounts = false;
    413                 accountsPref.setIcon(R.drawable.ic_add_an_account);
    414                 accountsPref.setSummary(R.string.accounts_category_summary_no_account);
    415                 AccountsFragment.setUpAddAccountPrefIntent(accountsPref, getContext());
    416             } else {
    417                 mHasAccounts = true;
    418                 accountsPref.setIcon(R.drawable.ic_accounts_and_sign_in);
    419                 if (accounts.length == 1) {
    420                     accountsPref.setSummary(accounts[0].name);
    421                 } else {
    422                     accountsPref.setSummary(getResources().getQuantityString(
    423                             R.plurals.accounts_category_summary, accounts.length, accounts.length));
    424                 }
    425             }
    426         }
    427     }
    428 
    429     @Override
    430     public void onStart() {
    431         super.onStart();
    432         IntentFilter btChangeFilter = new IntentFilter();
    433         btChangeFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    434         btChangeFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    435         btChangeFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    436         getContext().registerReceiver(mBCMReceiver, btChangeFilter);
    437     }
    438 
    439     @Override
    440     public void onStop() {
    441         super.onStop();
    442         getContext().unregisterReceiver(mBCMReceiver);
    443     }
    444 
    445     @Override
    446     public void onAttach(Context context) {
    447         super.onAttach(context);
    448         ComponentName componentName = new ComponentName(
    449                 "com.android.settings.intelligence",
    450                 "com.android.settings.intelligence.suggestions.SuggestionService");
    451         mSuggestionControllerMixin = new SuggestionControllerMixin(
    452                                             context, this, getLifecycle(), componentName);
    453     }
    454 
    455     @Override
    456     public boolean onPreferenceTreeClick(Preference preference) {
    457         if (preference.getKey().equals(KEY_ACCOUNTS_AND_SIGN_IN) && !mHasAccounts
    458                 || (preference.getKey().equals(KEY_ACCESSORIES) && !mHasBtAccessories)) {
    459             getContext().startActivity(preference.getIntent());
    460             return true;
    461         } else {
    462             return super.onPreferenceTreeClick(preference);
    463         }
    464     }
    465 
    466     @Override
    467     public void onSuggestionClosed(Preference preference) {
    468         if (mSuggestionsList == null || mSuggestionsList.getPreferenceCount() == 0) {
    469             return;
    470         } else if (mSuggestionsList.getPreferenceCount() == 1) {
    471             getPreferenceScreen().removePreference(mSuggestionsList);
    472         } else {
    473             mSuggestionsList.removePreference(preference);
    474         }
    475     }
    476 }
    477