• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings;
18 
19 import static 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 
getSwitchBar()191     public SwitchBar getSwitchBar() {
192         return mSwitchBar;
193     }
194 
195     @Override
onPreferenceStartFragment(PreferenceFragment caller, Preference pref)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
onPreferenceTreeClick(Preference preference)209     public boolean onPreferenceTreeClick(Preference preference) {
210         return false;
211     }
212 
213     @Override
getSharedPreferences(String name, int mode)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 
getMetricsTag()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
onCreate(Bundle savedState)234     protected void onCreate(Bundle savedState) {
235         super.onCreate(savedState);
236         Log.d(LOG_TAG, "Starting onCreate");
237 
238         if (isLockTaskModePinned() && !isSettingsRunOnTop() && !isLaunchableInTaskModePinned()) {
239             Log.w(LOG_TAG, "Devices lock task mode pinned.");
240             finish();
241         }
242 
243         long startTime = System.currentTimeMillis();
244 
245         final FeatureFactory factory = FeatureFactory.getFactory(this);
246 
247         mDashboardFeatureProvider = factory.getDashboardFeatureProvider(this);
248 
249         // Should happen before any call to getIntent()
250         getMetaData();
251 
252         final Intent intent = getIntent();
253         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
254             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
255         }
256 
257         // Getting Intent properties can only be done after the super.onCreate(...)
258         final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
259 
260         final ComponentName cn = intent.getComponent();
261         final String className = cn.getClassName();
262 
263         mIsShowingDashboard = className.equals(Settings.class.getName());
264 
265         // This is a "Sub Settings" when:
266         // - this is a real SubSettings
267         // - or :settings:show_fragment_as_subsetting is passed to the Intent
268         final boolean isSubSettings = this instanceof SubSettings ||
269                 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
270 
271         // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content
272         // insets
273         if (isSubSettings) {
274             setTheme(R.style.Theme_SubSettings);
275         }
276 
277         setContentView(mIsShowingDashboard ?
278                 R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
279 
280         mContent = findViewById(R.id.main_content);
281 
282         getFragmentManager().addOnBackStackChangedListener(this);
283 
284         if (savedState != null) {
285             // We are restarting from a previous saved state; used that to initialize, instead
286             // of starting fresh.
287             setTitleFromIntent(intent);
288 
289             ArrayList<DashboardCategory> categories =
290                     savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
291             if (categories != null) {
292                 mCategories.clear();
293                 mCategories.addAll(categories);
294                 setTitleFromBackStack();
295             }
296         } else {
297             launchSettingFragment(initialFragmentName, isSubSettings, intent);
298         }
299 
300         final boolean deviceProvisioned = Utils.isDeviceProvisioned(this);
301         if (mIsShowingDashboard) {
302             findViewById(R.id.search_bar).setVisibility(
303                     deviceProvisioned ? View.VISIBLE : View.INVISIBLE);
304             findViewById(R.id.action_bar).setVisibility(View.GONE);
305             final Toolbar toolbar = findViewById(R.id.search_action_bar);
306             FeatureFactory.getFactory(this).getSearchFeatureProvider()
307                     .initSearchToolbar(this, toolbar);
308             setActionBar(toolbar);
309 
310             // Please forgive me for what I am about to do.
311             //
312             // Need to make the navigation icon non-clickable so that the entire card is clickable
313             // and goes to the search UI. Also set the background to null so there's no ripple.
314             View navView = toolbar.getNavigationView();
315             navView.setClickable(false);
316             navView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
317             navView.setBackground(null);
318         }
319 
320         ActionBar actionBar = getActionBar();
321         if (actionBar != null) {
322             actionBar.setDisplayHomeAsUpEnabled(deviceProvisioned);
323             actionBar.setHomeButtonEnabled(deviceProvisioned);
324             actionBar.setDisplayShowTitleEnabled(!mIsShowingDashboard);
325         }
326         mSwitchBar = findViewById(R.id.switch_bar);
327         if (mSwitchBar != null) {
328             mSwitchBar.setMetricsTag(getMetricsTag());
329         }
330 
331         // see if we should show Back/Next buttons
332         if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
333 
334             View buttonBar = findViewById(R.id.button_bar);
335             if (buttonBar != null) {
336                 buttonBar.setVisibility(View.VISIBLE);
337 
338                 Button backButton = (Button) findViewById(R.id.back_button);
339                 backButton.setOnClickListener(new OnClickListener() {
340                     public void onClick(View v) {
341                         setResult(RESULT_CANCELED, null);
342                         finish();
343                     }
344                 });
345                 Button skipButton = (Button) findViewById(R.id.skip_button);
346                 skipButton.setOnClickListener(new OnClickListener() {
347                     public void onClick(View v) {
348                         setResult(RESULT_OK, null);
349                         finish();
350                     }
351                 });
352                 mNextButton = (Button) findViewById(R.id.next_button);
353                 mNextButton.setOnClickListener(new OnClickListener() {
354                     public void onClick(View v) {
355                         setResult(RESULT_OK, null);
356                         finish();
357                     }
358                 });
359 
360                 // set our various button parameters
361                 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
362                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
363                     if (TextUtils.isEmpty(buttonText)) {
364                         mNextButton.setVisibility(View.GONE);
365                     } else {
366                         mNextButton.setText(buttonText);
367                     }
368                 }
369                 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
370                     String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
371                     if (TextUtils.isEmpty(buttonText)) {
372                         backButton.setVisibility(View.GONE);
373                     } else {
374                         backButton.setText(buttonText);
375                     }
376                 }
377                 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
378                     skipButton.setVisibility(View.VISIBLE);
379                 }
380             }
381         }
382 
383         if (DEBUG_TIMING) {
384             Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms");
385         }
386     }
387 
388     @VisibleForTesting
launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent)389     void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
390         if (!mIsShowingDashboard && initialFragmentName != null) {
391             setTitleFromIntent(intent);
392 
393             Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
394             switchToFragment(initialFragmentName, initialArguments, true, false,
395                     mInitialTitleResId, mInitialTitle, false);
396         } else {
397             // Show search icon as up affordance if we are displaying the main Dashboard
398             mInitialTitleResId = R.string.dashboard_title;
399 
400             switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
401                     mInitialTitleResId, mInitialTitle, false);
402         }
403     }
404 
setTitleFromIntent(Intent intent)405     private void setTitleFromIntent(Intent intent) {
406         Log.d(LOG_TAG, "Starting to set activity title");
407         final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
408         if (initialTitleResId > 0) {
409             mInitialTitle = null;
410             mInitialTitleResId = initialTitleResId;
411 
412             final String initialTitleResPackageName = intent.getStringExtra(
413                     EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME);
414             if (initialTitleResPackageName != null) {
415                 try {
416                     Context authContext = createPackageContextAsUser(initialTitleResPackageName,
417                             0 /* flags */, new UserHandle(UserHandle.myUserId()));
418                     mInitialTitle = authContext.getResources().getText(mInitialTitleResId);
419                     setTitle(mInitialTitle);
420                     mInitialTitleResId = -1;
421                     return;
422                 } catch (NameNotFoundException e) {
423                     Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName);
424                 }
425             } else {
426                 setTitle(mInitialTitleResId);
427             }
428         } else {
429             mInitialTitleResId = -1;
430             final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
431             mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
432             setTitle(mInitialTitle);
433         }
434         Log.d(LOG_TAG, "Done setting title");
435     }
436 
437     @Override
onBackStackChanged()438     public void onBackStackChanged() {
439         setTitleFromBackStack();
440     }
441 
setTitleFromBackStack()442     private void setTitleFromBackStack() {
443         final int count = getFragmentManager().getBackStackEntryCount();
444 
445         if (count == 0) {
446             if (mInitialTitleResId > 0) {
447                 setTitle(mInitialTitleResId);
448             } else {
449                 setTitle(mInitialTitle);
450             }
451             return;
452         }
453 
454         FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
455         setTitleFromBackStackEntry(bse);
456     }
457 
setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse)458     private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
459         final CharSequence title;
460         final int titleRes = bse.getBreadCrumbTitleRes();
461         if (titleRes > 0) {
462             title = getText(titleRes);
463         } else {
464             title = bse.getBreadCrumbTitle();
465         }
466         if (title != null) {
467             setTitle(title);
468         }
469     }
470 
471     @Override
onSaveInstanceState(Bundle outState)472     protected void onSaveInstanceState(Bundle outState) {
473         super.onSaveInstanceState(outState);
474         saveState(outState);
475     }
476 
477     /**
478      * For testing purposes to avoid crashes from final variables in Activity's onSaveInstantState.
479      */
480     @VisibleForTesting
saveState(Bundle outState)481     void saveState(Bundle outState) {
482         if (mCategories.size() > 0) {
483             outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories);
484         }
485     }
486 
487     @Override
onResume()488     protected void onResume() {
489         super.onResume();
490 
491         mDevelopmentSettingsListener = new BroadcastReceiver() {
492             @Override
493             public void onReceive(Context context, Intent intent) {
494                 updateTilesList();
495             }
496         };
497         LocalBroadcastManager.getInstance(this).registerReceiver(mDevelopmentSettingsListener,
498                 new IntentFilter(DevelopmentSettingsEnabler.DEVELOPMENT_SETTINGS_CHANGED_ACTION));
499 
500         registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
501 
502         updateTilesList();
503         updateDeviceIndex();
504     }
505 
506     @Override
onPause()507     protected void onPause() {
508         super.onPause();
509         LocalBroadcastManager.getInstance(this).unregisterReceiver(mDevelopmentSettingsListener);
510         mDevelopmentSettingsListener = null;
511         unregisterReceiver(mBatteryInfoReceiver);
512     }
513 
514     @Override
setTaskDescription(ActivityManager.TaskDescription taskDescription)515     public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
516         final Bitmap icon = getBitmapFromXmlResource(R.drawable.ic_launcher_settings);
517         taskDescription.setIcon(icon);
518         super.setTaskDescription(taskDescription);
519     }
520 
isValidFragment(String fragmentName)521     protected boolean isValidFragment(String fragmentName) {
522         // Almost all fragments are wrapped in this,
523         // except for a few that have their own activities.
524         for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) {
525             if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
526         }
527         return false;
528     }
529 
530     @Override
getIntent()531     public Intent getIntent() {
532         Intent superIntent = super.getIntent();
533         String startingFragment = getStartingFragmentClass(superIntent);
534         // This is called from super.onCreate, isMultiPane() is not yet reliable
535         // Do not use onIsHidingHeaders either, which relies itself on this method
536         if (startingFragment != null) {
537             Intent modIntent = new Intent(superIntent);
538             modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
539             Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
540             if (args != null) {
541                 args = new Bundle(args);
542             } else {
543                 args = new Bundle();
544             }
545             args.putParcelable("intent", superIntent);
546             modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
547             return modIntent;
548         }
549         return superIntent;
550     }
551 
552     /**
553      * Checks if the component name in the intent is different from the Settings class and
554      * returns the class name to load as a fragment.
555      */
getStartingFragmentClass(Intent intent)556     private String getStartingFragmentClass(Intent intent) {
557         if (mFragmentClass != null) return mFragmentClass;
558 
559         String intentClass = intent.getComponent().getClassName();
560         if (intentClass.equals(getClass().getName())) return null;
561 
562         if ("com.android.settings.RunningServices".equals(intentClass)
563                 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
564             // Old names of manage apps.
565             intentClass = ManageApplications.class.getName();
566         }
567 
568         return intentClass;
569     }
570 
571     /**
572      * Called by a preference panel fragment to finish itself.
573      *
574      * @param resultCode Optional result code to send back to the original
575      *                   launching fragment.
576      * @param resultData Optional result data to send back to the original
577      *                   launching fragment.
578      */
finishPreferencePanel(int resultCode, Intent resultData)579     public void finishPreferencePanel(int resultCode, Intent resultData) {
580         setResult(resultCode, resultData);
581         finish();
582     }
583 
584     /**
585      * Switch to a specific Fragment with taking care of validation, Title and BackStack
586      */
switchToFragment(String fragmentName, Bundle args, boolean validate, boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition)587     private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
588             boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) {
589         Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
590         if (validate && !isValidFragment(fragmentName)) {
591             throw new IllegalArgumentException("Invalid fragment for this activity: "
592                     + fragmentName);
593         }
594         Fragment f = Fragment.instantiate(this, fragmentName, args);
595         FragmentTransaction transaction = getFragmentManager().beginTransaction();
596         transaction.replace(R.id.main_content, f);
597         if (withTransition) {
598             TransitionManager.beginDelayedTransition(mContent);
599         }
600         if (addToBackStack) {
601             transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
602         }
603         if (titleResId > 0) {
604             transaction.setBreadCrumbTitle(titleResId);
605         } else if (title != null) {
606             transaction.setBreadCrumbTitle(title);
607         }
608         transaction.commitAllowingStateLoss();
609         getFragmentManager().executePendingTransactions();
610         Log.d(LOG_TAG, "Executed frag manager pendingTransactions");
611         return f;
612     }
613 
updateTilesList()614     private void updateTilesList() {
615         // Generally the items that are will be changing from these updates will
616         // not be in the top list of tiles, so run it in the background and the
617         // SettingsDrawerActivity will pick up on the updates automatically.
618         AsyncTask.execute(new Runnable() {
619             @Override
620             public void run() {
621                 doUpdateTilesList();
622             }
623         });
624     }
625 
updateDeviceIndex()626     private void updateDeviceIndex() {
627         DeviceIndexFeatureProvider indexProvider = FeatureFactory.getFactory(
628                 this).getDeviceIndexFeatureProvider();
629 
630         ThreadUtils.postOnBackgroundThread(
631                 () -> indexProvider.updateIndex(SettingsActivity.this, false /* force */));
632     }
633 
doUpdateTilesList()634     private void doUpdateTilesList() {
635         PackageManager pm = getPackageManager();
636         final UserManager um = UserManager.get(this);
637         final boolean isAdmin = um.isAdminUser();
638         final FeatureFactory featureFactory = FeatureFactory.getFactory(this);
639         boolean somethingChanged = false;
640         final String packageName = getPackageName();
641         final StringBuilder changedList = new StringBuilder();
642         somethingChanged = setTileEnabled(changedList,
643                 new ComponentName(packageName, WifiSettingsActivity.class.getName()),
644                 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin) || somethingChanged;
645 
646         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
647                         Settings.BluetoothSettingsActivity.class.getName()),
648                 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin)
649                 || somethingChanged;
650 
651 
652         // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise
653         // enable DataPlanUsageSummaryActivity.
654         somethingChanged = setTileEnabled(changedList,
655                 new ComponentName(packageName, Settings.DataUsageSummaryActivity.class.getName()),
656                 Utils.isBandwidthControlEnabled() /* enabled */,
657                 isAdmin) || somethingChanged;
658 
659         somethingChanged = setTileEnabled(changedList,
660                 new ComponentName(packageName,
661                         Settings.ConnectedDeviceDashboardActivity.class.getName()),
662                 !UserManager.isDeviceInDemoMode(this) /* enabled */,
663                 isAdmin) || somethingChanged;
664 
665         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
666                         Settings.SimSettingsActivity.class.getName()),
667                 Utils.showSimCardTile(this), isAdmin)
668                 || somethingChanged;
669 
670         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
671                         Settings.PowerUsageSummaryActivity.class.getName()),
672                 mBatteryPresent, isAdmin) || somethingChanged;
673 
674         final boolean isDataUsageSettingsV2Enabled =
675                 FeatureFlagUtils.isEnabled(this, FeatureFlags.DATA_USAGE_SETTINGS_V2);
676         // Enable new data usage page if v2 enabled
677         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
678                         Settings.DataUsageSummaryActivity.class.getName()),
679                 Utils.isBandwidthControlEnabled() && isDataUsageSettingsV2Enabled, isAdmin)
680                 || somethingChanged;
681         // Enable legacy data usage page if v2 disabled
682         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
683                         Settings.DataUsageSummaryLegacyActivity.class.getName()),
684                 Utils.isBandwidthControlEnabled() && !isDataUsageSettingsV2Enabled, isAdmin)
685                 || somethingChanged;
686 
687         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
688                         Settings.UserSettingsActivity.class.getName()),
689                 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers()
690                         && !Utils.isMonkeyRunning(), isAdmin)
691                 || somethingChanged;
692 
693         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
694                         Settings.NetworkDashboardActivity.class.getName()),
695                 !UserManager.isDeviceInDemoMode(this), isAdmin)
696                 || somethingChanged;
697 
698         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
699                         Settings.DateTimeSettingsActivity.class.getName()),
700                 !UserManager.isDeviceInDemoMode(this), isAdmin)
701                 || somethingChanged;
702 
703         final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this)
704                 && !Utils.isMonkeyRunning();
705         final boolean isAdminOrDemo = um.isAdminUser() || um.isDemoUser();
706         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
707                         Settings.DevelopmentSettingsDashboardActivity.class.getName()),
708                 showDev, isAdminOrDemo)
709                 || somethingChanged;
710 
711         // Enable/disable backup settings depending on whether the user is admin.
712         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
713                 BackupSettingsActivity.class.getName()), true, isAdmin)
714                 || somethingChanged;
715 
716         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
717                         Settings.WifiDisplaySettingsActivity.class.getName()),
718                 WifiDisplaySettings.isAvailable(this), isAdmin)
719                 || somethingChanged;
720 
721         // Enable/disable the Me Card page.
722         final boolean aboutPhoneV2Enabled = featureFactory
723                 .getAccountFeatureProvider()
724                 .isAboutPhoneV2Enabled(this);
725         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
726                         Settings.MyDeviceInfoActivity.class.getName()),
727                 aboutPhoneV2Enabled, isAdmin)
728                 || somethingChanged;
729         somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
730                         Settings.DeviceInfoSettingsActivity.class.getName()),
731                 !aboutPhoneV2Enabled, isAdmin)
732                 || somethingChanged;
733 
734         if (UserHandle.MU_ENABLED && !isAdmin) {
735 
736             // When on restricted users, disable all extra categories (but only the settings ones).
737             final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
738             synchronized (categories) {
739                 for (DashboardCategory category : categories) {
740                     final int tileCount = category.getTilesCount();
741                     for (int i = 0; i < tileCount; i++) {
742                         final ComponentName component = category.getTile(i).intent.getComponent();
743                         final String name = component.getClassName();
744                         final boolean isEnabledForRestricted = ArrayUtils.contains(
745                                 SettingsGateway.SETTINGS_FOR_RESTRICTED, name) || (isAdminOrDemo
746                                 && Settings.DevelopmentSettingsDashboardActivity.class.getName()
747                                 .equals(name));
748                         if (packageName.equals(component.getPackageName())
749                                 && !isEnabledForRestricted) {
750                             somethingChanged =
751                                     setTileEnabled(changedList, component, false, isAdmin)
752                                             || somethingChanged;
753                         }
754                     }
755                 }
756             }
757         }
758 
759         // Final step, refresh categories.
760         if (somethingChanged) {
761             Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories "
762                     + changedList.toString());
763             updateCategories();
764         } else {
765             Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call");
766         }
767     }
768 
769     /**
770      * @return whether or not the enabled state actually changed.
771      */
setTileEnabled(StringBuilder changedList, ComponentName component, boolean enabled, boolean isAdmin)772     private boolean setTileEnabled(StringBuilder changedList, ComponentName component,
773             boolean enabled, boolean isAdmin) {
774         if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName())
775                 && !ArrayUtils.contains(SettingsGateway.SETTINGS_FOR_RESTRICTED,
776                 component.getClassName())) {
777             enabled = false;
778         }
779         boolean changed = setTileEnabled(component, enabled);
780         if (changed) {
781             changedList.append(component.toShortString()).append(",");
782         }
783         return changed;
784     }
785 
getMetaData()786     private void getMetaData() {
787         try {
788             ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
789                     PackageManager.GET_META_DATA);
790             if (ai == null || ai.metaData == null) return;
791             mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
792         } catch (NameNotFoundException nnfe) {
793             // No recovery
794             Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
795         }
796     }
797 
798     // give subclasses access to the Next button
hasNextButton()799     public boolean hasNextButton() {
800         return mNextButton != null;
801     }
802 
getNextButton()803     public Button getNextButton() {
804         return mNextButton;
805     }
806 
807     /**
808      * @return whether or not the activity can be launched from other apps in the pinning screen.
809      */
isLaunchableInTaskModePinned()810     public boolean isLaunchableInTaskModePinned() {
811         return false;
812     }
813 
814     @VisibleForTesting
getBitmapFromXmlResource(int drawableRes)815     Bitmap getBitmapFromXmlResource(int drawableRes) {
816         Drawable drawable = getResources().getDrawable(drawableRes, getTheme());
817         Canvas canvas = new Canvas();
818         Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
819                 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
820         canvas.setBitmap(bitmap);
821         drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
822         drawable.draw(canvas);
823 
824         return bitmap;
825     }
826 
isLockTaskModePinned()827     private boolean isLockTaskModePinned() {
828         final ActivityManager activityManager =
829             getApplicationContext().getSystemService(ActivityManager.class);
830         return activityManager.getLockTaskModeState() == ActivityManager.LOCK_TASK_MODE_PINNED;
831     }
832 
isSettingsRunOnTop()833     private boolean isSettingsRunOnTop() {
834         final ActivityManager activityManager =
835             getApplicationContext().getSystemService(ActivityManager.class);
836         final String taskPkgName = activityManager.getRunningTasks(1 /* maxNum */)
837             .get(0 /* index */).baseActivity.getPackageName();
838         return TextUtils.equals(getPackageName(), taskPkgName);
839     }
840 }
841