OSDN Git Service

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