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