• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.settings;
18 
19 import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED;
20 
21 import android.app.ActionBar;
22 import android.app.Activity;
23 import android.app.Fragment;
24 import android.app.FragmentManager;
25 import android.app.FragmentTransaction;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.SharedPreferences;
32 import android.content.pm.ActivityInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.PackageManager.NameNotFoundException;
35 import android.content.pm.ResolveInfo;
36 import android.content.res.Configuration;
37 import android.content.res.TypedArray;
38 import android.content.res.XmlResourceParser;
39 import android.nfc.NfcAdapter;
40 import android.os.Bundle;
41 import android.os.Handler;
42 import android.os.Message;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.preference.Preference;
46 import android.preference.PreferenceFragment;
47 import android.preference.PreferenceManager;
48 import android.preference.PreferenceScreen;
49 import android.provider.Settings.Global;
50 import android.text.TextUtils;
51 import android.transition.TransitionManager;
52 import android.util.ArrayMap;
53 import android.util.AttributeSet;
54 import android.util.Log;
55 import android.util.Pair;
56 import android.util.TypedValue;
57 import android.util.Xml;
58 import android.view.Menu;
59 import android.view.MenuInflater;
60 import android.view.MenuItem;
61 import android.view.View;
62 import android.view.View.OnClickListener;
63 import android.view.ViewGroup;
64 import android.widget.Button;
65 import android.widget.SearchView;
66 
67 import com.android.internal.logging.MetricsLogger;
68 import com.android.internal.util.ArrayUtils;
69 import com.android.internal.util.XmlUtils;
70 import com.android.settings.accessibility.AccessibilitySettings;
71 import com.android.settings.accessibility.CaptionPropertiesFragment;
72 import com.android.settings.accounts.AccountSettings;
73 import com.android.settings.accounts.AccountSyncSettings;
74 import com.android.settings.applications.DrawOverlayDetails;
75 import com.android.settings.applications.InstalledAppDetails;
76 import com.android.settings.applications.ManageApplications;
77 import com.android.settings.applications.ManageAssist;
78 import com.android.settings.applications.ProcessStatsSummary;
79 import com.android.settings.applications.ProcessStatsUi;
80 import com.android.settings.applications.UsageAccessDetails;
81 import com.android.settings.applications.WriteSettingsDetails;
82 import com.android.settings.bluetooth.BluetoothSettings;
83 import com.android.settings.dashboard.DashboardCategory;
84 import com.android.settings.dashboard.DashboardSummary;
85 import com.android.settings.dashboard.DashboardTile;
86 import com.android.settings.dashboard.NoHomeDialogFragment;
87 import com.android.settings.dashboard.SearchResultsSummary;
88 import com.android.settings.deviceinfo.PrivateVolumeForget;
89 import com.android.settings.deviceinfo.PrivateVolumeSettings;
90 import com.android.settings.deviceinfo.PublicVolumeSettings;
91 import com.android.settings.deviceinfo.StorageSettings;
92 import com.android.settings.fuelgauge.BatterySaverSettings;
93 import com.android.settings.fuelgauge.PowerUsageDetail;
94 import com.android.settings.fuelgauge.PowerUsageSummary;
95 import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
96 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
97 import com.android.settings.inputmethod.SpellCheckersSettings;
98 import com.android.settings.inputmethod.UserDictionaryList;
99 import com.android.settings.location.LocationSettings;
100 import com.android.settings.nfc.AndroidBeam;
101 import com.android.settings.nfc.PaymentSettings;
102 import com.android.settings.notification.AppNotificationSettings;
103 import com.android.settings.notification.NotificationAccessSettings;
104 import com.android.settings.notification.NotificationSettings;
105 import com.android.settings.notification.NotificationStation;
106 import com.android.settings.notification.OtherSoundSettings;
107 import com.android.settings.notification.ZenAccessSettings;
108 import com.android.settings.notification.ZenModeAutomationSettings;
109 import com.android.settings.notification.ZenModeEventRuleSettings;
110 import com.android.settings.notification.ZenModeExternalRuleSettings;
111 import com.android.settings.notification.ZenModePrioritySettings;
112 import com.android.settings.notification.ZenModeSettings;
113 import com.android.settings.notification.ZenModeScheduleRuleSettings;
114 import com.android.settings.print.PrintJobSettingsFragment;
115 import com.android.settings.print.PrintSettingsFragment;
116 import com.android.settings.search.DynamicIndexableContentMonitor;
117 import com.android.settings.search.Index;
118 import com.android.settings.sim.SimSettings;
119 import com.android.settings.tts.TextToSpeechSettings;
120 import com.android.settings.users.UserSettings;
121 import com.android.settings.vpn2.VpnSettings;
122 import com.android.settings.wfd.WifiDisplaySettings;
123 import com.android.settings.widget.SwitchBar;
124 import com.android.settings.wifi.AdvancedWifiSettings;
125 import com.android.settings.wifi.SavedAccessPointsWifiSettings;
126 import com.android.settings.wifi.WifiSettings;
127 import com.android.settings.wifi.p2p.WifiP2pSettings;
128 
129 import org.xmlpull.v1.XmlPullParser;
130 import org.xmlpull.v1.XmlPullParserException;
131 
132 import java.io.IOException;
133 import java.util.ArrayList;
134 import java.util.List;
135 import java.util.Map;
136 import java.util.Set;
137 
138 public class SettingsActivity extends Activity
139         implements PreferenceManager.OnPreferenceTreeClickListener,
140         PreferenceFragment.OnPreferenceStartFragmentCallback,
141         ButtonBarHandler, FragmentManager.OnBackStackChangedListener,
142         SearchView.OnQueryTextListener, SearchView.OnCloseListener,
143         MenuItem.OnActionExpandListener {
144 
145     private static final String LOG_TAG = "Settings";
146 
147     // Constants for state save/restore
148     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
149     private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
150     private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
151     private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
152     private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
153     private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count";
154 
155     /**
156      * When starting this activity, the invoking Intent can contain this extra
157      * string to specify which fragment should be initially displayed.
158      * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
159      * will call isValidFragment() to confirm that the fragment class name is valid for this
160      * activity.
161      */
162     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
163 
164     /**
165      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
166      * this extra can also be specified to supply a Bundle of arguments to pass
167      * to that fragment when it is instantiated during the initial creation
168      * of the activity.
169      */
170     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
171 
172     /**
173      * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
174      */
175     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
176 
177     public static final String BACK_STACK_PREFS = ":settings:prefs";
178 
179     // extras that allow any preference activity to be launched as part of a wizard
180 
181     // show Back and Next buttons? takes boolean parameter
182     // Back will then return RESULT_CANCELED and Next RESULT_OK
183     protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
184 
185     // add a Skip button?
186     private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
187 
188     // specify custom text for the Back or Next buttons, or cause a button to not appear
189     // at all by setting it to null
190     protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
191     protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
192 
193     /**
194      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
195      * those extra can also be specify to supply the title or title res id to be shown for
196      * that fragment.
197      */
198     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
199     /**
200      * The package name used to resolve the title resource id.
201      */
202     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
203             ":settings:show_fragment_title_res_package_name";
204     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
205             ":settings:show_fragment_title_resid";
206     public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
207             ":settings:show_fragment_as_shortcut";
208 
209     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
210             ":settings:show_fragment_as_subsetting";
211 
212     private static final String META_DATA_KEY_FRAGMENT_CLASS =
213         "com.android.settings.FRAGMENT_CLASS";
214 
215     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
216 
217     private static final String EMPTY_QUERY = "";
218 
219     /**
220      * Settings will search for system activities of this action and add them as a top level
221      * settings tile using the following parameters.
222      *
223      * <p>A category must be specified in the meta-data for the activity named
224      * {@link #EXTRA_CATEGORY_KEY}
225      *
226      * <p>The title may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_TITLE}
227      * otherwise the label for the activity will be used.
228      *
229      * <p>The icon may be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_ICON}
230      * otherwise the icon for the activity will be used.
231      *
232      * <p>A summary my be defined by meta-data named {@link Utils#META_DATA_PREFERENCE_SUMMARY}
233      */
234     private static final String EXTRA_SETTINGS_ACTION =
235             "com.android.settings.action.EXTRA_SETTINGS";
236 
237     /**
238      * The key used to get the category from metadata of activities of action
239      * {@link #EXTRA_SETTINGS_ACTION}
240      * The value must be one of:
241      * <li>com.android.settings.category.wireless</li>
242      * <li>com.android.settings.category.device</li>
243      * <li>com.android.settings.category.personal</li>
244      * <li>com.android.settings.category.system</li>
245      */
246     private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";
247 
248     private static boolean sShowNoHomeNotice = false;
249 
250     private String mFragmentClass;
251 
252     private CharSequence mInitialTitle;
253     private int mInitialTitleResId;
254 
255     // Show only these settings for restricted users
256     private int[] SETTINGS_FOR_RESTRICTED = {
257             R.id.wireless_section,
258             R.id.wifi_settings,
259             R.id.bluetooth_settings,
260             R.id.data_usage_settings,
261             R.id.sim_settings,
262             R.id.wireless_settings,
263             R.id.device_section,
264             R.id.notification_settings,
265             R.id.display_settings,
266             R.id.storage_settings,
267             R.id.application_settings,
268             R.id.battery_settings,
269             R.id.personal_section,
270             R.id.location_settings,
271             R.id.security_settings,
272             R.id.language_settings,
273             R.id.user_settings,
274             R.id.account_settings,
275             R.id.system_section,
276             R.id.date_time_settings,
277             R.id.about_settings,
278             R.id.accessibility_settings,
279             R.id.print_settings,
280             R.id.nfc_payment_settings,
281             R.id.home_settings,
282             R.id.dashboard
283     };
284 
285     private static final String[] ENTRY_FRAGMENTS = {
286             WirelessSettings.class.getName(),
287             WifiSettings.class.getName(),
288             AdvancedWifiSettings.class.getName(),
289             SavedAccessPointsWifiSettings.class.getName(),
290             BluetoothSettings.class.getName(),
291             SimSettings.class.getName(),
292             TetherSettings.class.getName(),
293             WifiP2pSettings.class.getName(),
294             VpnSettings.class.getName(),
295             DateTimeSettings.class.getName(),
296             LocalePicker.class.getName(),
297             InputMethodAndLanguageSettings.class.getName(),
298             SpellCheckersSettings.class.getName(),
299             UserDictionaryList.class.getName(),
300             UserDictionarySettings.class.getName(),
301             HomeSettings.class.getName(),
302             DisplaySettings.class.getName(),
303             DeviceInfoSettings.class.getName(),
304             ManageApplications.class.getName(),
305             ManageAssist.class.getName(),
306             ProcessStatsUi.class.getName(),
307             NotificationStation.class.getName(),
308             LocationSettings.class.getName(),
309             SecuritySettings.class.getName(),
310             UsageAccessDetails.class.getName(),
311             PrivacySettings.class.getName(),
312             DeviceAdminSettings.class.getName(),
313             AccessibilitySettings.class.getName(),
314             CaptionPropertiesFragment.class.getName(),
315             com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
316             TextToSpeechSettings.class.getName(),
317             StorageSettings.class.getName(),
318             PrivateVolumeForget.class.getName(),
319             PrivateVolumeSettings.class.getName(),
320             PublicVolumeSettings.class.getName(),
321             DevelopmentSettings.class.getName(),
322             AndroidBeam.class.getName(),
323             WifiDisplaySettings.class.getName(),
324             PowerUsageSummary.class.getName(),
325             AccountSyncSettings.class.getName(),
326             AccountSettings.class.getName(),
327             CryptKeeperSettings.class.getName(),
328             DataUsageSummary.class.getName(),
329             DreamSettings.class.getName(),
330             UserSettings.class.getName(),
331             NotificationAccessSettings.class.getName(),
332             ZenAccessSettings.class.getName(),
333             PrintSettingsFragment.class.getName(),
334             PrintJobSettingsFragment.class.getName(),
335             TrustedCredentialsSettings.class.getName(),
336             PaymentSettings.class.getName(),
337             KeyboardLayoutPickerFragment.class.getName(),
338             ZenModeSettings.class.getName(),
339             NotificationSettings.class.getName(),
340             ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
341             ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
342             InstalledAppDetails.class.getName(),
343             BatterySaverSettings.class.getName(),
344             AppNotificationSettings.class.getName(),
345             OtherSoundSettings.class.getName(),
346             ApnSettings.class.getName(),
347             WifiCallingSettings.class.getName(),
348             ZenModePrioritySettings.class.getName(),
349             ZenModeAutomationSettings.class.getName(),
350             ZenModeScheduleRuleSettings.class.getName(),
351             ZenModeEventRuleSettings.class.getName(),
352             ZenModeExternalRuleSettings.class.getName(),
353             ProcessStatsUi.class.getName(),
354             PowerUsageDetail.class.getName(),
355             ProcessStatsSummary.class.getName(),
356             DrawOverlayDetails.class.getName(),
357             WriteSettingsDetails.class.getName(),
358     };
359 
360 
361     private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
362             "android.settings.APPLICATION_DETAILS_SETTINGS"
363     };
364 
365     private SharedPreferences mDevelopmentPreferences;
366     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
367 
368     private boolean mBatteryPresent = true;
369     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
370         @Override
371         public void onReceive(Context context, Intent intent) {
372             String action = intent.getAction();
373             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
374                 boolean batteryPresent = Utils.isBatteryPresent(intent);
375 
376                 if (mBatteryPresent != batteryPresent) {
377                     mBatteryPresent = batteryPresent;
378                     invalidateCategories(true);
379                 }
380             }
381         }
382     };
383 
384     private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
385             new DynamicIndexableContentMonitor();
386 
387     private ActionBar mActionBar;
388     private SwitchBar mSwitchBar;
389 
390     private Button mNextButton;
391 
392     private boolean mDisplayHomeAsUpEnabled;
393     private boolean mDisplaySearch;
394 
395     private boolean mIsShowingDashboard;
396     private boolean mIsShortcut;
397 
398     private ViewGroup mContent;
399 
400     private SearchView mSearchView;
401     private MenuItem mSearchMenuItem;
402     private boolean mSearchMenuItemExpanded = false;
403     private SearchResultsSummary mSearchResultsFragment;
404     private String mSearchQuery;
405 
406     // Categories
407     private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>();
408 
409     private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh";
410     private static final int MSG_BUILD_CATEGORIES = 1;
411     private Handler mHandler = new Handler() {
412         @Override
413         public void handleMessage(Message msg) {
414             switch (msg.what) {
415                 case MSG_BUILD_CATEGORIES: {
416                     final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH);
417                     if (forceRefresh) {
418                         buildDashboardCategories(mCategories);
419                     }
420                 } break;
421             }
422         }
423     };
424 
425     private boolean mNeedToRevertToInitialFragment = false;
426     private int mHomeActivitiesCount = 1;
427 
428     private Intent mResultIntentData;
429 
getSwitchBar()430     public SwitchBar getSwitchBar() {
431         return mSwitchBar;
432     }
433 
getDashboardCategories(boolean forceRefresh)434     public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) {
435         if (forceRefresh || mCategories.size() == 0) {
436             buildDashboardCategories(mCategories);
437         }
438         return mCategories;
439     }
440 
441     @Override
onPreferenceStartFragment(PreferenceFragment caller, Preference pref)442     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
443         // Override the fragment title for Wallpaper settings
444         int titleRes = pref.getTitleRes();
445         if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
446             titleRes = R.string.wallpaper_settings_fragment_title;
447         } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
448                 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
449             if (UserManager.get(this).isLinkedUser()) {
450                 titleRes = R.string.profile_info_settings_title;
451             } else {
452                 titleRes = R.string.user_info_settings_title;
453             }
454         }
455         startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
456                 null, 0);
457         return true;
458     }
459 
460     @Override
onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)461     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
462         return false;
463     }
464 
invalidateCategories(boolean forceRefresh)465     private void invalidateCategories(boolean forceRefresh) {
466         if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) {
467             Message msg = new Message();
468             msg.what = MSG_BUILD_CATEGORIES;
469             msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh);
470         }
471     }
472 
473     @Override
onConfigurationChanged(Configuration newConfig)474     public void onConfigurationChanged(Configuration newConfig) {
475         super.onConfigurationChanged(newConfig);
476         Index.getInstance(this).update();
477     }
478 
479     @Override
onStart()480     protected void onStart() {
481         super.onStart();
482 
483         if (mNeedToRevertToInitialFragment) {
484             revertToInitialFragment();
485         }
486     }
487 
488     @Override
onCreateOptionsMenu(Menu menu)489     public boolean onCreateOptionsMenu(Menu menu) {
490         if (!mDisplaySearch) {
491             return false;
492         }
493 
494         MenuInflater inflater = getMenuInflater();
495         inflater.inflate(R.menu.options_menu, menu);
496 
497         // Cache the search query (can be overriden by the OnQueryTextListener)
498         final String query = mSearchQuery;
499 
500         mSearchMenuItem = menu.findItem(R.id.search);
501         mSearchView = (SearchView) mSearchMenuItem.getActionView();
502 
503         if (mSearchMenuItem == null || mSearchView == null) {
504             return false;
505         }
506 
507         if (mSearchResultsFragment != null) {
508             mSearchResultsFragment.setSearchView(mSearchView);
509         }
510 
511         mSearchMenuItem.setOnActionExpandListener(this);
512         mSearchView.setOnQueryTextListener(this);
513         mSearchView.setOnCloseListener(this);
514 
515         if (mSearchMenuItemExpanded) {
516             mSearchMenuItem.expandActionView();
517         }
518         mSearchView.setQuery(query, true /* submit */);
519 
520         return true;
521     }
522 
isShortCutIntent(final Intent intent)523     private static boolean isShortCutIntent(final Intent intent) {
524         Set<String> categories = intent.getCategories();
525         return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
526     }
527 
isLikeShortCutIntent(final Intent intent)528     private static boolean isLikeShortCutIntent(final Intent intent) {
529         String action = intent.getAction();
530         if (action == null) {
531             return false;
532         }
533         for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
534             if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
535         }
536         return false;
537     }
538 
539     @Override
onCreate(Bundle savedState)540     protected void onCreate(Bundle savedState) {
541         super.onCreate(savedState);
542 
543         // Should happen before any call to getIntent()
544         getMetaData();
545 
546         final Intent intent = getIntent();
547         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
548             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
549         }
550 
551         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
552                 Context.MODE_PRIVATE);
553 
554         // Getting Intent properties can only be done after the super.onCreate(...)
555         final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
556 
557         mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
558                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
559 
560         final ComponentName cn = intent.getComponent();
561         final String className = cn.getClassName();
562 
563         mIsShowingDashboard = className.equals(Settings.class.getName());
564 
565         // This is a "Sub Settings" when:
566         // - this is a real SubSettings
567         // - or :settings:show_fragment_as_subsetting is passed to the Intent
568         final boolean isSubSettings = this instanceof SubSettings ||
569                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
570 
571         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
572         if (isSubSettings) {
573             // Check also that we are not a Theme Dialog as we don't want to override them
574             final int themeResId = getThemeResId();
575             if (themeResId != R.style.Theme_DialogWhenLarge &&
576                     themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
577                 setTheme(R.style.Theme_SubSettings);
578             }
579         }
580 
581         setContentView(mIsShowingDashboard ?
582                 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
583 
584         mContent = (ViewGroup) findViewById(R.id.main_content);
585 
586         getFragmentManager().addOnBackStackChangedListener(this);
587 
588         if (mIsShowingDashboard) {
589             // Run the Index update only if we have some space
590             if (!Utils.isLowStorage(this)) {
591                 Index.getInstance(getApplicationContext()).update();
592             } else {
593                 Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
594             }
595         }
596 
597         if (savedState != null) {
598             // We are restarting from a previous saved state; used that to initialize, instead
599             // of starting fresh.
600             mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
601             mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
602 
603             setTitleFromIntent(intent);
604 
605             ArrayList<DashboardCategory> categories =
606                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
607             if (categories != null) {
608                 mCategories.clear();
609                 mCategories.addAll(categories);
610                 setTitleFromBackStack();
611             }
612 
613             mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
614             mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
615             mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
616                     1 /* one home activity by default */);
617         } else {
618             if (!mIsShowingDashboard) {
619                 mDisplaySearch = false;
620                 // UP will be shown only if it is a sub settings
621                 if (mIsShortcut) {
622                     mDisplayHomeAsUpEnabled = isSubSettings;
623                 } else if (isSubSettings) {
624                     mDisplayHomeAsUpEnabled = true;
625                 } else {
626                     mDisplayHomeAsUpEnabled = false;
627                 }
628                 setTitleFromIntent(intent);
629 
630                 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
631                 switchToFragment(initialFragmentName, initialArguments, true, false,
632                         mInitialTitleResId, mInitialTitle, false);
633             } else {
634                 // No UP affordance if we are displaying the main Dashboard
635                 mDisplayHomeAsUpEnabled = false;
636                 // Show Search affordance
637                 mDisplaySearch = true;
638                 mInitialTitleResId = R.string.dashboard_title;
639                 switchToFragment(DashboardSummary.class.getName(), null, false, false,
640                         mInitialTitleResId, mInitialTitle, false);
641             }
642         }
643 
644         mActionBar = getActionBar();
645         if (mActionBar != null) {
646             mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
647             mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
648         }
649         mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
650 
651         // see if we should show Back/Next buttons
652         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
653 
654             View buttonBar = findViewById(R.id.button_bar);
655             if (buttonBar != null) {
656                 buttonBar.setVisibility(View.VISIBLE);
657 
658                 Button backButton = (Button)findViewById(R.id.back_button);
659                 backButton.setOnClickListener(new OnClickListener() {
660                     public void onClick(View v) {
661                         setResult(RESULT_CANCELED, getResultIntentData());
662                         finish();
663                     }
664                 });
665                 Button skipButton = (Button)findViewById(R.id.skip_button);
666                 skipButton.setOnClickListener(new OnClickListener() {
667                     public void onClick(View v) {
668                         setResult(RESULT_OK, getResultIntentData());
669                         finish();
670                     }
671                 });
672                 mNextButton = (Button)findViewById(R.id.next_button);
673                 mNextButton.setOnClickListener(new OnClickListener() {
674                     public void onClick(View v) {
675                         setResult(RESULT_OK, getResultIntentData());
676                         finish();
677                     }
678                 });
679 
680                 // set our various button parameters
681                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
682                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
683                     if (TextUtils.isEmpty(buttonText)) {
684                         mNextButton.setVisibility(View.GONE);
685                     }
686                     else {
687                         mNextButton.setText(buttonText);
688                     }
689                 }
690                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
691                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
692                     if (TextUtils.isEmpty(buttonText)) {
693                         backButton.setVisibility(View.GONE);
694                     }
695                     else {
696                         backButton.setText(buttonText);
697                     }
698                 }
699                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
700                     skipButton.setVisibility(View.VISIBLE);
701                 }
702             }
703         }
704 
705         mHomeActivitiesCount = getHomeActivitiesCount();
706     }
707 
getHomeActivitiesCount()708     private int getHomeActivitiesCount() {
709         final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
710         getPackageManager().getHomeActivities(homeApps);
711         return homeApps.size();
712     }
713 
setTitleFromIntent(Intent intent)714     private void setTitleFromIntent(Intent intent) {
715         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
716         if (initialTitleResId > 0) {
717             mInitialTitle = null;
718             mInitialTitleResId = initialTitleResId;
719 
720             final String initialTitleResPackageName = intent.getStringExtra(
721                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
722             if (initialTitleResPackageName != null) {
723                 try {
724                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
725                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
726                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
727                     setTitle(mInitialTitle);
728                     mInitialTitleResId = -1;
729                     return;
730                 } catch (NameNotFoundException e) {
731                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
732                 }
733             } else {
734                 setTitle(mInitialTitleResId);
735             }
736         } else {
737             mInitialTitleResId = -1;
738             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
739             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
740             setTitle(mInitialTitle);
741         }
742     }
743 
744     @Override
onBackStackChanged()745     public void onBackStackChanged() {
746         setTitleFromBackStack();
747     }
748 
setTitleFromBackStack()749     private int setTitleFromBackStack() {
750         final int count = getFragmentManager().getBackStackEntryCount();
751 
752         if (count == 0) {
753             if (mInitialTitleResId > 0) {
754                 setTitle(mInitialTitleResId);
755             } else {
756                 setTitle(mInitialTitle);
757             }
758             return 0;
759         }
760 
761         FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
762         setTitleFromBackStackEntry(bse);
763 
764         return count;
765     }
766 
setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse)767     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
768         final CharSequence title;
769         final int titleRes = bse.getBreadCrumbTitleRes();
770         if (titleRes > 0) {
771             title = getText(titleRes);
772         } else {
773             title = bse.getBreadCrumbTitle();
774         }
775         if (title != null) {
776             setTitle(title);
777         }
778     }
779 
780     @Override
onSaveInstanceState(Bundle outState)781     protected void onSaveInstanceState(Bundle outState) {
782         super.onSaveInstanceState(outState);
783 
784         if (mCategories.size() > 0) {
785             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
786         }
787 
788         outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
789         outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
790 
791         if (mDisplaySearch) {
792             // The option menus are created if the ActionBar is visible and they are also created
793             // asynchronously. If you launch Settings with an Intent action like
794             // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
795             // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
796             // menu item and search view are null.
797             boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
798             outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
799 
800             String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
801             outState.putString(SAVE_KEY_SEARCH_QUERY, query);
802         }
803 
804         outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount);
805     }
806 
807     @Override
onResume()808     public void onResume() {
809         super.onResume();
810         if (mIsShowingDashboard) {
811             MetricsLogger.visible(this, MetricsLogger.MAIN_SETTINGS);
812         }
813 
814         final int newHomeActivityCount = getHomeActivitiesCount();
815         if (newHomeActivityCount != mHomeActivitiesCount) {
816             mHomeActivitiesCount = newHomeActivityCount;
817             invalidateCategories(true);
818         }
819 
820         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
821             @Override
822             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
823                 invalidateCategories(true);
824             }
825         };
826         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
827                 mDevelopmentPreferencesListener);
828 
829         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
830 
831         mDynamicIndexableContentMonitor.register(this);
832 
833         if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) {
834             onQueryTextSubmit(mSearchQuery);
835         }
836     }
837 
838     @Override
onPause()839     public void onPause() {
840         super.onPause();
841         if (mIsShowingDashboard) {
842             MetricsLogger.hidden(this, MetricsLogger.MAIN_SETTINGS);
843         }
844         unregisterReceiver(mBatteryInfoReceiver);
845         mDynamicIndexableContentMonitor.unregister();
846     }
847 
848     @Override
onDestroy()849     public void onDestroy() {
850         super.onDestroy();
851 
852         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
853                 mDevelopmentPreferencesListener);
854         mDevelopmentPreferencesListener = null;
855     }
856 
isValidFragment(String fragmentName)857     protected boolean isValidFragment(String fragmentName) {
858         // Almost all fragments are wrapped in this,
859         // except for a few that have their own activities.
860         for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
861             if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
862         }
863         return false;
864     }
865 
866     @Override
getIntent()867     public Intent getIntent() {
868         Intent superIntent = super.getIntent();
869         String startingFragment = getStartingFragmentClass(superIntent);
870         // This is called from super.onCreate, isMultiPane() is not yet reliable
871         // Do not use onIsHidingHeaders either, which relies itself on this method
872         if (startingFragment != null) {
873             Intent modIntent = new Intent(superIntent);
874             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
875             Bundle args = superIntent.getExtras();
876             if (args != null) {
877                 args = new Bundle(args);
878             } else {
879                 args = new Bundle();
880             }
881             args.putParcelable("intent", superIntent);
882             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
883             return modIntent;
884         }
885         return superIntent;
886     }
887 
888     /**
889      * Checks if the component name in the intent is different from the Settings class and
890      * returns the class name to load as a fragment.
891      */
getStartingFragmentClass(Intent intent)892     private String getStartingFragmentClass(Intent intent) {
893         if (mFragmentClass != null) return mFragmentClass;
894 
895         String intentClass = intent.getComponent().getClassName();
896         if (intentClass.equals(getClass().getName())) return null;
897 
898         if ("com.android.settings.ManageApplications".equals(intentClass)
899                 || "com.android.settings.RunningServices".equals(intentClass)
900                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
901             // Old names of manage apps.
902             intentClass = com.android.settings.applications.ManageApplications.class.getName();
903         }
904 
905         return intentClass;
906     }
907 
908     /**
909      * Start a new fragment containing a preference panel.  If the preferences
910      * are being displayed in multi-pane mode, the given fragment class will
911      * be instantiated and placed in the appropriate pane.  If running in
912      * single-pane mode, a new activity will be launched in which to show the
913      * fragment.
914      *
915      * @param fragmentClass Full name of the class implementing the fragment.
916      * @param args Any desired arguments to supply to the fragment.
917      * @param titleRes Optional resource identifier of the title of this
918      * fragment.
919      * @param titleText Optional text of the title of this fragment.
920      * @param resultTo Optional fragment that result data should be sent to.
921      * If non-null, resultTo.onActivityResult() will be called when this
922      * preference panel is done.  The launched panel must use
923      * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
924      * @param resultRequestCode If resultTo is non-null, this is the caller's
925      * request code to be received with the result.
926      */
startPreferencePanel(String fragmentClass, Bundle args, int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode)927     public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
928             CharSequence titleText, Fragment resultTo, int resultRequestCode) {
929         String title = null;
930         if (titleRes < 0) {
931             if (titleText != null) {
932                 title = titleText.toString();
933             } else {
934                 // There not much we can do in that case
935                 title = "";
936             }
937         }
938         Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
939                 titleRes, title, mIsShortcut);
940     }
941 
942     /**
943      * Start a new fragment in a new activity containing a preference panel for a given user. If the
944      * preferences are being displayed in multi-pane mode, the given fragment class will be
945      * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
946      * activity will be launched in which to show the fragment.
947      *
948      * @param fragmentClass Full name of the class implementing the fragment.
949      * @param args Any desired arguments to supply to the fragment.
950      * @param titleRes Optional resource identifier of the title of this fragment.
951      * @param titleText Optional text of the title of this fragment.
952      * @param userHandle The user for which the panel has to be started.
953      */
startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes, CharSequence titleText, UserHandle userHandle)954     public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes,
955             CharSequence titleText, UserHandle userHandle) {
956         // This is a workaround.
957         //
958         // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
959         // starting the fragment could cause a native stack corruption. See b/17523189. However,
960         // adding that flag and start the preference panel with the same UserHandler will make it
961         // impossible to use back button to return to the previous screen. See b/20042570.
962         //
963         // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
964         // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
965         // when we're calling it as the same user.
966         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
967             startPreferencePanel(fragmentClass, args, titleRes, titleText, null, 0);
968         } else {
969             String title = null;
970             if (titleRes < 0) {
971                 if (titleText != null) {
972                     title = titleText.toString();
973                 } else {
974                     // There not much we can do in that case
975                     title = "";
976                 }
977             }
978             Utils.startWithFragmentAsUser(this, fragmentClass, args,
979                     titleRes, title, mIsShortcut, userHandle);
980         }
981     }
982 
983     /**
984      * Called by a preference panel fragment to finish itself.
985      *
986      * @param caller The fragment that is asking to be finished.
987      * @param resultCode Optional result code to send back to the original
988      * launching fragment.
989      * @param resultData Optional result data to send back to the original
990      * launching fragment.
991      */
finishPreferencePanel(Fragment caller, int resultCode, Intent resultData)992     public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
993         setResult(resultCode, resultData);
994         finish();
995     }
996 
997     /**
998      * Start a new fragment.
999      *
1000      * @param fragment The fragment to start
1001      * @param push If true, the current fragment will be pushed onto the back stack.  If false,
1002      * the current fragment will be replaced.
1003      */
startPreferenceFragment(Fragment fragment, boolean push)1004     public void startPreferenceFragment(Fragment fragment, boolean push) {
1005         FragmentTransaction transaction = getFragmentManager().beginTransaction();
1006         transaction.replace(R.id.main_content, fragment);
1007         if (push) {
1008             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1009             transaction.addToBackStack(BACK_STACK_PREFS);
1010         } else {
1011             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1012         }
1013         transaction.commitAllowingStateLoss();
1014     }
1015 
1016     /**
1017      * Switch to a specific Fragment with taking care of validation, Title and BackStack
1018      */
switchToFragment(String fragmentName, Bundle args, boolean validate, boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition)1019     private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
1020             boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
1021         if (validate && !isValidFragment(fragmentName)) {
1022             throw new IllegalArgumentException("Invalid fragment for this activity: "
1023                     + fragmentName);
1024         }
1025         Fragment f = Fragment.instantiate(this, fragmentName, args);
1026         FragmentTransaction transaction = getFragmentManager().beginTransaction();
1027         transaction.replace(R.id.main_content, f);
1028         if (withTransition) {
1029             TransitionManager.beginDelayedTransition(mContent);
1030         }
1031         if (addToBackStack) {
1032             transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
1033         }
1034         if (titleResId > 0) {
1035             transaction.setBreadCrumbTitle(titleResId);
1036         } else if (title != null) {
1037             transaction.setBreadCrumbTitle(title);
1038         }
1039         transaction.commitAllowingStateLoss();
1040         getFragmentManager().executePendingTransactions();
1041         return f;
1042     }
1043 
1044     /**
1045      * Called when the activity needs its list of categories/tiles built.
1046      *
1047      * @param categories The list in which to place the tiles categories.
1048      */
buildDashboardCategories(List<DashboardCategory> categories)1049     private void buildDashboardCategories(List<DashboardCategory> categories) {
1050         categories.clear();
1051         loadCategoriesFromResource(R.xml.dashboard_categories, categories, this);
1052         updateTilesList(categories);
1053     }
1054 
1055     /**
1056      * Parse the given XML file as a categories description, adding each
1057      * parsed categories and tiles into the target list.
1058      *
1059      * @param resid The XML resource to load and parse.
1060      * @param target The list in which the parsed categories and tiles should be placed.
1061      */
loadCategoriesFromResource(int resid, List<DashboardCategory> target, Context context)1062     public static void loadCategoriesFromResource(int resid, List<DashboardCategory> target,
1063             Context context) {
1064         XmlResourceParser parser = null;
1065         try {
1066             parser = context.getResources().getXml(resid);
1067             AttributeSet attrs = Xml.asAttributeSet(parser);
1068 
1069             int type;
1070             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1071                     && type != XmlPullParser.START_TAG) {
1072                 // Parse next until start tag is found
1073             }
1074 
1075             String nodeName = parser.getName();
1076             if (!"dashboard-categories".equals(nodeName)) {
1077                 throw new RuntimeException(
1078                         "XML document must start with <preference-categories> tag; found"
1079                                 + nodeName + " at " + parser.getPositionDescription());
1080             }
1081 
1082             Bundle curBundle = null;
1083 
1084             final int outerDepth = parser.getDepth();
1085             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1086                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1087                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1088                     continue;
1089                 }
1090 
1091                 nodeName = parser.getName();
1092                 if ("dashboard-category".equals(nodeName)) {
1093                     DashboardCategory category = new DashboardCategory();
1094 
1095                     TypedArray sa = context.obtainStyledAttributes(
1096                             attrs, com.android.internal.R.styleable.PreferenceHeader);
1097                     category.id = sa.getResourceId(
1098                             com.android.internal.R.styleable.PreferenceHeader_id,
1099                             (int)DashboardCategory.CAT_ID_UNDEFINED);
1100 
1101                     TypedValue tv = sa.peekValue(
1102                             com.android.internal.R.styleable.PreferenceHeader_title);
1103                     if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1104                         if (tv.resourceId != 0) {
1105                             category.titleRes = tv.resourceId;
1106                         } else {
1107                             category.title = tv.string;
1108                         }
1109                     }
1110                     sa.recycle();
1111                     sa = context.obtainStyledAttributes(attrs,
1112                             com.android.internal.R.styleable.Preference);
1113                     tv = sa.peekValue(
1114                             com.android.internal.R.styleable.Preference_key);
1115                     if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1116                         if (tv.resourceId != 0) {
1117                             category.key = context.getString(tv.resourceId);
1118                         } else {
1119                             category.key = tv.string.toString();
1120                         }
1121                     }
1122                     sa.recycle();
1123 
1124                     final int innerDepth = parser.getDepth();
1125                     while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1126                             && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1127                         if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1128                             continue;
1129                         }
1130 
1131                         String innerNodeName = parser.getName();
1132                         if (innerNodeName.equals("dashboard-tile")) {
1133                             DashboardTile tile = new DashboardTile();
1134 
1135                             sa = context.obtainStyledAttributes(
1136                                     attrs, com.android.internal.R.styleable.PreferenceHeader);
1137                             tile.id = sa.getResourceId(
1138                                     com.android.internal.R.styleable.PreferenceHeader_id,
1139                                     (int)TILE_ID_UNDEFINED);
1140                             tv = sa.peekValue(
1141                                     com.android.internal.R.styleable.PreferenceHeader_title);
1142                             if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1143                                 if (tv.resourceId != 0) {
1144                                     tile.titleRes = tv.resourceId;
1145                                 } else {
1146                                     tile.title = tv.string;
1147                                 }
1148                             }
1149                             tv = sa.peekValue(
1150                                     com.android.internal.R.styleable.PreferenceHeader_summary);
1151                             if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1152                                 if (tv.resourceId != 0) {
1153                                     tile.summaryRes = tv.resourceId;
1154                                 } else {
1155                                     tile.summary = tv.string;
1156                                 }
1157                             }
1158                             tile.iconRes = sa.getResourceId(
1159                                     com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1160                             tile.fragment = sa.getString(
1161                                     com.android.internal.R.styleable.PreferenceHeader_fragment);
1162                             sa.recycle();
1163 
1164                             if (curBundle == null) {
1165                                 curBundle = new Bundle();
1166                             }
1167 
1168                             final int innerDepth2 = parser.getDepth();
1169                             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1170                                     && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) {
1171                                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1172                                     continue;
1173                                 }
1174 
1175                                 String innerNodeName2 = parser.getName();
1176                                 if (innerNodeName2.equals("extra")) {
1177                                     context.getResources().parseBundleExtra("extra", attrs,
1178                                             curBundle);
1179                                     XmlUtils.skipCurrentTag(parser);
1180 
1181                                 } else if (innerNodeName2.equals("intent")) {
1182                                     tile.intent = Intent.parseIntent(context.getResources(), parser,
1183                                             attrs);
1184 
1185                                 } else {
1186                                     XmlUtils.skipCurrentTag(parser);
1187                                 }
1188                             }
1189 
1190                             if (curBundle.size() > 0) {
1191                                 tile.fragmentArguments = curBundle;
1192                                 curBundle = null;
1193                             }
1194 
1195                             // Show the SIM Cards setting if there are more than 2 SIMs installed.
1196                             if(tile.id != R.id.sim_settings || Utils.showSimCardTile(context)){
1197                                 category.addTile(tile);
1198                             }
1199 
1200                         } else if (innerNodeName.equals("external-tiles")) {
1201                             category.externalIndex = category.getTilesCount();
1202                         } else {
1203                             XmlUtils.skipCurrentTag(parser);
1204                         }
1205                     }
1206 
1207                     target.add(category);
1208                 } else {
1209                     XmlUtils.skipCurrentTag(parser);
1210                 }
1211             }
1212 
1213         } catch (XmlPullParserException e) {
1214             throw new RuntimeException("Error parsing categories", e);
1215         } catch (IOException e) {
1216             throw new RuntimeException("Error parsing categories", e);
1217         } finally {
1218             if (parser != null) parser.close();
1219         }
1220     }
1221 
updateTilesList(List<DashboardCategory> target)1222     private void updateTilesList(List<DashboardCategory> target) {
1223         final boolean showDev = mDevelopmentPreferences.getBoolean(
1224                 DevelopmentSettings.PREF_SHOW,
1225                 android.os.Build.TYPE.equals("eng"));
1226 
1227         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1228 
1229         final int size = target.size();
1230         for (int i = 0; i < size; i++) {
1231 
1232             DashboardCategory category = target.get(i);
1233 
1234             // Ids are integers, so downcasting is ok
1235             int id = (int) category.id;
1236             int n = category.getTilesCount() - 1;
1237             while (n >= 0) {
1238 
1239                 DashboardTile tile = category.getTile(n);
1240                 boolean removeTile = false;
1241                 id = (int) tile.id;
1242                 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1243                     if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) {
1244                         removeTile = true;
1245                     }
1246                 } else if (id == R.id.wifi_settings) {
1247                     // Remove WiFi Settings if WiFi service is not available.
1248                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1249                         removeTile = true;
1250                     }
1251                 } else if (id == R.id.bluetooth_settings) {
1252                     // Remove Bluetooth Settings if Bluetooth service is not available.
1253                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1254                         removeTile = true;
1255                     }
1256                 } else if (id == R.id.data_usage_settings) {
1257                     // Remove data usage when kernel module not enabled
1258                     if (!Utils.isBandwidthControlEnabled()) {
1259                         removeTile = true;
1260                     }
1261                 } else if (id == R.id.battery_settings) {
1262                     // Remove battery settings when battery is not available. (e.g. TV)
1263 
1264                     if (!mBatteryPresent) {
1265                         removeTile = true;
1266                     }
1267                 } else if (id == R.id.home_settings) {
1268                     if (!updateHomeSettingTiles(tile)) {
1269                         removeTile = true;
1270                     }
1271                 } else if (id == R.id.user_settings) {
1272                     boolean hasMultipleUsers =
1273                             ((UserManager) getSystemService(Context.USER_SERVICE))
1274                                     .getUserCount() > 1;
1275                     if (!UserHandle.MU_ENABLED
1276                             || !UserManager.supportsMultipleUsers()
1277                             || Utils.isMonkeyRunning()) {
1278                         removeTile = true;
1279                     }
1280                 } else if (id == R.id.nfc_payment_settings) {
1281                     if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1282                         removeTile = true;
1283                     } else {
1284                         // Only show if NFC is on and we have the HCE feature
1285                         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1286                         if (adapter == null || !adapter.isEnabled() ||
1287                                 !getPackageManager().hasSystemFeature(
1288                                         PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1289                             removeTile = true;
1290                         }
1291                     }
1292                 } else if (id == R.id.print_settings) {
1293                     boolean hasPrintingSupport = getPackageManager().hasSystemFeature(
1294                             PackageManager.FEATURE_PRINTING);
1295                     if (!hasPrintingSupport) {
1296                         removeTile = true;
1297                     }
1298                 } else if (id == R.id.development_settings) {
1299                     if (!showDev || um.hasUserRestriction(
1300                             UserManager.DISALLOW_DEBUGGING_FEATURES)) {
1301                         removeTile = true;
1302                     }
1303                 }
1304 
1305                 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1306                         && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1307                     removeTile = true;
1308                 }
1309 
1310                 if (removeTile && n < category.getTilesCount()) {
1311                     category.removeTile(n);
1312                 }
1313                 n--;
1314             }
1315         }
1316         addExternalTiles(target);
1317     }
1318 
addExternalTiles(List<DashboardCategory> target)1319     private void addExternalTiles(List<DashboardCategory> target) {
1320         if (Global.getInt(getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
1321             // Don't add external tiles until device is set up.
1322             return;
1323         }
1324         Map<Pair<String, String>, DashboardTile> addedCache =
1325                 new ArrayMap<Pair<String, String>, DashboardTile>();
1326         UserManager userManager = UserManager.get(this);
1327         for (UserHandle user : userManager.getUserProfiles()) {
1328             addExternalTiles(target, user, addedCache);
1329         }
1330     }
1331 
addExternalTiles(List<DashboardCategory> target, UserHandle user, Map<Pair<String, String>, DashboardTile> addedCache)1332     private void addExternalTiles(List<DashboardCategory> target, UserHandle user,
1333             Map<Pair<String, String>, DashboardTile> addedCache) {
1334         PackageManager pm = getPackageManager();
1335         Intent intent = new Intent(EXTRA_SETTINGS_ACTION);
1336         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
1337                 PackageManager.GET_META_DATA, user.getIdentifier());
1338         for (ResolveInfo resolved : results) {
1339             if (!resolved.system) {
1340                 // Do not allow any app to add to settings, only system ones.
1341                 continue;
1342             }
1343             ActivityInfo activityInfo = resolved.activityInfo;
1344             Bundle metaData = activityInfo.metaData;
1345             if ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY)) {
1346                 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for action "
1347                         + EXTRA_SETTINGS_ACTION + " missing metadata " +
1348                         (metaData == null ? "" : EXTRA_CATEGORY_KEY));
1349                 continue;
1350             }
1351             String categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
1352             DashboardCategory category = getCategory(target, categoryKey);
1353             if (category == null) {
1354                 Log.w(LOG_TAG, "Activity " + resolved.activityInfo.name + " has unknown "
1355                         + "category key " + categoryKey);
1356                 continue;
1357             }
1358             Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
1359                     activityInfo.name);
1360             DashboardTile tile = addedCache.get(key);
1361             if (tile == null) {
1362                 tile = new DashboardTile();
1363                 tile.intent = new Intent().setClassName(
1364                         activityInfo.packageName, activityInfo.name);
1365                 Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile);
1366 
1367                 if (category.externalIndex == -1) {
1368                     // If no location for external tiles has been specified for this category,
1369                     // then just put them at the end.
1370                     category.addTile(tile);
1371                 } else {
1372                     category.addTile(category.externalIndex, tile);
1373                 }
1374                 addedCache.put(key, tile);
1375             }
1376             tile.userHandle.add(user);
1377         }
1378     }
1379 
getCategory(List<DashboardCategory> target, String categoryKey)1380     private DashboardCategory getCategory(List<DashboardCategory> target, String categoryKey) {
1381         for (DashboardCategory category : target) {
1382             if (categoryKey.equals(category.key)) {
1383                 return category;
1384             }
1385         }
1386         return null;
1387     }
1388 
updateHomeSettingTiles(DashboardTile tile)1389     private boolean updateHomeSettingTiles(DashboardTile tile) {
1390         // Once we decide to show Home settings, keep showing it forever
1391         SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1392         if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1393             return true;
1394         }
1395 
1396         try {
1397             mHomeActivitiesCount = getHomeActivitiesCount();
1398             if (mHomeActivitiesCount < 2) {
1399                 // When there's only one available home app, omit this settings
1400                 // category entirely at the top level UI.  If the user just
1401                 // uninstalled the penultimate home app candidiate, we also
1402                 // now tell them about why they aren't seeing 'Home' in the list.
1403                 if (sShowNoHomeNotice) {
1404                     sShowNoHomeNotice = false;
1405                     NoHomeDialogFragment.show(this);
1406                 }
1407                 return false;
1408             } else {
1409                 // Okay, we're allowing the Home settings category.  Tell it, when
1410                 // invoked via this front door, that we'll need to be told about the
1411                 // case when the user uninstalls all but one home app.
1412                 if (tile.fragmentArguments == null) {
1413                     tile.fragmentArguments = new Bundle();
1414                 }
1415                 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1416             }
1417         } catch (Exception e) {
1418             // Can't look up the home activity; bail on configuring the icon
1419             Log.w(LOG_TAG, "Problem looking up home activity!", e);
1420         }
1421 
1422         sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1423         return true;
1424     }
1425 
getMetaData()1426     private void getMetaData() {
1427         try {
1428             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1429                     PackageManager.GET_META_DATA);
1430             if (ai == null || ai.metaData == null) return;
1431             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1432         } catch (NameNotFoundException nnfe) {
1433             // No recovery
1434             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1435         }
1436     }
1437 
1438     // give subclasses access to the Next button
hasNextButton()1439     public boolean hasNextButton() {
1440         return mNextButton != null;
1441     }
1442 
getNextButton()1443     public Button getNextButton() {
1444         return mNextButton;
1445     }
1446 
1447     @Override
shouldUpRecreateTask(Intent targetIntent)1448     public boolean shouldUpRecreateTask(Intent targetIntent) {
1449         return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1450     }
1451 
requestHomeNotice()1452     public static void requestHomeNotice() {
1453         sShowNoHomeNotice = true;
1454     }
1455 
1456     @Override
onQueryTextSubmit(String query)1457     public boolean onQueryTextSubmit(String query) {
1458         switchToSearchResultsFragmentIfNeeded();
1459         mSearchQuery = query;
1460         return mSearchResultsFragment.onQueryTextSubmit(query);
1461     }
1462 
1463     @Override
onQueryTextChange(String newText)1464     public boolean onQueryTextChange(String newText) {
1465         mSearchQuery = newText;
1466         if (mSearchResultsFragment == null) {
1467             return false;
1468         }
1469         return mSearchResultsFragment.onQueryTextChange(newText);
1470     }
1471 
1472     @Override
onClose()1473     public boolean onClose() {
1474         return false;
1475     }
1476 
1477     @Override
onMenuItemActionExpand(MenuItem item)1478     public boolean onMenuItemActionExpand(MenuItem item) {
1479         if (item.getItemId() == mSearchMenuItem.getItemId()) {
1480             switchToSearchResultsFragmentIfNeeded();
1481         }
1482         return true;
1483     }
1484 
1485     @Override
onMenuItemActionCollapse(MenuItem item)1486     public boolean onMenuItemActionCollapse(MenuItem item) {
1487         if (item.getItemId() == mSearchMenuItem.getItemId()) {
1488             if (mSearchMenuItemExpanded) {
1489                 revertToInitialFragment();
1490             }
1491         }
1492         return true;
1493     }
1494 
switchToSearchResultsFragmentIfNeeded()1495     private void switchToSearchResultsFragmentIfNeeded() {
1496         if (mSearchResultsFragment != null) {
1497             return;
1498         }
1499         Fragment current = getFragmentManager().findFragmentById(R.id.main_content);
1500         if (current != null && current instanceof SearchResultsSummary) {
1501             mSearchResultsFragment = (SearchResultsSummary) current;
1502         } else {
1503             mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1504                     SearchResultsSummary.class.getName(), null, false, true,
1505                     R.string.search_results_title, null, true);
1506         }
1507         mSearchResultsFragment.setSearchView(mSearchView);
1508         mSearchMenuItemExpanded = true;
1509     }
1510 
needToRevertToInitialFragment()1511     public void needToRevertToInitialFragment() {
1512         mNeedToRevertToInitialFragment = true;
1513     }
1514 
revertToInitialFragment()1515     private void revertToInitialFragment() {
1516         mNeedToRevertToInitialFragment = false;
1517         mSearchResultsFragment = null;
1518         mSearchMenuItemExpanded = false;
1519         getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1520                 FragmentManager.POP_BACK_STACK_INCLUSIVE);
1521         if (mSearchMenuItem != null) {
1522             mSearchMenuItem.collapseActionView();
1523         }
1524     }
1525 
getResultIntentData()1526     public Intent getResultIntentData() {
1527         return mResultIntentData;
1528     }
1529 
setResultIntentData(Intent resultIntentData)1530     public void setResultIntentData(Intent resultIntentData) {
1531         mResultIntentData = resultIntentData;
1532     }
1533 }
1534