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