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