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