• 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.applications.appinfo.AppButtonsPreferenceController.KEY_REMOVE_TASK_WHEN_FINISHING;
20 
21 import android.app.ActionBar;
22 import android.app.ActivityManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.SharedPreferences;
29 import android.content.pm.ActivityInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.PackageManager.NameNotFoundException;
32 import android.content.res.Resources.Theme;
33 import android.graphics.drawable.Icon;
34 import android.os.AsyncTask;
35 import android.os.Bundle;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.text.TextUtils;
39 import android.util.Log;
40 import android.view.View;
41 import android.widget.Button;
42 
43 import androidx.annotation.Nullable;
44 import androidx.annotation.VisibleForTesting;
45 import androidx.fragment.app.Fragment;
46 import androidx.fragment.app.FragmentManager;
47 import androidx.fragment.app.FragmentTransaction;
48 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
49 import androidx.preference.Preference;
50 import androidx.preference.PreferenceFragmentCompat;
51 import androidx.preference.PreferenceManager;
52 
53 import com.android.internal.util.ArrayUtils;
54 import com.android.settings.Settings.WifiSettingsActivity;
55 import com.android.settings.applications.manageapplications.ManageApplications;
56 import com.android.settings.core.OnActivityResultListener;
57 import com.android.settings.core.SettingsBaseActivity;
58 import com.android.settings.core.SubSettingLauncher;
59 import com.android.settings.core.gateway.SettingsGateway;
60 import com.android.settings.dashboard.DashboardFeatureProvider;
61 import com.android.settings.homepage.TopLevelSettings;
62 import com.android.settings.overlay.FeatureFactory;
63 import com.android.settings.wfd.WifiDisplaySettings;
64 import com.android.settings.widget.SettingsMainSwitchBar;
65 import com.android.settingslib.core.instrumentation.Instrumentable;
66 import com.android.settingslib.core.instrumentation.SharedPreferencesLogger;
67 import com.android.settingslib.development.DevelopmentSettingsEnabler;
68 import com.android.settingslib.drawer.DashboardCategory;
69 
70 import com.google.android.setupcompat.util.WizardManagerHelper;
71 
72 import java.util.ArrayList;
73 import java.util.List;
74 
75 
76 public class SettingsActivity extends SettingsBaseActivity
77         implements PreferenceManager.OnPreferenceTreeClickListener,
78         PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
79         ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
80 
81     private static final String LOG_TAG = "SettingsActivity";
82 
83     // Constants for state save/restore
84     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
85 
86     /**
87      * When starting this activity, the invoking Intent can contain this extra
88      * string to specify which fragment should be initially displayed.
89      * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
90      * will call isValidFragment() to confirm that the fragment class name is valid for this
91      * activity.
92      */
93     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
94 
95     /**
96      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
97      * this extra can also be specified to supply a Bundle of arguments to pass
98      * to that fragment when it is instantiated during the initial creation
99      * of the activity.
100      */
101     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
102 
103     /**
104      * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
105      */
106     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
107 
108     // extras that allow any preference activity to be launched as part of a wizard
109 
110     // show Back and Next buttons? takes boolean parameter
111     // Back will then return RESULT_CANCELED and Next RESULT_OK
112     protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
113 
114     // add a Skip button?
115     private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
116 
117     // specify custom text for the Back or Next buttons, or cause a button to not appear
118     // at all by setting it to null
119     protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
120     protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
121 
122     /**
123      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
124      * those extra can also be specify to supply the title or title res id to be shown for
125      * that fragment.
126      */
127     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
128     /**
129      * The package name used to resolve the title resource id.
130      */
131     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
132             ":settings:show_fragment_title_res_package_name";
133     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
134             ":settings:show_fragment_title_resid";
135 
136     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
137             ":settings:show_fragment_as_subsetting";
138 
139     /**
140      * Personal or Work profile tab of {@link ProfileSelectFragment}
141      * <p>0: Personal tab.
142      * <p>1: Work profile tab.
143      */
144     public static final String EXTRA_SHOW_FRAGMENT_TAB =
145             ":settings:show_fragment_tab";
146 
147     public static final String META_DATA_KEY_FRAGMENT_CLASS =
148             "com.android.settings.FRAGMENT_CLASS";
149 
150     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
151 
152     private String mFragmentClass;
153 
154     private CharSequence mInitialTitle;
155     private int mInitialTitleResId;
156 
157     private BroadcastReceiver mDevelopmentSettingsListener;
158 
159     private boolean mBatteryPresent = true;
160     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
161         @Override
162         public void onReceive(Context context, Intent intent) {
163             String action = intent.getAction();
164             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
165                 boolean batteryPresent = Utils.isBatteryPresent(intent);
166 
167                 if (mBatteryPresent != batteryPresent) {
168                     mBatteryPresent = batteryPresent;
169                     updateTilesList();
170                 }
171             }
172         }
173     };
174 
175     private SettingsMainSwitchBar mMainSwitch;
176 
177     private Button mNextButton;
178 
179     // Categories
180     private ArrayList<DashboardCategory> mCategories = new ArrayList<>();
181 
182     private DashboardFeatureProvider mDashboardFeatureProvider;
183 
getSwitchBar()184     public SettingsMainSwitchBar getSwitchBar() {
185         return mMainSwitch;
186     }
187 
188     @Override
onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref)189     public boolean onPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref) {
190         new SubSettingLauncher(this)
191                 .setDestination(pref.getFragment())
192                 .setArguments(pref.getExtras())
193                 .setSourceMetricsCategory(caller instanceof Instrumentable
194                         ? ((Instrumentable) caller).getMetricsCategory()
195                         : Instrumentable.METRICS_CATEGORY_UNKNOWN)
196                 .setTitleRes(-1)
197                 .launch();
198         return true;
199     }
200 
201     @Override
onPreferenceTreeClick(Preference preference)202     public boolean onPreferenceTreeClick(Preference preference) {
203         return false;
204     }
205 
206     @Override
getSharedPreferences(String name, int mode)207     public SharedPreferences getSharedPreferences(String name, int mode) {
208         if (name.equals(getPackageName() + "_preferences")) {
209             return new SharedPreferencesLogger(this, getMetricsTag(),
210                     FeatureFactory.getFactory(this).getMetricsFeatureProvider());
211         }
212         return super.getSharedPreferences(name, mode);
213     }
214 
getMetricsTag()215     private String getMetricsTag() {
216         String tag = null;
217         if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
218             tag = getInitialFragmentName(getIntent());
219         }
220         if (TextUtils.isEmpty(tag)) {
221             Log.w(LOG_TAG, "MetricsTag is invalid " + tag);
222             tag = getClass().getName();
223         }
224         if (tag.startsWith("com.android.settings.")) {
225             tag = tag.replace("com.android.settings.", "");
226         }
227         return tag;
228     }
229 
230     @Override
onCreate(Bundle savedState)231     protected void onCreate(Bundle savedState) {
232         super.onCreate(savedState);
233         Log.d(LOG_TAG, "Starting onCreate");
234         long startTime = System.currentTimeMillis();
235 
236         final FeatureFactory factory = FeatureFactory.getFactory(this);
237 
238         mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
239 
240         // Should happen before any call to getIntent()
241         getMetaData();
242 
243         final Intent intent = getIntent();
244         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
245             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
246         }
247 
248         // Getting Intent properties can only be done after the super.onCreate(...)
249         final String initialFragmentName = getInitialFragmentName(intent);
250 
251         // This is a "Sub Settings" when:
252         // - this is a real SubSettings
253         // - or :settings:show_fragment_as_subsetting is passed to the Intent
254         final boolean isSubSettings = this instanceof SubSettings ||
255                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
256 
257         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
258         // insets.
259         // If this is in setup flow, don't apply theme. Because light theme needs to be applied
260         // in SettingsBaseActivity#onCreate().
261         if (isSubSettings && !WizardManagerHelper.isAnySetupWizard(getIntent())) {
262             setTheme(R.style.Theme_SubSettings);
263         }
264 
265         setContentView(R.layout.settings_main_prefs);
266 
267         getSupportFragmentManager().addOnBackStackChangedListener(this);
268 
269         if (savedState != null) {
270             // We are restarting from a previous saved state; used that to initialize, instead
271             // of starting fresh.
272             setTitleFromIntent(intent);
273 
274             ArrayList<DashboardCategory> categories =
275                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
276             if (categories != null) {
277                 mCategories.clear();
278                 mCategories.addAll(categories);
279                 setTitleFromBackStack();
280             }
281         } else {
282             launchSettingFragment(initialFragmentName, intent);
283         }
284 
285         final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
286 
287         final ActionBar actionBar = getActionBar();
288         if (actionBar != null) {
289             actionBar.setDisplayHomeAsUpEnabled(!isInSetupWizard);
290             actionBar.setHomeButtonEnabled(!isInSetupWizard);
291             actionBar.setDisplayShowTitleEnabled(true);
292         }
293         mMainSwitch = findViewById(R.id.switch_bar);
294         if (mMainSwitch != null) {
295             mMainSwitch.setMetricsTag(getMetricsTag());
296             mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
297         }
298 
299         // see if we should show Back/Next buttons
300         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
301 
302             View buttonBar = findViewById(R.id.button_bar);
303             if (buttonBar != null) {
304                 buttonBar.setVisibility(View.VISIBLE);
305 
306                 Button backButton = findViewById(R.id.back_button);
307                 backButton.setOnClickListener(v -> {
308                     setResult(RESULT_CANCELED, null);
309                     finish();
310                 });
311                 Button skipButton = findViewById(R.id.skip_button);
312                 skipButton.setOnClickListener(v -> {
313                     setResult(RESULT_OK, null);
314                     finish();
315                 });
316                 mNextButton = findViewById(R.id.next_button);
317                 mNextButton.setOnClickListener(v -> {
318                     setResult(RESULT_OK, null);
319                     finish();
320                 });
321 
322                 // set our various button parameters
323                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
324                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
325                     if (TextUtils.isEmpty(buttonText)) {
326                         mNextButton.setVisibility(View.GONE);
327                     } else {
328                         mNextButton.setText(buttonText);
329                     }
330                 }
331                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
332                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
333                     if (TextUtils.isEmpty(buttonText)) {
334                         backButton.setVisibility(View.GONE);
335                     } else {
336                         backButton.setText(buttonText);
337                     }
338                 }
339                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
340                     skipButton.setVisibility(View.VISIBLE);
341                 }
342             }
343         }
344 
345         if (DEBUG_TIMING) {
346             Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
347         }
348     }
349 
350     /** Returns the initial fragment name that the activity will launch. */
351     @VisibleForTesting
getInitialFragmentName(Intent intent)352     public String getInitialFragmentName(Intent intent) {
353         return intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
354     }
355 
356     @Override
onApplyThemeResource(Theme theme, int resid, boolean first)357     protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
358         theme.applyStyle(R.style.SetupWizardPartnerResource, true);
359         super.onApplyThemeResource(theme, resid, first);
360     }
361 
362     @Override
onActivityResult(int requestCode, int resultCode, @Nullable Intent data)363     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
364         super.onActivityResult(requestCode, resultCode, data);
365         final List<Fragment> fragments = getSupportFragmentManager().getFragments();
366         if (fragments != null) {
367             for (Fragment fragment : fragments) {
368                 if (fragment instanceof OnActivityResultListener) {
369                     fragment.onActivityResult(requestCode, resultCode, data);
370                 }
371             }
372         }
373     }
374 
375     @VisibleForTesting
launchSettingFragment(String initialFragmentName, Intent intent)376     void launchSettingFragment(String initialFragmentName, Intent intent) {
377         if (initialFragmentName != null) {
378             setTitleFromIntent(intent);
379 
380             Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
381             switchToFragment(initialFragmentName, initialArguments, true,
382                     mInitialTitleResId, mInitialTitle);
383         } else {
384             // Show search icon as up affordance if we are displaying the main Dashboard
385             mInitialTitleResId = R.string.dashboard_title;
386             switchToFragment(TopLevelSettings.class.getName(), null /* args */, false,
387                     mInitialTitleResId, mInitialTitle);
388         }
389     }
390 
setTitleFromIntent(Intent intent)391     private void setTitleFromIntent(Intent intent) {
392         Log.d(LOG_TAG, "Starting to set activity title");
393         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
394         if (initialTitleResId > 0) {
395             mInitialTitle = null;
396             mInitialTitleResId = initialTitleResId;
397 
398             final String initialTitleResPackageName = intent.getStringExtra(
399                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
400             if (initialTitleResPackageName != null) {
401                 try {
402                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
403                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
404                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
405                     setTitle(mInitialTitle);
406                     mInitialTitleResId = -1;
407                     return;
408                 } catch (NameNotFoundException e) {
409                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
410                 }
411             } else {
412                 setTitle(mInitialTitleResId);
413             }
414         } else {
415             mInitialTitleResId = -1;
416             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
417             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
418             setTitle(mInitialTitle);
419         }
420         Log.d(LOG_TAG, "Done setting title");
421     }
422 
423     @Override
onBackStackChanged()424     public void onBackStackChanged() {
425         setTitleFromBackStack();
426     }
427 
setTitleFromBackStack()428     private void setTitleFromBackStack() {
429         final int count = getSupportFragmentManager().getBackStackEntryCount();
430 
431         if (count == 0) {
432             if (mInitialTitleResId > 0) {
433                 setTitle(mInitialTitleResId);
434             } else {
435                 setTitle(mInitialTitle);
436             }
437             return;
438         }
439 
440         FragmentManager.BackStackEntry bse = getSupportFragmentManager().
441                 getBackStackEntryAt(count - 1);
442         setTitleFromBackStackEntry(bse);
443     }
444 
setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse)445     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
446         final CharSequence title;
447         final int titleRes = bse.getBreadCrumbTitleRes();
448         if (titleRes > 0) {
449             title = getText(titleRes);
450         } else {
451             title = bse.getBreadCrumbTitle();
452         }
453         if (title != null) {
454             setTitle(title);
455         }
456     }
457 
458     @Override
onSaveInstanceState(Bundle outState)459     protected void onSaveInstanceState(Bundle outState) {
460         super.onSaveInstanceState(outState);
461         saveState(outState);
462     }
463 
464     /**
465      * For testing purposes to avoid crashes from final variables in Activity's onSaveInstantState.
466      */
467     @VisibleForTesting
saveState(Bundle outState)468     void saveState(Bundle outState) {
469         if (mCategories.size() > 0) {
470             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
471         }
472     }
473 
474     @Override
onResume()475     protected void onResume() {
476         super.onResume();
477 
478         mDevelopmentSettingsListener = new BroadcastReceiver() {
479             @Override
480             public void onReceive(Context context, Intent intent) {
481                 updateTilesList();
482             }
483         };
484         LocalBroadcastManager.getInstance(this).registerReceiver(mDevelopmentSettingsListener,
485                 new IntentFilter(DevelopmentSettingsEnabler.DEVELOPMENT_SETTINGS_CHANGED_ACTION));
486 
487         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
488 
489         updateTilesList();
490     }
491 
492     @Override
onPause()493     protected void onPause() {
494         super.onPause();
495         LocalBroadcastManager.getInstance(this).unregisterReceiver(mDevelopmentSettingsListener);
496         mDevelopmentSettingsListener = null;
497         unregisterReceiver(mBatteryInfoReceiver);
498     }
499 
500     @Override
setTaskDescription(ActivityManager.TaskDescription taskDescription)501     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
502         taskDescription.setIcon(Icon.createWithResource(this, R.drawable.ic_launcher_settings));
503         super.setTaskDescription(taskDescription);
504     }
505 
isValidFragment(String fragmentName)506     protected boolean isValidFragment(String fragmentName) {
507         // Almost all fragments are wrapped in this,
508         // except for a few that have their own activities.
509         for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) {
510             if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
511         }
512         return false;
513     }
514 
515     @Override
getIntent()516     public Intent getIntent() {
517         Intent superIntent = super.getIntent();
518         String startingFragment = getStartingFragmentClass(superIntent);
519         // This is called from super.onCreate, isMultiPane() is not yet reliable
520         // Do not use onIsHidingHeaders either, which relies itself on this method
521         if (startingFragment != null) {
522             Intent modIntent = new Intent(superIntent);
523             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
524             Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
525             if (args != null) {
526                 args = new Bundle(args);
527             } else {
528                 args = new Bundle();
529             }
530             args.putParcelable("intent", superIntent);
531             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
532             return modIntent;
533         }
534         return superIntent;
535     }
536 
537     /**
538      * Checks if the component name in the intent is different from the Settings class and
539      * returns the class name to load as a fragment.
540      */
getStartingFragmentClass(Intent intent)541     private String getStartingFragmentClass(Intent intent) {
542         if (mFragmentClass != null) return mFragmentClass;
543 
544         String intentClass = intent.getComponent().getClassName();
545         if (intentClass.equals(getClass().getName())) return null;
546 
547         if ("com.android.settings.RunningServices".equals(intentClass)
548                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
549             // Old names of manage apps.
550             intentClass = ManageApplications.class.getName();
551         }
552 
553         return intentClass;
554     }
555 
556     /**
557      * Called by a preference panel fragment to finish itself.
558      *
559      * @param resultCode Optional result code to send back to the original
560      *                   launching fragment.
561      * @param resultData Optional result data to send back to the original
562      *                   launching fragment.
563      */
finishPreferencePanel(int resultCode, Intent resultData)564     public void finishPreferencePanel(int resultCode, Intent resultData) {
565         setResult(resultCode, resultData);
566         if (resultData != null &&
567                 resultData.getBooleanExtra(KEY_REMOVE_TASK_WHEN_FINISHING, false)) {
568             finishAndRemoveTask();
569         } else {
570             finish();
571         }
572     }
573 
574     /**
575      * Switch to a specific Fragment with taking care of validation, Title and BackStack
576      */
switchToFragment(String fragmentName, Bundle args, boolean validate, int titleResId, CharSequence title)577     private void switchToFragment(String fragmentName, Bundle args, boolean validate,
578             int titleResId, CharSequence title) {
579         Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
580         if (validate && !isValidFragment(fragmentName)) {
581             throw new IllegalArgumentException("Invalid fragment for this activity: "
582                     + fragmentName);
583         }
584         Fragment f = Utils.getTargetFragment(this, fragmentName, args);
585         if (f == null) {
586             return;
587         }
588         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
589         transaction.replace(R.id.main_content, f);
590         if (titleResId > 0) {
591             transaction.setBreadCrumbTitle(titleResId);
592         } else if (title != null) {
593             transaction.setBreadCrumbTitle(title);
594         }
595         transaction.commitAllowingStateLoss();
596         getSupportFragmentManager().executePendingTransactions();
597         Log.d(LOG_TAG, "Executed frag manager pendingTransactions");
598     }
599 
updateTilesList()600     private void updateTilesList() {
601         // Generally the items that are will be changing from these updates will
602         // not be in the top list of tiles, so run it in the background and the
603         // SettingsBaseActivity will pick up on the updates automatically.
604         AsyncTask.execute(() -> doUpdateTilesList());
605     }
606 
doUpdateTilesList()607     private void doUpdateTilesList() {
608         PackageManager pm = getPackageManager();
609         final UserManager um = UserManager.get(this);
610         final boolean isAdmin = um.isAdminUser();
611         boolean somethingChanged = false;
612         final String packageName = getPackageName();
613         final StringBuilder changedList = new StringBuilder();
614         somethingChanged = setTileEnabled(changedList,
615                 new ComponentName(packageName, WifiSettingsActivity.class.getName()),
616                 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin) || somethingChanged;
617 
618         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
619                         Settings.BluetoothSettingsActivity.class.getName()),
620                 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin)
621                 || somethingChanged;
622 
623         // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise
624         // enable DataPlanUsageSummaryActivity.
625         somethingChanged = setTileEnabled(changedList,
626                 new ComponentName(packageName, Settings.DataUsageSummaryActivity.class.getName()),
627                 Utils.isBandwidthControlEnabled() /* enabled */,
628                 isAdmin) || somethingChanged;
629 
630         somethingChanged = setTileEnabled(changedList,
631                 new ComponentName(packageName,
632                         Settings.ConnectedDeviceDashboardActivity.class.getName()),
633                 !UserManager.isDeviceInDemoMode(this) /* enabled */,
634                 isAdmin) || somethingChanged;
635 
636         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
637                         Settings.PowerUsageSummaryActivity.class.getName()),
638                 mBatteryPresent, isAdmin) || somethingChanged;
639 
640         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
641                         Settings.DataUsageSummaryActivity.class.getName()),
642                 Utils.isBandwidthControlEnabled(), isAdmin)
643                 || somethingChanged;
644 
645         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
646                         Settings.UserSettingsActivity.class.getName()),
647                 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
648                         && !Utils.isMonkeyRunning(), isAdmin)
649                 || somethingChanged;
650 
651         final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
652                 && !Utils.isMonkeyRunning();
653         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
654                         Settings.DevelopmentSettingsDashboardActivity.class.getName()),
655                 showDev, isAdmin)
656                 || somethingChanged;
657 
658         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
659                         Settings.WifiDisplaySettingsActivity.class.getName()),
660                 WifiDisplaySettings.isAvailable(this), isAdmin)
661                 || somethingChanged;
662 
663         if (UserHandle.MU_ENABLED && !isAdmin) {
664             // When on restricted users, disable all extra categories (but only the settings ones).
665             final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
666             synchronized (categories) {
667                 for (DashboardCategory category : categories) {
668                     final int tileCount = category.getTilesCount();
669                     for (int i = 0; i < tileCount; i++) {
670                         final ComponentName component = category.getTile(i)
671                                 .getIntent().getComponent();
672                         final String name = component.getClassName();
673                         final boolean isEnabledForRestricted = ArrayUtils.contains(
674                                 SettingsGateway.SETTINGS_FOR_RESTRICTED, name);
675                         if (packageName.equals(component.getPackageName())
676                                 && !isEnabledForRestricted) {
677                             somethingChanged =
678                                     setTileEnabled(changedList, component, false, isAdmin)
679                                             || somethingChanged;
680                         }
681                     }
682                 }
683             }
684         }
685 
686         // Final step, refresh categories.
687         if (somethingChanged) {
688             Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
689                     + changedList.toString());
690             mCategoryMixin.updateCategories();
691         } else {
692             Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
693         }
694     }
695 
696     /**
697      * @return whether or not the enabled state actually changed.
698      */
setTileEnabled(StringBuilder changedList, ComponentName component, boolean enabled, boolean isAdmin)699     private boolean setTileEnabled(StringBuilder changedList, ComponentName component,
700             boolean enabled, boolean isAdmin) {
701         if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
702                 && !ArrayUtils.contains(SettingsGateway.SETTINGS_FOR_RESTRICTED,
703                 component.getClassName())) {
704             enabled = false;
705         }
706         boolean changed = setTileEnabled(component, enabled);
707         if (changed) {
708             changedList.append(component.toShortString()).append(",");
709         }
710         return changed;
711     }
712 
getMetaData()713     private void getMetaData() {
714         try {
715             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
716                     PackageManager.GET_META_DATA);
717             if (ai == null || ai.metaData == null) return;
718             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
719         } catch (NameNotFoundException nnfe) {
720             // No recovery
721             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
722         }
723     }
724 
725     // give subclasses access to the Next button
hasNextButton()726     public boolean hasNextButton() {
727         return mNextButton != null;
728     }
729 
getNextButton()730     public Button getNextButton() {
731         return mNextButton;
732     }
733 }
734