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 android.app.ActionBar;
     20 import android.app.ActivityManager;
     21 import android.app.Fragment;
     22 import android.app.FragmentManager;
     23 import android.app.FragmentTransaction;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.SharedPreferences;
     30 import android.content.pm.ActivityInfo;
     31 import android.content.pm.PackageManager;
     32 import android.content.pm.PackageManager.NameNotFoundException;
     33 import android.graphics.Bitmap;
     34 import android.graphics.Canvas;
     35 import android.graphics.drawable.Drawable;
     36 import android.nfc.NfcAdapter;
     37 import android.os.AsyncTask;
     38 import android.os.Bundle;
     39 import android.os.UserHandle;
     40 import android.os.UserManager;
     41 import android.support.annotation.VisibleForTesting;
     42 import android.support.v14.preference.PreferenceFragment;
     43 import android.support.v7.preference.Preference;
     44 import android.support.v7.preference.PreferenceManager;
     45 import android.text.TextUtils;
     46 import android.transition.TransitionManager;
     47 import android.util.Log;
     48 import android.view.Menu;
     49 import android.view.View;
     50 import android.view.View.OnClickListener;
     51 import android.view.ViewGroup;
     52 import android.widget.Button;
     53 
     54 import com.android.internal.util.ArrayUtils;
     55 import com.android.settings.Settings.WifiSettingsActivity;
     56 import com.android.settings.backup.BackupSettingsActivity;
     57 import com.android.settings.core.gateway.SettingsGateway;
     58 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
     59 import com.android.settings.core.instrumentation.SharedPreferencesLogger;
     60 import com.android.settings.dashboard.DashboardFeatureProvider;
     61 import com.android.settings.dashboard.DashboardSummary;
     62 import com.android.settings.development.DevelopmentSettings;
     63 import com.android.settings.overlay.FeatureFactory;
     64 import com.android.settings.search.DynamicIndexableContentMonitor;
     65 import com.android.settings.search2.SearchFeatureProvider;
     66 import com.android.settings.wfd.WifiDisplaySettings;
     67 import com.android.settings.widget.SwitchBar;
     68 import com.android.settingslib.drawer.DashboardCategory;
     69 import com.android.settingslib.drawer.SettingsDrawerActivity;
     70 
     71 import java.util.ArrayList;
     72 import java.util.List;
     73 import java.util.Set;
     74 
     75 public class SettingsActivity extends SettingsDrawerActivity
     76         implements PreferenceManager.OnPreferenceTreeClickListener,
     77         PreferenceFragment.OnPreferenceStartFragmentCallback,
     78         ButtonBarHandler, FragmentManager.OnBackStackChangedListener {
     79 
     80     private static final String LOG_TAG = "Settings";
     81 
     82     public static final int LOADER_ID_INDEXABLE_CONTENT_MONITOR = 1;
     83 
     84     // Constants for state save/restore
     85     private static final String SAVE_KEY_CATEGORIES = ":settings:categories";
     86     @VisibleForTesting
     87     static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
     88     @VisibleForTesting
     89     static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search";
     90 
     91     /**
     92      * When starting this activity, the invoking Intent can contain this extra
     93      * string to specify which fragment should be initially displayed.
     94      * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
     95      * will call isValidFragment() to confirm that the fragment class name is valid for this
     96      * activity.
     97      */
     98     public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
     99 
    100     /**
    101      * The metrics category constant for logging source when a setting fragment is opened.
    102      */
    103     public static final String EXTRA_SOURCE_METRICS_CATEGORY = ":settings:source_metrics";
    104 
    105     /**
    106      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
    107      * this extra can also be specified to supply a Bundle of arguments to pass
    108      * to that fragment when it is instantiated during the initial creation
    109      * of the activity.
    110      */
    111     public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
    112 
    113     /**
    114      * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
    115      */
    116     public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
    117 
    118     public static final String BACK_STACK_PREFS = ":settings:prefs";
    119 
    120     // extras that allow any preference activity to be launched as part of a wizard
    121 
    122     // show Back and Next buttons? takes boolean parameter
    123     // Back will then return RESULT_CANCELED and Next RESULT_OK
    124     protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
    125 
    126     // add a Skip button?
    127     private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
    128 
    129     // specify custom text for the Back or Next buttons, or cause a button to not appear
    130     // at all by setting it to null
    131     protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
    132     protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
    133 
    134     /**
    135      * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
    136      * those extra can also be specify to supply the title or title res id to be shown for
    137      * that fragment.
    138      */
    139     public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
    140     /**
    141      * The package name used to resolve the title resource id.
    142      */
    143     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME =
    144             ":settings:show_fragment_title_res_package_name";
    145     public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
    146             ":settings:show_fragment_title_resid";
    147     public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT =
    148             ":settings:show_fragment_as_shortcut";
    149 
    150     public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
    151             ":settings:show_fragment_as_subsetting";
    152 
    153     @Deprecated
    154     public static final String EXTRA_HIDE_DRAWER = ":settings:hide_drawer";
    155 
    156     public static final String META_DATA_KEY_FRAGMENT_CLASS =
    157         "com.android.settings.FRAGMENT_CLASS";
    158 
    159     private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
    160 
    161     private static final int REQUEST_SUGGESTION = 42;
    162 
    163     private String mFragmentClass;
    164 
    165     private CharSequence mInitialTitle;
    166     private int mInitialTitleResId;
    167 
    168     private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = {
    169             "android.settings.APPLICATION_DETAILS_SETTINGS"
    170     };
    171 
    172     private SharedPreferences mDevelopmentPreferences;
    173     private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
    174 
    175     private boolean mBatteryPresent = true;
    176     private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
    177         @Override
    178         public void onReceive(Context context, Intent intent) {
    179             String action = intent.getAction();
    180             if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
    181                 boolean batteryPresent = Utils.isBatteryPresent(intent);
    182 
    183                 if (mBatteryPresent != batteryPresent) {
    184                     mBatteryPresent = batteryPresent;
    185                     updateTilesList();
    186                 }
    187             }
    188         }
    189     };
    190 
    191     private DynamicIndexableContentMonitor mDynamicIndexableContentMonitor;
    192 
    193     private ActionBar mActionBar;
    194     private SwitchBar mSwitchBar;
    195 
    196     private Button mNextButton;
    197 
    198     @VisibleForTesting
    199     boolean mDisplayHomeAsUpEnabled;
    200     @VisibleForTesting
    201     boolean mDisplaySearch;
    202 
    203     private boolean mIsShowingDashboard;
    204     private boolean mIsShortcut;
    205 
    206     private ViewGroup mContent;
    207 
    208     private SearchFeatureProvider mSearchFeatureProvider;
    209     private MetricsFeatureProvider mMetricsFeatureProvider;
    210 
    211     // Categories
    212     private ArrayList<DashboardCategory> mCategories = new ArrayList<>();
    213 
    214     private DashboardFeatureProvider mDashboardFeatureProvider;
    215     private ComponentName mCurrentSuggestion;
    216 
    217     public SwitchBar getSwitchBar() {
    218         return mSwitchBar;
    219     }
    220 
    221     @Override
    222     public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
    223         startPreferencePanel(caller, pref.getFragment(), pref.getExtras(), -1, pref.getTitle(),
    224                 null, 0);
    225         return true;
    226     }
    227 
    228     @Override
    229     public boolean onPreferenceTreeClick(Preference preference) {
    230         return false;
    231     }
    232 
    233     @Override
    234     public boolean onCreateOptionsMenu(Menu menu) {
    235         if (!mDisplaySearch) {
    236             return false;
    237         }
    238         mSearchFeatureProvider.setUpSearchMenu(menu, this);
    239         return true;
    240     }
    241 
    242     @Override
    243     public SharedPreferences getSharedPreferences(String name, int mode) {
    244         if (name.equals(getPackageName() + "_preferences")) {
    245             return new SharedPreferencesLogger(this, getMetricsTag());
    246         }
    247         return super.getSharedPreferences(name, mode);
    248     }
    249 
    250     private String getMetricsTag() {
    251         String tag = getClass().getName();
    252         if (getIntent() != null && getIntent().hasExtra(EXTRA_SHOW_FRAGMENT)) {
    253             tag = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
    254         }
    255         if (tag.startsWith("com.android.settings.")) {
    256             tag = tag.replace("com.android.settings.", "");
    257         }
    258         return tag;
    259     }
    260 
    261     private static boolean isShortCutIntent(final Intent intent) {
    262         Set<String> categories = intent.getCategories();
    263         return (categories != null) && categories.contains("com.android.settings.SHORTCUT");
    264     }
    265 
    266     private static boolean isLikeShortCutIntent(final Intent intent) {
    267         String action = intent.getAction();
    268         if (action == null) {
    269             return false;
    270         }
    271         for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) {
    272             if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true;
    273         }
    274         return false;
    275     }
    276 
    277     @Override
    278     protected void onCreate(Bundle savedState) {
    279         super.onCreate(savedState);
    280         long startTime = System.currentTimeMillis();
    281 
    282         final FeatureFactory factory = FeatureFactory.getFactory(this);
    283 
    284         mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
    285         mSearchFeatureProvider = factory.getSearchFeatureProvider();
    286         mMetricsFeatureProvider = factory.getMetricsFeatureProvider();
    287 
    288         // Should happen before any call to getIntent()
    289         getMetaData();
    290 
    291         final Intent intent = getIntent();
    292         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
    293             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
    294         }
    295 
    296         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
    297                 Context.MODE_PRIVATE);
    298 
    299         // Getting Intent properties can only be done after the super.onCreate(...)
    300         final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
    301 
    302         mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
    303                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
    304 
    305         final ComponentName cn = intent.getComponent();
    306         final String className = cn.getClassName();
    307 
    308         mIsShowingDashboard = className.equals(Settings.class.getName());
    309 
    310         // This is a "Sub Settings" when:
    311         // - this is a real SubSettings
    312         // - or :settings:show_fragment_as_subsetting is passed to the Intent
    313         final boolean isSubSettings = this instanceof SubSettings ||
    314                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
    315 
    316         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
    317         // insets
    318         if (isSubSettings) {
    319             setTheme(R.style.Theme_SubSettings);
    320         }
    321 
    322         setContentView(mIsShowingDashboard ?
    323                 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
    324 
    325         mContent = (ViewGroup) findViewById(R.id.main_content);
    326 
    327         getFragmentManager().addOnBackStackChangedListener(this);
    328 
    329         if (savedState != null) {
    330             // We are restarting from a previous saved state; used that to initialize, instead
    331             // of starting fresh.
    332             setTitleFromIntent(intent);
    333 
    334             ArrayList<DashboardCategory> categories =
    335                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
    336             if (categories != null) {
    337                 mCategories.clear();
    338                 mCategories.addAll(categories);
    339                 setTitleFromBackStack();
    340             }
    341 
    342             mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
    343 
    344         } else {
    345             launchSettingFragment(initialFragmentName, isSubSettings, intent);
    346         }
    347 
    348         mActionBar = getActionBar();
    349         if (mActionBar != null) {
    350             mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
    351             mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
    352         }
    353         mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
    354         if (mSwitchBar != null) {
    355             mSwitchBar.setMetricsTag(getMetricsTag());
    356         }
    357 
    358         // see if we should show Back/Next buttons
    359         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
    360 
    361             View buttonBar = findViewById(R.id.button_bar);
    362             if (buttonBar != null) {
    363                 buttonBar.setVisibility(View.VISIBLE);
    364 
    365                 Button backButton = (Button)findViewById(R.id.back_button);
    366                 backButton.setOnClickListener(new OnClickListener() {
    367                     public void onClick(View v) {
    368                         setResult(RESULT_CANCELED, null);
    369                         finish();
    370                     }
    371                 });
    372                 Button skipButton = (Button)findViewById(R.id.skip_button);
    373                 skipButton.setOnClickListener(new OnClickListener() {
    374                     public void onClick(View v) {
    375                         setResult(RESULT_OK, null);
    376                         finish();
    377                     }
    378                 });
    379                 mNextButton = (Button)findViewById(R.id.next_button);
    380                 mNextButton.setOnClickListener(new OnClickListener() {
    381                     public void onClick(View v) {
    382                         setResult(RESULT_OK, null);
    383                         finish();
    384                     }
    385                 });
    386 
    387                 // set our various button parameters
    388                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
    389                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
    390                     if (TextUtils.isEmpty(buttonText)) {
    391                         mNextButton.setVisibility(View.GONE);
    392                     }
    393                     else {
    394                         mNextButton.setText(buttonText);
    395                     }
    396                 }
    397                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
    398                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
    399                     if (TextUtils.isEmpty(buttonText)) {
    400                         backButton.setVisibility(View.GONE);
    401                     }
    402                     else {
    403                         backButton.setText(buttonText);
    404                     }
    405                 }
    406                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
    407                     skipButton.setVisibility(View.VISIBLE);
    408                 }
    409             }
    410         }
    411 
    412         if (DEBUG_TIMING) {
    413             Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
    414         }
    415     }
    416 
    417     @VisibleForTesting
    418     void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
    419         if (!mIsShowingDashboard && initialFragmentName != null) {
    420             mDisplaySearch = false;
    421             // UP will be shown only if it is a sub settings
    422             if (mIsShortcut) {
    423                 mDisplayHomeAsUpEnabled = isSubSettings;
    424             } else if (isSubSettings) {
    425                 mDisplayHomeAsUpEnabled = true;
    426             } else {
    427                 mDisplayHomeAsUpEnabled = false;
    428             }
    429             setTitleFromIntent(intent);
    430 
    431             Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
    432             switchToFragment(initialFragmentName, initialArguments, true, false,
    433                 mInitialTitleResId, mInitialTitle, false);
    434         } else {
    435             // No UP affordance if we are displaying the main Dashboard
    436             mDisplayHomeAsUpEnabled = false;
    437             // Show Search affordance
    438             mDisplaySearch = true;
    439             mInitialTitleResId = R.string.dashboard_title;
    440 
    441             switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
    442                 mInitialTitleResId, mInitialTitle, false);
    443         }
    444     }
    445 
    446     public void setDisplaySearchMenu(boolean displaySearch) {
    447         if (displaySearch != mDisplaySearch) {
    448             mDisplaySearch = displaySearch;
    449             invalidateOptionsMenu();
    450         }
    451     }
    452 
    453     private void setTitleFromIntent(Intent intent) {
    454         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
    455         if (initialTitleResId > 0) {
    456             mInitialTitle = null;
    457             mInitialTitleResId = initialTitleResId;
    458 
    459             final String initialTitleResPackageName = intent.getStringExtra(
    460                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
    461             if (initialTitleResPackageName != null) {
    462                 try {
    463                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
    464                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
    465                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
    466                     setTitle(mInitialTitle);
    467                     mInitialTitleResId = -1;
    468                     return;
    469                 } catch (NameNotFoundException e) {
    470                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
    471                 }
    472             } else {
    473                 setTitle(mInitialTitleResId);
    474             }
    475         } else {
    476             mInitialTitleResId = -1;
    477             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
    478             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
    479             setTitle(mInitialTitle);
    480         }
    481     }
    482 
    483     @Override
    484     public void onBackStackChanged() {
    485         setTitleFromBackStack();
    486     }
    487 
    488     private void setTitleFromBackStack() {
    489         final int count = getFragmentManager().getBackStackEntryCount();
    490 
    491         if (count == 0) {
    492             if (mInitialTitleResId > 0) {
    493                 setTitle(mInitialTitleResId);
    494             } else {
    495                 setTitle(mInitialTitle);
    496             }
    497             return;
    498         }
    499 
    500         FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
    501         setTitleFromBackStackEntry(bse);
    502     }
    503 
    504     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
    505         final CharSequence title;
    506         final int titleRes = bse.getBreadCrumbTitleRes();
    507         if (titleRes > 0) {
    508             title = getText(titleRes);
    509         } else {
    510             title = bse.getBreadCrumbTitle();
    511         }
    512         if (title != null) {
    513             setTitle(title);
    514         }
    515     }
    516 
    517     @Override
    518     protected void onSaveInstanceState(Bundle outState) {
    519         super.onSaveInstanceState(outState);
    520         saveState(outState);
    521     }
    522 
    523     /**
    524      * For testing purposes to avoid crashes from final variables in Activity's onSaveInstantState.
    525      */
    526     @VisibleForTesting
    527     void saveState(Bundle outState) {
    528         if (mCategories.size() > 0) {
    529             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
    530         }
    531 
    532         outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
    533         outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch);
    534     }
    535 
    536     @Override
    537     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    538         super.onRestoreInstanceState(savedInstanceState);
    539 
    540         mDisplayHomeAsUpEnabled = savedInstanceState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
    541         mDisplaySearch = savedInstanceState.getBoolean(SAVE_KEY_SHOW_SEARCH);
    542     }
    543 
    544     @Override
    545     protected void onResume() {
    546         super.onResume();
    547 
    548         mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
    549             @Override
    550             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    551                 updateTilesList();
    552             }
    553         };
    554         mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
    555                 mDevelopmentPreferencesListener);
    556 
    557         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
    558         if (mDynamicIndexableContentMonitor == null) {
    559             mDynamicIndexableContentMonitor = new DynamicIndexableContentMonitor();
    560         }
    561         mDynamicIndexableContentMonitor.register(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
    562 
    563         updateTilesList();
    564     }
    565 
    566     @Override
    567     protected void onPause() {
    568         super.onPause();
    569         mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
    570                 mDevelopmentPreferencesListener);
    571         mDevelopmentPreferencesListener = null;
    572         unregisterReceiver(mBatteryInfoReceiver);
    573         if (mDynamicIndexableContentMonitor != null) {
    574             mDynamicIndexableContentMonitor.unregister(this, LOADER_ID_INDEXABLE_CONTENT_MONITOR);
    575         }
    576     }
    577 
    578     @Override
    579     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
    580         final Bitmap icon = getBitmapFromXmlResource(R.drawable.ic_launcher_settings);
    581         taskDescription.setIcon(icon);
    582         super.setTaskDescription(taskDescription);
    583     }
    584 
    585     protected boolean isValidFragment(String fragmentName) {
    586         // Almost all fragments are wrapped in this,
    587         // except for a few that have their own activities.
    588         for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) {
    589             if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
    590         }
    591         return false;
    592     }
    593 
    594     @Override
    595     public Intent getIntent() {
    596         Intent superIntent = super.getIntent();
    597         String startingFragment = getStartingFragmentClass(superIntent);
    598         // This is called from super.onCreate, isMultiPane() is not yet reliable
    599         // Do not use onIsHidingHeaders either, which relies itself on this method
    600         if (startingFragment != null) {
    601             Intent modIntent = new Intent(superIntent);
    602             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
    603             Bundle args = superIntent.getExtras();
    604             if (args != null) {
    605                 args = new Bundle(args);
    606             } else {
    607                 args = new Bundle();
    608             }
    609             args.putParcelable("intent", superIntent);
    610             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
    611             return modIntent;
    612         }
    613         return superIntent;
    614     }
    615 
    616     /**
    617      * Checks if the component name in the intent is different from the Settings class and
    618      * returns the class name to load as a fragment.
    619      */
    620     private String getStartingFragmentClass(Intent intent) {
    621         if (mFragmentClass != null) return mFragmentClass;
    622 
    623         String intentClass = intent.getComponent().getClassName();
    624         if (intentClass.equals(getClass().getName())) return null;
    625 
    626         if ("com.android.settings.ManageApplications".equals(intentClass)
    627                 || "com.android.settings.RunningServices".equals(intentClass)
    628                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
    629             // Old names of manage apps.
    630             intentClass = com.android.settings.applications.ManageApplications.class.getName();
    631         }
    632 
    633         return intentClass;
    634     }
    635 
    636     /**
    637      * Start a new fragment containing a preference panel.  If the preferences
    638      * are being displayed in multi-pane mode, the given fragment class will
    639      * be instantiated and placed in the appropriate pane.  If running in
    640      * single-pane mode, a new activity will be launched in which to show the
    641      * fragment.
    642      *
    643      * @param fragmentClass Full name of the class implementing the fragment.
    644      * @param args Any desired arguments to supply to the fragment.
    645      * @param titleRes Optional resource identifier of the title of this
    646      * fragment.
    647      * @param titleText Optional text of the title of this fragment.
    648      * @param resultTo Optional fragment that result data should be sent to.
    649      * If non-null, resultTo.onActivityResult() will be called when this
    650      * preference panel is done.  The launched panel must use
    651      * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
    652      * @param resultRequestCode If resultTo is non-null, this is the caller's
    653      * request code to be received with the result.
    654      */
    655     public void startPreferencePanel(Fragment caller, String fragmentClass, Bundle args,
    656             int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode) {
    657         String title = null;
    658         if (titleRes < 0) {
    659             if (titleText != null) {
    660                 title = titleText.toString();
    661             } else {
    662                 // There not much we can do in that case
    663                 title = "";
    664             }
    665         }
    666         Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode,
    667                 titleRes, title, mIsShortcut, mMetricsFeatureProvider.getMetricsCategory(caller));
    668     }
    669 
    670     /**
    671      * Start a new fragment in a new activity containing a preference panel for a given user. If the
    672      * preferences are being displayed in multi-pane mode, the given fragment class will be
    673      * instantiated and placed in the appropriate pane. If running in single-pane mode, a new
    674      * activity will be launched in which to show the fragment.
    675      *
    676      * @param fragmentClass Full name of the class implementing the fragment.
    677      * @param args Any desired arguments to supply to the fragment.
    678      * @param titleRes Optional resource identifier of the title of this fragment.
    679      * @param titleText Optional text of the title of this fragment.
    680      * @param userHandle The user for which the panel has to be started.
    681      */
    682     public void startPreferencePanelAsUser(Fragment caller, String fragmentClass,
    683             Bundle args, int titleRes, CharSequence titleText, UserHandle userHandle) {
    684         // This is a workaround.
    685         //
    686         // Calling startWithFragmentAsUser() without specifying FLAG_ACTIVITY_NEW_TASK to the intent
    687         // starting the fragment could cause a native stack corruption. See b/17523189. However,
    688         // adding that flag and start the preference panel with the same UserHandler will make it
    689         // impossible to use back button to return to the previous screen. See b/20042570.
    690         //
    691         // We work around this issue by adding FLAG_ACTIVITY_NEW_TASK to the intent, while doing
    692         // another check here to call startPreferencePanel() instead of startWithFragmentAsUser()
    693         // when we're calling it as the same user.
    694         if (userHandle.getIdentifier() == UserHandle.myUserId()) {
    695             startPreferencePanel(caller, fragmentClass, args, titleRes, titleText, null, 0);
    696         } else {
    697             String title = null;
    698             if (titleRes < 0) {
    699                 if (titleText != null) {
    700                     title = titleText.toString();
    701                 } else {
    702                     // There not much we can do in that case
    703                     title = "";
    704                 }
    705             }
    706             Utils.startWithFragmentAsUser(this, fragmentClass, args, titleRes, title,
    707                     mIsShortcut, mMetricsFeatureProvider.getMetricsCategory(caller), userHandle);
    708         }
    709     }
    710 
    711     /**
    712      * Called by a preference panel fragment to finish itself.
    713      *
    714      * @param caller The fragment that is asking to be finished.
    715      * @param resultCode Optional result code to send back to the original
    716      * launching fragment.
    717      * @param resultData Optional result data to send back to the original
    718      * launching fragment.
    719      */
    720     public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
    721         setResult(resultCode, resultData);
    722         finish();
    723     }
    724 
    725     /**
    726      * Start a new fragment.
    727      *
    728      * @param fragment The fragment to start
    729      * @param push If true, the current fragment will be pushed onto the back stack.  If false,
    730      * the current fragment will be replaced.
    731      */
    732     public void startPreferenceFragment(Fragment fragment, boolean push) {
    733         FragmentTransaction transaction = getFragmentManager().beginTransaction();
    734         transaction.replace(R.id.main_content, fragment);
    735         if (push) {
    736             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    737             transaction.addToBackStack(BACK_STACK_PREFS);
    738         } else {
    739             transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    740         }
    741         transaction.commitAllowingStateLoss();
    742     }
    743 
    744     /**
    745      * Switch to a specific Fragment with taking care of validation, Title and BackStack
    746      */
    747     private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
    748             boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
    749         if (validate && !isValidFragment(fragmentName)) {
    750             throw new IllegalArgumentException("Invalid fragment for this activity: "
    751                     + fragmentName);
    752         }
    753         Fragment f = Fragment.instantiate(this, fragmentName, args);
    754         FragmentTransaction transaction = getFragmentManager().beginTransaction();
    755         transaction.replace(R.id.main_content, f);
    756         if (withTransition) {
    757             TransitionManager.beginDelayedTransition(mContent);
    758         }
    759         if (addToBackStack) {
    760             transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
    761         }
    762         if (titleResId > 0) {
    763             transaction.setBreadCrumbTitle(titleResId);
    764         } else if (title != null) {
    765             transaction.setBreadCrumbTitle(title);
    766         }
    767         transaction.commitAllowingStateLoss();
    768         getFragmentManager().executePendingTransactions();
    769         return f;
    770     }
    771 
    772     private void updateTilesList() {
    773         // Generally the items that are will be changing from these updates will
    774         // not be in the top list of tiles, so run it in the background and the
    775         // SettingsDrawerActivity will pick up on the updates automatically.
    776         AsyncTask.execute(new Runnable() {
    777             @Override
    778             public void run() {
    779                 doUpdateTilesList();
    780             }
    781         });
    782     }
    783 
    784     private void doUpdateTilesList() {
    785         PackageManager pm = getPackageManager();
    786         final UserManager um = UserManager.get(this);
    787         final boolean isAdmin = um.isAdminUser();
    788 
    789         String packageName = getPackageName();
    790         setTileEnabled(new ComponentName(packageName, WifiSettingsActivity.class.getName()),
    791                 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin);
    792 
    793         setTileEnabled(new ComponentName(packageName,
    794                         Settings.BluetoothSettingsActivity.class.getName()),
    795                 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin);
    796 
    797         setTileEnabled(new ComponentName(packageName,
    798                         Settings.DataUsageSummaryActivity.class.getName()),
    799                 Utils.isBandwidthControlEnabled(), isAdmin);
    800 
    801         setTileEnabled(new ComponentName(packageName,
    802                         Settings.SimSettingsActivity.class.getName()),
    803                 Utils.showSimCardTile(this), isAdmin);
    804 
    805         setTileEnabled(new ComponentName(packageName,
    806                         Settings.PowerUsageSummaryActivity.class.getName()),
    807                 mBatteryPresent, isAdmin);
    808 
    809         setTileEnabled(new ComponentName(packageName,
    810                         Settings.UserSettingsActivity.class.getName()),
    811                 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
    812                         && !Utils.isMonkeyRunning(), isAdmin);
    813 
    814         setTileEnabled(new ComponentName(packageName,
    815                         Settings.NetworkDashboardActivity.class.getName()),
    816                 !UserManager.isDeviceInDemoMode(this), isAdmin);
    817 
    818         setTileEnabled(new ComponentName(packageName,
    819                         Settings.ConnectedDeviceDashboardActivity.class.getName()),
    820                 !UserManager.isDeviceInDemoMode(this), isAdmin);
    821 
    822         setTileEnabled(new ComponentName(packageName,
    823                         Settings.DateTimeSettingsActivity.class.getName()),
    824                 !UserManager.isDeviceInDemoMode(this), isAdmin);
    825         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
    826         setTileEnabled(new ComponentName(packageName,
    827                         Settings.PaymentSettingsActivity.class.getName()),
    828                 pm.hasSystemFeature(PackageManager.FEATURE_NFC)
    829                         && pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
    830                         && adapter != null && adapter.isEnabled(), isAdmin);
    831 
    832         setTileEnabled(new ComponentName(packageName,
    833                         Settings.PrintSettingsActivity.class.getName()),
    834                 pm.hasSystemFeature(PackageManager.FEATURE_PRINTING), isAdmin);
    835 
    836         final boolean showDev = mDevelopmentPreferences.getBoolean(
    837                 DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng"))
    838                 && !um.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES);
    839         setTileEnabled(new ComponentName(packageName,
    840                         Settings.DevelopmentSettingsActivity.class.getName()),
    841                 showDev, isAdmin);
    842 
    843         // Enable/disable backup settings depending on whether the user is admin.
    844         setTileEnabled(new ComponentName(packageName,
    845                         BackupSettingsActivity.class.getName()), true, isAdmin);
    846 
    847         setTileEnabled(new ComponentName(packageName,
    848                         Settings.WifiDisplaySettingsActivity.class.getName()),
    849                 WifiDisplaySettings.isAvailable(this), isAdmin);
    850 
    851         if (UserHandle.MU_ENABLED && !isAdmin) {
    852 
    853             // When on restricted users, disable all extra categories (but only the settings ones).
    854             final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
    855             synchronized (categories) {
    856                 for (DashboardCategory category : categories) {
    857                     final int tileCount = category.getTilesCount();
    858                     for (int i = 0; i < tileCount; i++) {
    859                         final ComponentName component = category.getTile(i).intent.getComponent();
    860 
    861                         final String name = component.getClassName();
    862                         final boolean isEnabledForRestricted = ArrayUtils.contains(
    863                                 SettingsGateway.SETTINGS_FOR_RESTRICTED, name);
    864                         if (packageName.equals(component.getPackageName())
    865                                 && !isEnabledForRestricted) {
    866                             setTileEnabled(component, false, isAdmin);
    867                         }
    868                     }
    869                 }
    870             }
    871         }
    872 
    873         // Final step, refresh categories.
    874         updateCategories();
    875     }
    876 
    877     private void setTileEnabled(ComponentName component, boolean enabled, boolean isAdmin) {
    878         if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
    879                 && !ArrayUtils.contains(SettingsGateway.SETTINGS_FOR_RESTRICTED,
    880                 component.getClassName())) {
    881             enabled = false;
    882         }
    883         setTileEnabled(component, enabled);
    884     }
    885 
    886     private void getMetaData() {
    887         try {
    888             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
    889                     PackageManager.GET_META_DATA);
    890             if (ai == null || ai.metaData == null) return;
    891             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
    892         } catch (NameNotFoundException nnfe) {
    893             // No recovery
    894             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
    895         }
    896     }
    897 
    898     // give subclasses access to the Next button
    899     public boolean hasNextButton() {
    900         return mNextButton != null;
    901     }
    902 
    903     public Button getNextButton() {
    904         return mNextButton;
    905     }
    906 
    907     @Override
    908     public boolean shouldUpRecreateTask(Intent targetIntent) {
    909         return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
    910     }
    911 
    912     public void startSuggestion(Intent intent) {
    913         if (intent == null || ActivityManager.isUserAMonkey()) {
    914             return;
    915         }
    916         mCurrentSuggestion = intent.getComponent();
    917         startActivityForResult(intent, REQUEST_SUGGESTION);
    918     }
    919 
    920     @Override
    921     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    922         if (requestCode == REQUEST_SUGGESTION && mCurrentSuggestion != null
    923                 && resultCode != RESULT_CANCELED) {
    924             getPackageManager().setComponentEnabledSetting(mCurrentSuggestion,
    925                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    926         }
    927         super.onActivityResult(requestCode, resultCode, data);
    928     }
    929 
    930     @VisibleForTesting
    931     Bitmap getBitmapFromXmlResource(int drawableRes) {
    932         Drawable drawable = getResources().getDrawable(drawableRes, getTheme());
    933         Canvas canvas = new Canvas();
    934         Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
    935                 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    936         canvas.setBitmap(bitmap);
    937         drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    938         drawable.draw(canvas);
    939 
    940         return bitmap;
    941     }
    942 }