• 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 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 
newInstance()101     public static MainFragment newInstance() {
102         return new MainFragment();
103     }
104 
105     @Override
getMetricsCategory()106     public int getMetricsCategory() {
107         // Do not log visibility.
108         return METRICS_CATEGORY_UNKNOWN;
109     }
110 
111     @Override
getPreferenceScreenResId()112     protected int getPreferenceScreenResId() {
113         return R.xml.main_prefs;
114     }
115 
116     @Override
onCreate(Bundle savedInstanceState)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
onDestroy()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. */
shouldShowQuickSettings()134     private boolean shouldShowQuickSettings() {
135         for (AbstractPreferenceController controller : mPreferenceControllers) {
136             if (controller.isAvailable()) {
137                 return true;
138             }
139         }
140         return false;
141     }
142 
showOrHideQuickSettings()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. */
showQuickSettings()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. */
hideQuickSettings()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
onHotwordStateChanged()180     public void onHotwordStateChanged() {
181         if (mHotwordSwitch != null) {
182             mHotwordSwitchController.updateState(mHotwordSwitch);
183         }
184         showOrHideQuickSettings();
185     }
186 
187     @Override
onHotwordEnable()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
onHotwordDisable()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
onCreatePreferences(Bundle savedInstanceState, String rootKey)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
onCreatePreferenceControllers(Context context)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
updateWifi()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      */
systemIntentIsHandled(Context context, Intent intent)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
onSuggestionReady(List<Suggestion> data)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
updateSuggestionList(List<Suggestion> suggestions)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
onResume()380     public void onResume() {
381         super.onResume();
382         updateAccountPref();
383         updateAccessoryPref();
384     }
385 
isRestricted()386     private boolean isRestricted() {
387         return SecurityFragment.isRestrictedProfileInEffect(getContext());
388     }
389 
390     @VisibleForTesting
updateAccessoryPref()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
updateAccountPref()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
onStart()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
onStop()440     public void onStop() {
441         super.onStop();
442         getContext().unregisterReceiver(mBCMReceiver);
443     }
444 
445     @Override
onAttach(Context context)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
onPreferenceTreeClick(Preference preference)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
onSuggestionClosed(Preference preference)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