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.SettingsMainSwitchBar; 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 SettingsMainSwitchBar mMainSwitch; 176 177 private Button mNextButton; 178 179 // Categories 180 private ArrayList<DashboardCategory> mCategories = new ArrayList<>(); 181 182 private DashboardFeatureProvider mDashboardFeatureProvider; 183 getSwitchBar()184 public SettingsMainSwitchBar getSwitchBar() { 185 return mMainSwitch; 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 = getInitialFragmentName(getIntent()); 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 = getInitialFragmentName(intent); 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 mMainSwitch = findViewById(R.id.switch_bar); 294 if (mMainSwitch != null) { 295 mMainSwitch.setMetricsTag(getMetricsTag()); 296 mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1); 297 } 298 299 // see if we should show Back/Next buttons 300 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { 301 302 View buttonBar = findViewById(R.id.button_bar); 303 if (buttonBar != null) { 304 buttonBar.setVisibility(View.VISIBLE); 305 306 Button backButton = findViewById(R.id.back_button); 307 backButton.setOnClickListener(v -> { 308 setResult(RESULT_CANCELED, null); 309 finish(); 310 }); 311 Button skipButton = findViewById(R.id.skip_button); 312 skipButton.setOnClickListener(v -> { 313 setResult(RESULT_OK, null); 314 finish(); 315 }); 316 mNextButton = findViewById(R.id.next_button); 317 mNextButton.setOnClickListener(v -> { 318 setResult(RESULT_OK, null); 319 finish(); 320 }); 321 322 // set our various button parameters 323 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) { 324 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT); 325 if (TextUtils.isEmpty(buttonText)) { 326 mNextButton.setVisibility(View.GONE); 327 } else { 328 mNextButton.setText(buttonText); 329 } 330 } 331 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) { 332 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT); 333 if (TextUtils.isEmpty(buttonText)) { 334 backButton.setVisibility(View.GONE); 335 } else { 336 backButton.setText(buttonText); 337 } 338 } 339 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { 340 skipButton.setVisibility(View.VISIBLE); 341 } 342 } 343 } 344 345 if (DEBUG_TIMING) { 346 Log.d(LOG_TAG, "onCreate took " + (System.currentTimeMillis() - startTime) + " ms"); 347 } 348 } 349 350 /** Returns the initial fragment name that the activity will launch. */ 351 @VisibleForTesting getInitialFragmentName(Intent intent)352 public String getInitialFragmentName(Intent intent) { 353 return intent.getStringExtra(EXTRA_SHOW_FRAGMENT); 354 } 355 356 @Override onApplyThemeResource(Theme theme, int resid, boolean first)357 protected void onApplyThemeResource(Theme theme, int resid, boolean first) { 358 theme.applyStyle(R.style.SetupWizardPartnerResource, true); 359 super.onApplyThemeResource(theme, resid, first); 360 } 361 362 @Override onActivityResult(int requestCode, int resultCode, @Nullable Intent data)363 protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 364 super.onActivityResult(requestCode, resultCode, data); 365 final List<Fragment> fragments = getSupportFragmentManager().getFragments(); 366 if (fragments != null) { 367 for (Fragment fragment : fragments) { 368 if (fragment instanceof OnActivityResultListener) { 369 fragment.onActivityResult(requestCode, resultCode, data); 370 } 371 } 372 } 373 } 374 375 @VisibleForTesting launchSettingFragment(String initialFragmentName, Intent intent)376 void launchSettingFragment(String initialFragmentName, Intent intent) { 377 if (initialFragmentName != null) { 378 setTitleFromIntent(intent); 379 380 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 381 switchToFragment(initialFragmentName, initialArguments, true, 382 mInitialTitleResId, mInitialTitle); 383 } else { 384 // Show search icon as up affordance if we are displaying the main Dashboard 385 mInitialTitleResId = R.string.dashboard_title; 386 switchToFragment(TopLevelSettings.class.getName(), null /* args */, false, 387 mInitialTitleResId, mInitialTitle); 388 } 389 } 390 setTitleFromIntent(Intent intent)391 private void setTitleFromIntent(Intent intent) { 392 Log.d(LOG_TAG, "Starting to set activity title"); 393 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1); 394 if (initialTitleResId > 0) { 395 mInitialTitle = null; 396 mInitialTitleResId = initialTitleResId; 397 398 final String initialTitleResPackageName = intent.getStringExtra( 399 EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME); 400 if (initialTitleResPackageName != null) { 401 try { 402 Context authContext = createPackageContextAsUser(initialTitleResPackageName, 403 0 /* flags */, new UserHandle(UserHandle.myUserId())); 404 mInitialTitle = authContext.getResources().getText(mInitialTitleResId); 405 setTitle(mInitialTitle); 406 mInitialTitleResId = -1; 407 return; 408 } catch (NameNotFoundException e) { 409 Log.w(LOG_TAG, "Could not find package" + initialTitleResPackageName); 410 } 411 } else { 412 setTitle(mInitialTitleResId); 413 } 414 } else { 415 mInitialTitleResId = -1; 416 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE); 417 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle(); 418 setTitle(mInitialTitle); 419 } 420 Log.d(LOG_TAG, "Done setting title"); 421 } 422 423 @Override onBackStackChanged()424 public void onBackStackChanged() { 425 setTitleFromBackStack(); 426 } 427 setTitleFromBackStack()428 private void setTitleFromBackStack() { 429 final int count = getSupportFragmentManager().getBackStackEntryCount(); 430 431 if (count == 0) { 432 if (mInitialTitleResId > 0) { 433 setTitle(mInitialTitleResId); 434 } else { 435 setTitle(mInitialTitle); 436 } 437 return; 438 } 439 440 FragmentManager.BackStackEntry bse = getSupportFragmentManager(). 441 getBackStackEntryAt(count - 1); 442 setTitleFromBackStackEntry(bse); 443 } 444 setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse)445 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) { 446 final CharSequence title; 447 final int titleRes = bse.getBreadCrumbTitleRes(); 448 if (titleRes > 0) { 449 title = getText(titleRes); 450 } else { 451 title = bse.getBreadCrumbTitle(); 452 } 453 if (title != null) { 454 setTitle(title); 455 } 456 } 457 458 @Override onSaveInstanceState(Bundle outState)459 protected void onSaveInstanceState(Bundle outState) { 460 super.onSaveInstanceState(outState); 461 saveState(outState); 462 } 463 464 /** 465 * For testing purposes to avoid crashes from final variables in Activity's onSaveInstantState. 466 */ 467 @VisibleForTesting saveState(Bundle outState)468 void saveState(Bundle outState) { 469 if (mCategories.size() > 0) { 470 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories); 471 } 472 } 473 474 @Override onResume()475 protected void onResume() { 476 super.onResume(); 477 478 mDevelopmentSettingsListener = new BroadcastReceiver() { 479 @Override 480 public void onReceive(Context context, Intent intent) { 481 updateTilesList(); 482 } 483 }; 484 LocalBroadcastManager.getInstance(this).registerReceiver(mDevelopmentSettingsListener, 485 new IntentFilter(DevelopmentSettingsEnabler.DEVELOPMENT_SETTINGS_CHANGED_ACTION)); 486 487 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 488 489 updateTilesList(); 490 } 491 492 @Override onPause()493 protected void onPause() { 494 super.onPause(); 495 LocalBroadcastManager.getInstance(this).unregisterReceiver(mDevelopmentSettingsListener); 496 mDevelopmentSettingsListener = null; 497 unregisterReceiver(mBatteryInfoReceiver); 498 } 499 500 @Override setTaskDescription(ActivityManager.TaskDescription taskDescription)501 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) { 502 taskDescription.setIcon(Icon.createWithResource(this, R.drawable.ic_launcher_settings)); 503 super.setTaskDescription(taskDescription); 504 } 505 isValidFragment(String fragmentName)506 protected boolean isValidFragment(String fragmentName) { 507 // Almost all fragments are wrapped in this, 508 // except for a few that have their own activities. 509 for (int i = 0; i < SettingsGateway.ENTRY_FRAGMENTS.length; i++) { 510 if (SettingsGateway.ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; 511 } 512 return false; 513 } 514 515 @Override getIntent()516 public Intent getIntent() { 517 Intent superIntent = super.getIntent(); 518 String startingFragment = getStartingFragmentClass(superIntent); 519 // This is called from super.onCreate, isMultiPane() is not yet reliable 520 // Do not use onIsHidingHeaders either, which relies itself on this method 521 if (startingFragment != null) { 522 Intent modIntent = new Intent(superIntent); 523 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 524 Bundle args = superIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 525 if (args != null) { 526 args = new Bundle(args); 527 } else { 528 args = new Bundle(); 529 } 530 args.putParcelable("intent", superIntent); 531 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); 532 return modIntent; 533 } 534 return superIntent; 535 } 536 537 /** 538 * Checks if the component name in the intent is different from the Settings class and 539 * returns the class name to load as a fragment. 540 */ getStartingFragmentClass(Intent intent)541 private String getStartingFragmentClass(Intent intent) { 542 if (mFragmentClass != null) return mFragmentClass; 543 544 String intentClass = intent.getComponent().getClassName(); 545 if (intentClass.equals(getClass().getName())) return null; 546 547 if ("com.android.settings.RunningServices".equals(intentClass) 548 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 549 // Old names of manage apps. 550 intentClass = ManageApplications.class.getName(); 551 } 552 553 return intentClass; 554 } 555 556 /** 557 * Called by a preference panel fragment to finish itself. 558 * 559 * @param resultCode Optional result code to send back to the original 560 * launching fragment. 561 * @param resultData Optional result data to send back to the original 562 * launching fragment. 563 */ finishPreferencePanel(int resultCode, Intent resultData)564 public void finishPreferencePanel(int resultCode, Intent resultData) { 565 setResult(resultCode, resultData); 566 if (resultData != null && 567 resultData.getBooleanExtra(KEY_REMOVE_TASK_WHEN_FINISHING, false)) { 568 finishAndRemoveTask(); 569 } else { 570 finish(); 571 } 572 } 573 574 /** 575 * Switch to a specific Fragment with taking care of validation, Title and BackStack 576 */ switchToFragment(String fragmentName, Bundle args, boolean validate, int titleResId, CharSequence title)577 private void switchToFragment(String fragmentName, Bundle args, boolean validate, 578 int titleResId, CharSequence title) { 579 Log.d(LOG_TAG, "Switching to fragment " + fragmentName); 580 if (validate && !isValidFragment(fragmentName)) { 581 throw new IllegalArgumentException("Invalid fragment for this activity: " 582 + fragmentName); 583 } 584 Fragment f = Utils.getTargetFragment(this, fragmentName, args); 585 if (f == null) { 586 return; 587 } 588 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 589 transaction.replace(R.id.main_content, f); 590 if (titleResId > 0) { 591 transaction.setBreadCrumbTitle(titleResId); 592 } else if (title != null) { 593 transaction.setBreadCrumbTitle(title); 594 } 595 transaction.commitAllowingStateLoss(); 596 getSupportFragmentManager().executePendingTransactions(); 597 Log.d(LOG_TAG, "Executed frag manager pendingTransactions"); 598 } 599 updateTilesList()600 private void updateTilesList() { 601 // Generally the items that are will be changing from these updates will 602 // not be in the top list of tiles, so run it in the background and the 603 // SettingsBaseActivity will pick up on the updates automatically. 604 AsyncTask.execute(() -> doUpdateTilesList()); 605 } 606 doUpdateTilesList()607 private void doUpdateTilesList() { 608 PackageManager pm = getPackageManager(); 609 final UserManager um = UserManager.get(this); 610 final boolean isAdmin = um.isAdminUser(); 611 boolean somethingChanged = false; 612 final String packageName = getPackageName(); 613 final StringBuilder changedList = new StringBuilder(); 614 somethingChanged = setTileEnabled(changedList, 615 new ComponentName(packageName, WifiSettingsActivity.class.getName()), 616 pm.hasSystemFeature(PackageManager.FEATURE_WIFI), isAdmin) || somethingChanged; 617 618 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 619 Settings.BluetoothSettingsActivity.class.getName()), 620 pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH), isAdmin) 621 || somethingChanged; 622 623 // Enable DataUsageSummaryActivity if the data plan feature flag is turned on otherwise 624 // enable DataPlanUsageSummaryActivity. 625 somethingChanged = setTileEnabled(changedList, 626 new ComponentName(packageName, Settings.DataUsageSummaryActivity.class.getName()), 627 Utils.isBandwidthControlEnabled() /* enabled */, 628 isAdmin) || somethingChanged; 629 630 somethingChanged = setTileEnabled(changedList, 631 new ComponentName(packageName, 632 Settings.ConnectedDeviceDashboardActivity.class.getName()), 633 !UserManager.isDeviceInDemoMode(this) /* enabled */, 634 isAdmin) || somethingChanged; 635 636 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 637 Settings.PowerUsageSummaryActivity.class.getName()), 638 mBatteryPresent, isAdmin) || somethingChanged; 639 640 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 641 Settings.DataUsageSummaryActivity.class.getName()), 642 Utils.isBandwidthControlEnabled(), isAdmin) 643 || somethingChanged; 644 645 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 646 Settings.UserSettingsActivity.class.getName()), 647 UserHandle.MU_ENABLED && UserManager.supportsMultipleUsers() 648 && !Utils.isMonkeyRunning(), isAdmin) 649 || somethingChanged; 650 651 final boolean showDev = DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(this) 652 && !Utils.isMonkeyRunning(); 653 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 654 Settings.DevelopmentSettingsDashboardActivity.class.getName()), 655 showDev, isAdmin) 656 || somethingChanged; 657 658 somethingChanged = setTileEnabled(changedList, new ComponentName(packageName, 659 Settings.WifiDisplaySettingsActivity.class.getName()), 660 WifiDisplaySettings.isAvailable(this), isAdmin) 661 || somethingChanged; 662 663 if (UserHandle.MU_ENABLED && !isAdmin) { 664 // When on restricted users, disable all extra categories (but only the settings ones). 665 final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories(); 666 synchronized (categories) { 667 for (DashboardCategory category : categories) { 668 final int tileCount = category.getTilesCount(); 669 for (int i = 0; i < tileCount; i++) { 670 final ComponentName component = category.getTile(i) 671 .getIntent().getComponent(); 672 final String name = component.getClassName(); 673 final boolean isEnabledForRestricted = ArrayUtils.contains( 674 SettingsGateway.SETTINGS_FOR_RESTRICTED, name); 675 if (packageName.equals(component.getPackageName()) 676 && !isEnabledForRestricted) { 677 somethingChanged = 678 setTileEnabled(changedList, component, false, isAdmin) 679 || somethingChanged; 680 } 681 } 682 } 683 } 684 } 685 686 // Final step, refresh categories. 687 if (somethingChanged) { 688 Log.d(LOG_TAG, "Enabled state changed for some tiles, reloading all categories " 689 + changedList.toString()); 690 mCategoryMixin.updateCategories(); 691 } else { 692 Log.d(LOG_TAG, "No enabled state changed, skipping updateCategory call"); 693 } 694 } 695 696 /** 697 * @return whether or not the enabled state actually changed. 698 */ setTileEnabled(StringBuilder changedList, ComponentName component, boolean enabled, boolean isAdmin)699 private boolean setTileEnabled(StringBuilder changedList, ComponentName component, 700 boolean enabled, boolean isAdmin) { 701 if (UserHandle.MU_ENABLED && !isAdmin && getPackageName().equals(component.getPackageName()) 702 && !ArrayUtils.contains(SettingsGateway.SETTINGS_FOR_RESTRICTED, 703 component.getClassName())) { 704 enabled = false; 705 } 706 boolean changed = setTileEnabled(component, enabled); 707 if (changed) { 708 changedList.append(component.toShortString()).append(","); 709 } 710 return changed; 711 } 712 getMetaData()713 private void getMetaData() { 714 try { 715 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 716 PackageManager.GET_META_DATA); 717 if (ai == null || ai.metaData == null) return; 718 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 719 } catch (NameNotFoundException nnfe) { 720 // No recovery 721 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString()); 722 } 723 } 724 725 // give subclasses access to the Next button hasNextButton()726 public boolean hasNextButton() { 727 return mNextButton != null; 728 } 729 getNextButton()730 public Button getNextButton() { 731 return mNextButton; 732 } 733 } 734