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 android.app.ActionBar; 20 import android.app.Activity; 21 import android.app.Fragment; 22 import android.app.FragmentManager; 23 import android.app.FragmentTransaction; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.SharedPreferences; 30 import android.content.pm.ActivityInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.PackageManager.NameNotFoundException; 33 import android.content.pm.ResolveInfo; 34 import android.content.res.Configuration; 35 import android.content.res.TypedArray; 36 import android.content.res.XmlResourceParser; 37 import android.nfc.NfcAdapter; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.INetworkManagementService; 41 import android.os.Message; 42 import android.os.RemoteException; 43 import android.os.ServiceManager; 44 import android.os.UserHandle; 45 import android.os.UserManager; 46 import android.preference.Preference; 47 import android.preference.PreferenceFragment; 48 import android.preference.PreferenceManager; 49 import android.preference.PreferenceScreen; 50 import android.text.TextUtils; 51 import android.transition.TransitionManager; 52 import android.util.AttributeSet; 53 import android.util.Log; 54 import android.util.TypedValue; 55 import android.util.Xml; 56 import android.view.Menu; 57 import android.view.MenuInflater; 58 import android.view.MenuItem; 59 import android.view.View; 60 import android.view.View.OnClickListener; 61 import android.view.ViewGroup; 62 import android.widget.Button; 63 import android.widget.SearchView; 64 65 import com.android.internal.util.ArrayUtils; 66 import com.android.internal.util.XmlUtils; 67 import com.android.settings.accessibility.AccessibilitySettings; 68 import com.android.settings.accessibility.CaptionPropertiesFragment; 69 import com.android.settings.accounts.AccountSettings; 70 import com.android.settings.accounts.AccountSyncSettings; 71 import com.android.settings.applications.InstalledAppDetails; 72 import com.android.settings.applications.ManageApplications; 73 import com.android.settings.applications.ProcessStatsUi; 74 import com.android.settings.bluetooth.BluetoothSettings; 75 import com.android.settings.dashboard.DashboardCategory; 76 import com.android.settings.dashboard.DashboardSummary; 77 import com.android.settings.dashboard.DashboardTile; 78 import com.android.settings.dashboard.NoHomeDialogFragment; 79 import com.android.settings.dashboard.SearchResultsSummary; 80 import com.android.settings.deviceinfo.Memory; 81 import com.android.settings.deviceinfo.UsbSettings; 82 import com.android.settings.fuelgauge.BatterySaverSettings; 83 import com.android.settings.fuelgauge.PowerUsageSummary; 84 import com.android.settings.notification.NotificationAppList; 85 import com.android.settings.notification.OtherSoundSettings; 86 import com.android.settings.quicklaunch.QuickLaunchSettings; 87 import com.android.settings.search.DynamicIndexableContentMonitor; 88 import com.android.settings.search.Index; 89 import com.android.settings.inputmethod.InputMethodAndLanguageSettings; 90 import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; 91 import com.android.settings.inputmethod.SpellCheckersSettings; 92 import com.android.settings.inputmethod.UserDictionaryList; 93 import com.android.settings.location.LocationSettings; 94 import com.android.settings.nfc.AndroidBeam; 95 import com.android.settings.nfc.PaymentSettings; 96 import com.android.settings.notification.AppNotificationSettings; 97 import com.android.settings.notification.ConditionProviderSettings; 98 import com.android.settings.notification.NotificationAccessSettings; 99 import com.android.settings.notification.NotificationSettings; 100 import com.android.settings.notification.NotificationStation; 101 import com.android.settings.notification.ZenModeSettings; 102 import com.android.settings.print.PrintJobSettingsFragment; 103 import com.android.settings.print.PrintSettingsFragment; 104 import com.android.settings.sim.SimSettings; 105 import com.android.settings.tts.TextToSpeechSettings; 106 import com.android.settings.users.UserSettings; 107 import com.android.settings.voice.VoiceInputSettings; 108 import com.android.settings.vpn2.VpnSettings; 109 import com.android.settings.wfd.WifiDisplaySettings; 110 import com.android.settings.widget.SwitchBar; 111 import com.android.settings.wifi.AdvancedWifiSettings; 112 import com.android.settings.wifi.SavedAccessPointsWifiSettings; 113 import com.android.settings.wifi.WifiSettings; 114 import com.android.settings.wifi.p2p.WifiP2pSettings; 115 116 import org.xmlpull.v1.XmlPullParser; 117 import org.xmlpull.v1.XmlPullParserException; 118 119 import java.io.IOException; 120 import java.util.ArrayList; 121 import java.util.List; 122 import java.util.Set; 123 124 import static com.android.settings.dashboard.DashboardTile.TILE_ID_UNDEFINED; 125 126 public class SettingsActivity extends Activity 127 implements PreferenceManager.OnPreferenceTreeClickListener, 128 PreferenceFragment.OnPreferenceStartFragmentCallback, 129 ButtonBarHandler, FragmentManager.OnBackStackChangedListener, 130 SearchView.OnQueryTextListener, SearchView.OnCloseListener, 131 MenuItem.OnActionExpandListener { 132 133 private static final String LOG_TAG = "Settings"; 134 135 // Constants for state save/restore 136 private static final String SAVE_KEY_CATEGORIES = ":settings:categories"; 137 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded"; 138 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query"; 139 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up"; 140 private static final String SAVE_KEY_SHOW_SEARCH = ":settings:show_search"; 141 private static final String SAVE_KEY_HOME_ACTIVITIES_COUNT = ":settings:home_activities_count"; 142 143 /** 144 * When starting this activity, the invoking Intent can contain this extra 145 * string to specify which fragment should be initially displayed. 146 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity 147 * will call isValidFragment() to confirm that the fragment class name is valid for this 148 * activity. 149 */ 150 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment"; 151 152 /** 153 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, 154 * this extra can also be specified to supply a Bundle of arguments to pass 155 * to that fragment when it is instantiated during the initial creation 156 * of the activity. 157 */ 158 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"; 159 160 /** 161 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS} 162 */ 163 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; 164 165 public static final String BACK_STACK_PREFS = ":settings:prefs"; 166 167 // extras that allow any preference activity to be launched as part of a wizard 168 169 // show Back and Next buttons? takes boolean parameter 170 // Back will then return RESULT_CANCELED and Next RESULT_OK 171 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar"; 172 173 // add a Skip button? 174 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip"; 175 176 // specify custom text for the Back or Next buttons, or cause a button to not appear 177 // at all by setting it to null 178 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text"; 179 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text"; 180 181 /** 182 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT}, 183 * those extra can also be specify to supply the title or title res id to be shown for 184 * that fragment. 185 */ 186 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title"; 187 public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID = 188 ":settings:show_fragment_title_resid"; 189 public static final String EXTRA_SHOW_FRAGMENT_AS_SHORTCUT = 190 ":settings:show_fragment_as_shortcut"; 191 192 public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING = 193 ":settings:show_fragment_as_subsetting"; 194 195 private static final String META_DATA_KEY_FRAGMENT_CLASS = 196 "com.android.settings.FRAGMENT_CLASS"; 197 198 private static final String EXTRA_UI_OPTIONS = "settings:ui_options"; 199 200 private static final String EMPTY_QUERY = ""; 201 202 private static boolean sShowNoHomeNotice = false; 203 204 private String mFragmentClass; 205 206 private CharSequence mInitialTitle; 207 private int mInitialTitleResId; 208 209 // Show only these settings for restricted users 210 private int[] SETTINGS_FOR_RESTRICTED = { 211 R.id.wireless_section, 212 R.id.wifi_settings, 213 R.id.bluetooth_settings, 214 R.id.data_usage_settings, 215 R.id.sim_settings, 216 R.id.wireless_settings, 217 R.id.device_section, 218 R.id.notification_settings, 219 R.id.display_settings, 220 R.id.storage_settings, 221 R.id.application_settings, 222 R.id.battery_settings, 223 R.id.personal_section, 224 R.id.location_settings, 225 R.id.security_settings, 226 R.id.language_settings, 227 R.id.user_settings, 228 R.id.account_settings, 229 R.id.system_section, 230 R.id.date_time_settings, 231 R.id.about_settings, 232 R.id.accessibility_settings, 233 R.id.print_settings, 234 R.id.nfc_payment_settings, 235 R.id.home_settings, 236 R.id.dashboard 237 }; 238 239 private static final String[] ENTRY_FRAGMENTS = { 240 WirelessSettings.class.getName(), 241 WifiSettings.class.getName(), 242 AdvancedWifiSettings.class.getName(), 243 SavedAccessPointsWifiSettings.class.getName(), 244 BluetoothSettings.class.getName(), 245 SimSettings.class.getName(), 246 TetherSettings.class.getName(), 247 WifiP2pSettings.class.getName(), 248 VpnSettings.class.getName(), 249 DateTimeSettings.class.getName(), 250 LocalePicker.class.getName(), 251 InputMethodAndLanguageSettings.class.getName(), 252 VoiceInputSettings.class.getName(), 253 SpellCheckersSettings.class.getName(), 254 UserDictionaryList.class.getName(), 255 UserDictionarySettings.class.getName(), 256 HomeSettings.class.getName(), 257 DisplaySettings.class.getName(), 258 DeviceInfoSettings.class.getName(), 259 ManageApplications.class.getName(), 260 ProcessStatsUi.class.getName(), 261 NotificationStation.class.getName(), 262 LocationSettings.class.getName(), 263 SecuritySettings.class.getName(), 264 UsageAccessSettings.class.getName(), 265 PrivacySettings.class.getName(), 266 DeviceAdminSettings.class.getName(), 267 AccessibilitySettings.class.getName(), 268 CaptionPropertiesFragment.class.getName(), 269 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(), 270 TextToSpeechSettings.class.getName(), 271 Memory.class.getName(), 272 DevelopmentSettings.class.getName(), 273 UsbSettings.class.getName(), 274 AndroidBeam.class.getName(), 275 WifiDisplaySettings.class.getName(), 276 PowerUsageSummary.class.getName(), 277 AccountSyncSettings.class.getName(), 278 AccountSettings.class.getName(), 279 CryptKeeperSettings.class.getName(), 280 DataUsageSummary.class.getName(), 281 DreamSettings.class.getName(), 282 UserSettings.class.getName(), 283 NotificationAccessSettings.class.getName(), 284 ConditionProviderSettings.class.getName(), 285 PrintSettingsFragment.class.getName(), 286 PrintJobSettingsFragment.class.getName(), 287 TrustedCredentialsSettings.class.getName(), 288 PaymentSettings.class.getName(), 289 KeyboardLayoutPickerFragment.class.getName(), 290 ZenModeSettings.class.getName(), 291 NotificationSettings.class.getName(), 292 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(), 293 ChooseLockPattern.ChooseLockPatternFragment.class.getName(), 294 InstalledAppDetails.class.getName(), 295 BatterySaverSettings.class.getName(), 296 NotificationAppList.class.getName(), 297 AppNotificationSettings.class.getName(), 298 OtherSoundSettings.class.getName(), 299 QuickLaunchSettings.class.getName(), 300 ApnSettings.class.getName() 301 }; 302 303 304 private static final String[] LIKE_SHORTCUT_INTENT_ACTION_ARRAY = { 305 "android.settings.APPLICATION_DETAILS_SETTINGS" 306 }; 307 308 private SharedPreferences mDevelopmentPreferences; 309 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener; 310 311 private boolean mBatteryPresent = true; 312 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { 313 @Override 314 public void onReceive(Context context, Intent intent) { 315 String action = intent.getAction(); 316 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { 317 boolean batteryPresent = Utils.isBatteryPresent(intent); 318 319 if (mBatteryPresent != batteryPresent) { 320 mBatteryPresent = batteryPresent; 321 invalidateCategories(true); 322 } 323 } 324 } 325 }; 326 327 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor = 328 new DynamicIndexableContentMonitor(); 329 330 private ActionBar mActionBar; 331 private SwitchBar mSwitchBar; 332 333 private Button mNextButton; 334 335 private boolean mDisplayHomeAsUpEnabled; 336 private boolean mDisplaySearch; 337 338 private boolean mIsShowingDashboard; 339 private boolean mIsShortcut; 340 341 private ViewGroup mContent; 342 343 private SearchView mSearchView; 344 private MenuItem mSearchMenuItem; 345 private boolean mSearchMenuItemExpanded = false; 346 private SearchResultsSummary mSearchResultsFragment; 347 private String mSearchQuery; 348 349 // Categories 350 private ArrayList<DashboardCategory> mCategories = new ArrayList<DashboardCategory>(); 351 352 private static final String MSG_DATA_FORCE_REFRESH = "msg_data_force_refresh"; 353 private static final int MSG_BUILD_CATEGORIES = 1; 354 private Handler mHandler = new Handler() { 355 @Override 356 public void handleMessage(Message msg) { 357 switch (msg.what) { 358 case MSG_BUILD_CATEGORIES: { 359 final boolean forceRefresh = msg.getData().getBoolean(MSG_DATA_FORCE_REFRESH); 360 if (forceRefresh) { 361 buildDashboardCategories(mCategories); 362 } 363 } break; 364 } 365 } 366 }; 367 368 private boolean mNeedToRevertToInitialFragment = false; 369 private int mHomeActivitiesCount = 1; 370 371 private Intent mResultIntentData; 372 getSwitchBar()373 public SwitchBar getSwitchBar() { 374 return mSwitchBar; 375 } 376 getDashboardCategories(boolean forceRefresh)377 public List<DashboardCategory> getDashboardCategories(boolean forceRefresh) { 378 if (forceRefresh || mCategories.size() == 0) { 379 buildDashboardCategories(mCategories); 380 } 381 return mCategories; 382 } 383 384 @Override onPreferenceStartFragment(PreferenceFragment caller, Preference pref)385 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) { 386 // Override the fragment title for Wallpaper settings 387 int titleRes = pref.getTitleRes(); 388 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) { 389 titleRes = R.string.wallpaper_settings_fragment_title; 390 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName()) 391 && UserHandle.myUserId() != UserHandle.USER_OWNER) { 392 if (UserManager.get(this).isLinkedUser()) { 393 titleRes = R.string.profile_info_settings_title; 394 } else { 395 titleRes = R.string.user_info_settings_title; 396 } 397 } 398 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(), 399 null, 0); 400 return true; 401 } 402 403 @Override onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)404 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 405 return false; 406 } 407 invalidateCategories(boolean forceRefresh)408 private void invalidateCategories(boolean forceRefresh) { 409 if (!mHandler.hasMessages(MSG_BUILD_CATEGORIES)) { 410 Message msg = new Message(); 411 msg.what = MSG_BUILD_CATEGORIES; 412 msg.getData().putBoolean(MSG_DATA_FORCE_REFRESH, forceRefresh); 413 } 414 } 415 416 @Override onConfigurationChanged(Configuration newConfig)417 public void onConfigurationChanged(Configuration newConfig) { 418 super.onConfigurationChanged(newConfig); 419 Index.getInstance(this).update(); 420 } 421 422 @Override onStart()423 protected void onStart() { 424 super.onStart(); 425 426 if (mNeedToRevertToInitialFragment) { 427 revertToInitialFragment(); 428 } 429 } 430 431 @Override onCreateOptionsMenu(Menu menu)432 public boolean onCreateOptionsMenu(Menu menu) { 433 if (!mDisplaySearch) { 434 return false; 435 } 436 437 MenuInflater inflater = getMenuInflater(); 438 inflater.inflate(R.menu.options_menu, menu); 439 440 // Cache the search query (can be overriden by the OnQueryTextListener) 441 final String query = mSearchQuery; 442 443 mSearchMenuItem = menu.findItem(R.id.search); 444 mSearchView = (SearchView) mSearchMenuItem.getActionView(); 445 446 if (mSearchMenuItem == null || mSearchView == null) { 447 return false; 448 } 449 450 if (mSearchResultsFragment != null) { 451 mSearchResultsFragment.setSearchView(mSearchView); 452 } 453 454 mSearchMenuItem.setOnActionExpandListener(this); 455 mSearchView.setOnQueryTextListener(this); 456 mSearchView.setOnCloseListener(this); 457 458 if (mSearchMenuItemExpanded) { 459 mSearchMenuItem.expandActionView(); 460 } 461 mSearchView.setQuery(query, true /* submit */); 462 463 return true; 464 } 465 isShortCutIntent(final Intent intent)466 private static boolean isShortCutIntent(final Intent intent) { 467 Set<String> categories = intent.getCategories(); 468 return (categories != null) && categories.contains("com.android.settings.SHORTCUT"); 469 } 470 isLikeShortCutIntent(final Intent intent)471 private static boolean isLikeShortCutIntent(final Intent intent) { 472 String action = intent.getAction(); 473 if (action == null) { 474 return false; 475 } 476 for (int i = 0; i < LIKE_SHORTCUT_INTENT_ACTION_ARRAY.length; i++) { 477 if (LIKE_SHORTCUT_INTENT_ACTION_ARRAY[i].equals(action)) return true; 478 } 479 return false; 480 } 481 482 @Override onCreate(Bundle savedState)483 protected void onCreate(Bundle savedState) { 484 super.onCreate(savedState); 485 486 // Should happen before any call to getIntent() 487 getMetaData(); 488 489 final Intent intent = getIntent(); 490 if (intent.hasExtra(EXTRA_UI_OPTIONS)) { 491 getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0)); 492 } 493 494 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE, 495 Context.MODE_PRIVATE); 496 497 // Getting Intent properties can only be done after the super.onCreate(...) 498 final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT); 499 500 mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) || 501 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false); 502 503 final ComponentName cn = intent.getComponent(); 504 final String className = cn.getClassName(); 505 506 mIsShowingDashboard = className.equals(Settings.class.getName()); 507 508 // This is a "Sub Settings" when: 509 // - this is a real SubSettings 510 // - or :settings:show_fragment_as_subsetting is passed to the Intent 511 final boolean isSubSettings = className.equals(SubSettings.class.getName()) || 512 intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false); 513 514 // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets 515 if (isSubSettings) { 516 // Check also that we are not a Theme Dialog as we don't want to override them 517 final int themeResId = getThemeResId(); 518 if (themeResId != R.style.Theme_DialogWhenLarge && 519 themeResId != R.style.Theme_SubSettingsDialogWhenLarge) { 520 setTheme(R.style.Theme_SubSettings); 521 } 522 } 523 524 setContentView(mIsShowingDashboard ? 525 R.layout.settings_main_dashboard : R.layout.settings_main_prefs); 526 527 mContent = (ViewGroup) findViewById(R.id.main_content); 528 529 getFragmentManager().addOnBackStackChangedListener(this); 530 531 if (mIsShowingDashboard) { 532 Index.getInstance(getApplicationContext()).update(); 533 } 534 535 if (savedState != null) { 536 // We are restarting from a previous saved state; used that to initialize, instead 537 // of starting fresh. 538 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED); 539 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY); 540 541 setTitleFromIntent(intent); 542 543 ArrayList<DashboardCategory> categories = 544 savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES); 545 if (categories != null) { 546 mCategories.clear(); 547 mCategories.addAll(categories); 548 setTitleFromBackStack(); 549 } 550 551 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP); 552 mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH); 553 mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, 554 1 /* one home activity by default */); 555 } else { 556 if (!mIsShowingDashboard) { 557 // Search is shown we are launched thru a Settings "shortcut". UP will be shown 558 // only if it is a sub settings 559 if (mIsShortcut) { 560 mDisplayHomeAsUpEnabled = isSubSettings; 561 mDisplaySearch = false; 562 } else if (isSubSettings) { 563 mDisplayHomeAsUpEnabled = true; 564 mDisplaySearch = true; 565 } else { 566 mDisplayHomeAsUpEnabled = false; 567 mDisplaySearch = false; 568 } 569 setTitleFromIntent(intent); 570 571 Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); 572 switchToFragment(initialFragmentName, initialArguments, true, false, 573 mInitialTitleResId, mInitialTitle, false); 574 } else { 575 // No UP affordance if we are displaying the main Dashboard 576 mDisplayHomeAsUpEnabled = false; 577 // Show Search affordance 578 mDisplaySearch = true; 579 mInitialTitleResId = R.string.dashboard_title; 580 switchToFragment(DashboardSummary.class.getName(), null, false, false, 581 mInitialTitleResId, mInitialTitle, false); 582 } 583 } 584 585 mActionBar = getActionBar(); 586 if (mActionBar != null) { 587 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled); 588 mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled); 589 } 590 mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar); 591 592 // see if we should show Back/Next buttons 593 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) { 594 595 View buttonBar = findViewById(R.id.button_bar); 596 if (buttonBar != null) { 597 buttonBar.setVisibility(View.VISIBLE); 598 599 Button backButton = (Button)findViewById(R.id.back_button); 600 backButton.setOnClickListener(new OnClickListener() { 601 public void onClick(View v) { 602 setResult(RESULT_CANCELED, getResultIntentData()); 603 finish(); 604 } 605 }); 606 Button skipButton = (Button)findViewById(R.id.skip_button); 607 skipButton.setOnClickListener(new OnClickListener() { 608 public void onClick(View v) { 609 setResult(RESULT_OK, getResultIntentData()); 610 finish(); 611 } 612 }); 613 mNextButton = (Button)findViewById(R.id.next_button); 614 mNextButton.setOnClickListener(new OnClickListener() { 615 public void onClick(View v) { 616 setResult(RESULT_OK, getResultIntentData()); 617 finish(); 618 } 619 }); 620 621 // set our various button parameters 622 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) { 623 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT); 624 if (TextUtils.isEmpty(buttonText)) { 625 mNextButton.setVisibility(View.GONE); 626 } 627 else { 628 mNextButton.setText(buttonText); 629 } 630 } 631 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) { 632 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT); 633 if (TextUtils.isEmpty(buttonText)) { 634 backButton.setVisibility(View.GONE); 635 } 636 else { 637 backButton.setText(buttonText); 638 } 639 } 640 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) { 641 skipButton.setVisibility(View.VISIBLE); 642 } 643 } 644 } 645 646 mHomeActivitiesCount = getHomeActivitiesCount(); 647 } 648 getHomeActivitiesCount()649 private int getHomeActivitiesCount() { 650 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>(); 651 getPackageManager().getHomeActivities(homeApps); 652 return homeApps.size(); 653 } 654 setTitleFromIntent(Intent intent)655 private void setTitleFromIntent(Intent intent) { 656 final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1); 657 if (initialTitleResId > 0) { 658 mInitialTitle = null; 659 mInitialTitleResId = initialTitleResId; 660 setTitle(mInitialTitleResId); 661 } else { 662 mInitialTitleResId = -1; 663 final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE); 664 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle(); 665 setTitle(mInitialTitle); 666 } 667 } 668 669 @Override onBackStackChanged()670 public void onBackStackChanged() { 671 setTitleFromBackStack(); 672 } 673 setTitleFromBackStack()674 private int setTitleFromBackStack() { 675 final int count = getFragmentManager().getBackStackEntryCount(); 676 677 if (count == 0) { 678 if (mInitialTitleResId > 0) { 679 setTitle(mInitialTitleResId); 680 } else { 681 setTitle(mInitialTitle); 682 } 683 return 0; 684 } 685 686 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1); 687 setTitleFromBackStackEntry(bse); 688 689 return count; 690 } 691 setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse)692 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) { 693 final CharSequence title; 694 final int titleRes = bse.getBreadCrumbTitleRes(); 695 if (titleRes > 0) { 696 title = getText(titleRes); 697 } else { 698 title = bse.getBreadCrumbTitle(); 699 } 700 if (title != null) { 701 setTitle(title); 702 } 703 } 704 705 @Override onSaveInstanceState(Bundle outState)706 protected void onSaveInstanceState(Bundle outState) { 707 super.onSaveInstanceState(outState); 708 709 if (mCategories.size() > 0) { 710 outState.putParcelableArrayList(SAVE_KEY_CATEGORIES, mCategories); 711 } 712 713 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled); 714 outState.putBoolean(SAVE_KEY_SHOW_SEARCH, mDisplaySearch); 715 716 if (mDisplaySearch) { 717 // The option menus are created if the ActionBar is visible and they are also created 718 // asynchronously. If you launch Settings with an Intent action like 719 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked 720 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search 721 // menu item and search view are null. 722 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded(); 723 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded); 724 725 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY; 726 outState.putString(SAVE_KEY_SEARCH_QUERY, query); 727 } 728 729 outState.putInt(SAVE_KEY_HOME_ACTIVITIES_COUNT, mHomeActivitiesCount); 730 } 731 732 @Override onResume()733 public void onResume() { 734 super.onResume(); 735 736 final int newHomeActivityCount = getHomeActivitiesCount(); 737 if (newHomeActivityCount != mHomeActivitiesCount) { 738 mHomeActivitiesCount = newHomeActivityCount; 739 invalidateCategories(true); 740 } 741 742 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() { 743 @Override 744 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 745 invalidateCategories(true); 746 } 747 }; 748 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener( 749 mDevelopmentPreferencesListener); 750 751 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 752 753 mDynamicIndexableContentMonitor.register(this); 754 755 if(mDisplaySearch && !TextUtils.isEmpty(mSearchQuery)) { 756 onQueryTextSubmit(mSearchQuery); 757 } 758 } 759 760 @Override onPause()761 public void onPause() { 762 super.onPause(); 763 764 unregisterReceiver(mBatteryInfoReceiver); 765 mDynamicIndexableContentMonitor.unregister(); 766 } 767 768 @Override onDestroy()769 public void onDestroy() { 770 super.onDestroy(); 771 772 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener( 773 mDevelopmentPreferencesListener); 774 mDevelopmentPreferencesListener = null; 775 } 776 isValidFragment(String fragmentName)777 protected boolean isValidFragment(String fragmentName) { 778 // Almost all fragments are wrapped in this, 779 // except for a few that have their own activities. 780 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) { 781 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true; 782 } 783 return false; 784 } 785 786 @Override getIntent()787 public Intent getIntent() { 788 Intent superIntent = super.getIntent(); 789 String startingFragment = getStartingFragmentClass(superIntent); 790 // This is called from super.onCreate, isMultiPane() is not yet reliable 791 // Do not use onIsHidingHeaders either, which relies itself on this method 792 if (startingFragment != null) { 793 Intent modIntent = new Intent(superIntent); 794 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment); 795 Bundle args = superIntent.getExtras(); 796 if (args != null) { 797 args = new Bundle(args); 798 } else { 799 args = new Bundle(); 800 } 801 args.putParcelable("intent", superIntent); 802 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); 803 return modIntent; 804 } 805 return superIntent; 806 } 807 808 /** 809 * Checks if the component name in the intent is different from the Settings class and 810 * returns the class name to load as a fragment. 811 */ getStartingFragmentClass(Intent intent)812 private String getStartingFragmentClass(Intent intent) { 813 if (mFragmentClass != null) return mFragmentClass; 814 815 String intentClass = intent.getComponent().getClassName(); 816 if (intentClass.equals(getClass().getName())) return null; 817 818 if ("com.android.settings.ManageApplications".equals(intentClass) 819 || "com.android.settings.RunningServices".equals(intentClass) 820 || "com.android.settings.applications.StorageUse".equals(intentClass)) { 821 // Old names of manage apps. 822 intentClass = com.android.settings.applications.ManageApplications.class.getName(); 823 } 824 825 return intentClass; 826 } 827 828 /** 829 * Start a new fragment containing a preference panel. If the preferences 830 * are being displayed in multi-pane mode, the given fragment class will 831 * be instantiated and placed in the appropriate pane. If running in 832 * single-pane mode, a new activity will be launched in which to show the 833 * fragment. 834 * 835 * @param fragmentClass Full name of the class implementing the fragment. 836 * @param args Any desired arguments to supply to the fragment. 837 * @param titleRes Optional resource identifier of the title of this 838 * fragment. 839 * @param titleText Optional text of the title of this fragment. 840 * @param resultTo Optional fragment that result data should be sent to. 841 * If non-null, resultTo.onActivityResult() will be called when this 842 * preference panel is done. The launched panel must use 843 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done. 844 * @param resultRequestCode If resultTo is non-null, this is the caller's 845 * request code to be received with the result. 846 */ startPreferencePanel(String fragmentClass, Bundle args, int titleRes, CharSequence titleText, Fragment resultTo, int resultRequestCode)847 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes, 848 CharSequence titleText, Fragment resultTo, int resultRequestCode) { 849 String title = null; 850 if (titleRes < 0) { 851 if (titleText != null) { 852 title = titleText.toString(); 853 } else { 854 // There not much we can do in that case 855 title = ""; 856 } 857 } 858 Utils.startWithFragment(this, fragmentClass, args, resultTo, resultRequestCode, 859 titleRes, title, mIsShortcut); 860 } 861 862 /** 863 * Start a new fragment in a new activity containing a preference panel for a given user. If the 864 * preferences are being displayed in multi-pane mode, the given fragment class will be 865 * instantiated and placed in the appropriate pane. If running in single-pane mode, a new 866 * activity will be launched in which to show the fragment. 867 * 868 * @param fragmentClass Full name of the class implementing the fragment. 869 * @param args Any desired arguments to supply to the fragment. 870 * @param titleRes Optional resource identifier of the title of this fragment. 871 * @param titleText Optional text of the title of this fragment. 872 * @param userHandle The user for which the panel has to be started. 873 */ startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes, CharSequence titleText, UserHandle userHandle)874 public void startPreferencePanelAsUser(String fragmentClass, Bundle args, int titleRes, 875 CharSequence titleText, UserHandle userHandle) { 876 String title = null; 877 if (titleRes < 0) { 878 if (titleText != null) { 879 title = titleText.toString(); 880 } else { 881 // There not much we can do in that case 882 title = ""; 883 } 884 } 885 Utils.startWithFragmentAsUser(this, fragmentClass, args, 886 titleRes, title, mIsShortcut, userHandle); 887 } 888 889 /** 890 * Called by a preference panel fragment to finish itself. 891 * 892 * @param caller The fragment that is asking to be finished. 893 * @param resultCode Optional result code to send back to the original 894 * launching fragment. 895 * @param resultData Optional result data to send back to the original 896 * launching fragment. 897 */ finishPreferencePanel(Fragment caller, int resultCode, Intent resultData)898 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) { 899 setResult(resultCode, resultData); 900 finish(); 901 } 902 903 /** 904 * Start a new fragment. 905 * 906 * @param fragment The fragment to start 907 * @param push If true, the current fragment will be pushed onto the back stack. If false, 908 * the current fragment will be replaced. 909 */ startPreferenceFragment(Fragment fragment, boolean push)910 public void startPreferenceFragment(Fragment fragment, boolean push) { 911 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 912 transaction.replace(R.id.main_content, fragment); 913 if (push) { 914 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 915 transaction.addToBackStack(BACK_STACK_PREFS); 916 } else { 917 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); 918 } 919 transaction.commitAllowingStateLoss(); 920 } 921 922 /** 923 * Switch to a specific Fragment with taking care of validation, Title and BackStack 924 */ switchToFragment(String fragmentName, Bundle args, boolean validate, boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition)925 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate, 926 boolean addToBackStack, int titleResId, CharSequence title, boolean withTransition) { 927 if (validate && !isValidFragment(fragmentName)) { 928 throw new IllegalArgumentException("Invalid fragment for this activity: " 929 + fragmentName); 930 } 931 Fragment f = Fragment.instantiate(this, fragmentName, args); 932 FragmentTransaction transaction = getFragmentManager().beginTransaction(); 933 transaction.replace(R.id.main_content, f); 934 if (withTransition) { 935 TransitionManager.beginDelayedTransition(mContent); 936 } 937 if (addToBackStack) { 938 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS); 939 } 940 if (titleResId > 0) { 941 transaction.setBreadCrumbTitle(titleResId); 942 } else if (title != null) { 943 transaction.setBreadCrumbTitle(title); 944 } 945 transaction.commitAllowingStateLoss(); 946 getFragmentManager().executePendingTransactions(); 947 return f; 948 } 949 950 /** 951 * Called when the activity needs its list of categories/tiles built. 952 * 953 * @param categories The list in which to place the tiles categories. 954 */ buildDashboardCategories(List<DashboardCategory> categories)955 private void buildDashboardCategories(List<DashboardCategory> categories) { 956 categories.clear(); 957 loadCategoriesFromResource(R.xml.dashboard_categories, categories); 958 updateTilesList(categories); 959 } 960 961 /** 962 * Parse the given XML file as a categories description, adding each 963 * parsed categories and tiles into the target list. 964 * 965 * @param resid The XML resource to load and parse. 966 * @param target The list in which the parsed categories and tiles should be placed. 967 */ loadCategoriesFromResource(int resid, List<DashboardCategory> target)968 private void loadCategoriesFromResource(int resid, List<DashboardCategory> target) { 969 XmlResourceParser parser = null; 970 try { 971 parser = getResources().getXml(resid); 972 AttributeSet attrs = Xml.asAttributeSet(parser); 973 974 int type; 975 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 976 && type != XmlPullParser.START_TAG) { 977 // Parse next until start tag is found 978 } 979 980 String nodeName = parser.getName(); 981 if (!"dashboard-categories".equals(nodeName)) { 982 throw new RuntimeException( 983 "XML document must start with <preference-categories> tag; found" 984 + nodeName + " at " + parser.getPositionDescription()); 985 } 986 987 Bundle curBundle = null; 988 989 final int outerDepth = parser.getDepth(); 990 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 991 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 992 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 993 continue; 994 } 995 996 nodeName = parser.getName(); 997 if ("dashboard-category".equals(nodeName)) { 998 DashboardCategory category = new DashboardCategory(); 999 1000 TypedArray sa = obtainStyledAttributes( 1001 attrs, com.android.internal.R.styleable.PreferenceHeader); 1002 category.id = sa.getResourceId( 1003 com.android.internal.R.styleable.PreferenceHeader_id, 1004 (int)DashboardCategory.CAT_ID_UNDEFINED); 1005 1006 TypedValue tv = sa.peekValue( 1007 com.android.internal.R.styleable.PreferenceHeader_title); 1008 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 1009 if (tv.resourceId != 0) { 1010 category.titleRes = tv.resourceId; 1011 } else { 1012 category.title = tv.string; 1013 } 1014 } 1015 sa.recycle(); 1016 1017 final int innerDepth = parser.getDepth(); 1018 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1019 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { 1020 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1021 continue; 1022 } 1023 1024 String innerNodeName = parser.getName(); 1025 if (innerNodeName.equals("dashboard-tile")) { 1026 DashboardTile tile = new DashboardTile(); 1027 1028 sa = obtainStyledAttributes( 1029 attrs, com.android.internal.R.styleable.PreferenceHeader); 1030 tile.id = sa.getResourceId( 1031 com.android.internal.R.styleable.PreferenceHeader_id, 1032 (int)TILE_ID_UNDEFINED); 1033 tv = sa.peekValue( 1034 com.android.internal.R.styleable.PreferenceHeader_title); 1035 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 1036 if (tv.resourceId != 0) { 1037 tile.titleRes = tv.resourceId; 1038 } else { 1039 tile.title = tv.string; 1040 } 1041 } 1042 tv = sa.peekValue( 1043 com.android.internal.R.styleable.PreferenceHeader_summary); 1044 if (tv != null && tv.type == TypedValue.TYPE_STRING) { 1045 if (tv.resourceId != 0) { 1046 tile.summaryRes = tv.resourceId; 1047 } else { 1048 tile.summary = tv.string; 1049 } 1050 } 1051 tile.iconRes = sa.getResourceId( 1052 com.android.internal.R.styleable.PreferenceHeader_icon, 0); 1053 tile.fragment = sa.getString( 1054 com.android.internal.R.styleable.PreferenceHeader_fragment); 1055 sa.recycle(); 1056 1057 if (curBundle == null) { 1058 curBundle = new Bundle(); 1059 } 1060 1061 final int innerDepth2 = parser.getDepth(); 1062 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1063 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth2)) { 1064 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1065 continue; 1066 } 1067 1068 String innerNodeName2 = parser.getName(); 1069 if (innerNodeName2.equals("extra")) { 1070 getResources().parseBundleExtra("extra", attrs, curBundle); 1071 XmlUtils.skipCurrentTag(parser); 1072 1073 } else if (innerNodeName2.equals("intent")) { 1074 tile.intent = Intent.parseIntent(getResources(), parser, attrs); 1075 1076 } else { 1077 XmlUtils.skipCurrentTag(parser); 1078 } 1079 } 1080 1081 if (curBundle.size() > 0) { 1082 tile.fragmentArguments = curBundle; 1083 curBundle = null; 1084 } 1085 1086 // Show the SIM Cards setting if there are more than 2 SIMs installed. 1087 if(tile.id != R.id.sim_settings || Utils.showSimCardTile(this)){ 1088 category.addTile(tile); 1089 } 1090 1091 } else { 1092 XmlUtils.skipCurrentTag(parser); 1093 } 1094 } 1095 1096 target.add(category); 1097 } else { 1098 XmlUtils.skipCurrentTag(parser); 1099 } 1100 } 1101 1102 } catch (XmlPullParserException e) { 1103 throw new RuntimeException("Error parsing categories", e); 1104 } catch (IOException e) { 1105 throw new RuntimeException("Error parsing categories", e); 1106 } finally { 1107 if (parser != null) parser.close(); 1108 } 1109 } 1110 updateTilesList(List<DashboardCategory> target)1111 private void updateTilesList(List<DashboardCategory> target) { 1112 final boolean showDev = mDevelopmentPreferences.getBoolean( 1113 DevelopmentSettings.PREF_SHOW, 1114 android.os.Build.TYPE.equals("eng")); 1115 1116 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 1117 1118 final int size = target.size(); 1119 for (int i = 0; i < size; i++) { 1120 1121 DashboardCategory category = target.get(i); 1122 1123 // Ids are integers, so downcasting is ok 1124 int id = (int) category.id; 1125 int n = category.getTilesCount() - 1; 1126 while (n >= 0) { 1127 1128 DashboardTile tile = category.getTile(n); 1129 boolean removeTile = false; 1130 id = (int) tile.id; 1131 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) { 1132 if (!Utils.updateTileToSpecificActivityFromMetaDataOrRemove(this, tile)) { 1133 removeTile = true; 1134 } 1135 } else if (id == R.id.wifi_settings) { 1136 // Remove WiFi Settings if WiFi service is not available. 1137 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { 1138 removeTile = true; 1139 } 1140 } else if (id == R.id.bluetooth_settings) { 1141 // Remove Bluetooth Settings if Bluetooth service is not available. 1142 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { 1143 removeTile = true; 1144 } 1145 } else if (id == R.id.data_usage_settings) { 1146 // Remove data usage when kernel module not enabled 1147 final INetworkManagementService netManager = INetworkManagementService.Stub 1148 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); 1149 try { 1150 if (!netManager.isBandwidthControlEnabled()) { 1151 removeTile = true; 1152 } 1153 } catch (RemoteException e) { 1154 // ignored 1155 } 1156 } else if (id == R.id.battery_settings) { 1157 // Remove battery settings when battery is not available. (e.g. TV) 1158 1159 if (!mBatteryPresent) { 1160 removeTile = true; 1161 } 1162 } else if (id == R.id.home_settings) { 1163 if (!updateHomeSettingTiles(tile)) { 1164 removeTile = true; 1165 } 1166 } else if (id == R.id.user_settings) { 1167 boolean hasMultipleUsers = 1168 ((UserManager) getSystemService(Context.USER_SERVICE)) 1169 .getUserCount() > 1; 1170 if (!UserHandle.MU_ENABLED 1171 || (!UserManager.supportsMultipleUsers() 1172 && !hasMultipleUsers) 1173 || Utils.isMonkeyRunning()) { 1174 removeTile = true; 1175 } 1176 } else if (id == R.id.nfc_payment_settings) { 1177 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { 1178 removeTile = true; 1179 } else { 1180 // Only show if NFC is on and we have the HCE feature 1181 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); 1182 if (adapter == null || !adapter.isEnabled() || 1183 !getPackageManager().hasSystemFeature( 1184 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) { 1185 removeTile = true; 1186 } 1187 } 1188 } else if (id == R.id.print_settings) { 1189 boolean hasPrintingSupport = getPackageManager().hasSystemFeature( 1190 PackageManager.FEATURE_PRINTING); 1191 if (!hasPrintingSupport) { 1192 removeTile = true; 1193 } 1194 } else if (id == R.id.development_settings) { 1195 if (!showDev || um.hasUserRestriction( 1196 UserManager.DISALLOW_DEBUGGING_FEATURES)) { 1197 removeTile = true; 1198 } 1199 } 1200 1201 if (UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 1202 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { 1203 removeTile = true; 1204 } 1205 1206 if (removeTile && n < category.getTilesCount()) { 1207 category.removeTile(n); 1208 } 1209 n--; 1210 } 1211 } 1212 } 1213 updateHomeSettingTiles(DashboardTile tile)1214 private boolean updateHomeSettingTiles(DashboardTile tile) { 1215 // Once we decide to show Home settings, keep showing it forever 1216 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE); 1217 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) { 1218 return true; 1219 } 1220 1221 try { 1222 mHomeActivitiesCount = getHomeActivitiesCount(); 1223 if (mHomeActivitiesCount < 2) { 1224 // When there's only one available home app, omit this settings 1225 // category entirely at the top level UI. If the user just 1226 // uninstalled the penultimate home app candidiate, we also 1227 // now tell them about why they aren't seeing 'Home' in the list. 1228 if (sShowNoHomeNotice) { 1229 sShowNoHomeNotice = false; 1230 NoHomeDialogFragment.show(this); 1231 } 1232 return false; 1233 } else { 1234 // Okay, we're allowing the Home settings category. Tell it, when 1235 // invoked via this front door, that we'll need to be told about the 1236 // case when the user uninstalls all but one home app. 1237 if (tile.fragmentArguments == null) { 1238 tile.fragmentArguments = new Bundle(); 1239 } 1240 tile.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true); 1241 } 1242 } catch (Exception e) { 1243 // Can't look up the home activity; bail on configuring the icon 1244 Log.w(LOG_TAG, "Problem looking up home activity!", e); 1245 } 1246 1247 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply(); 1248 return true; 1249 } 1250 getMetaData()1251 private void getMetaData() { 1252 try { 1253 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(), 1254 PackageManager.GET_META_DATA); 1255 if (ai == null || ai.metaData == null) return; 1256 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS); 1257 } catch (NameNotFoundException nnfe) { 1258 // No recovery 1259 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString()); 1260 } 1261 } 1262 1263 // give subclasses access to the Next button hasNextButton()1264 public boolean hasNextButton() { 1265 return mNextButton != null; 1266 } 1267 getNextButton()1268 public Button getNextButton() { 1269 return mNextButton; 1270 } 1271 1272 @Override shouldUpRecreateTask(Intent targetIntent)1273 public boolean shouldUpRecreateTask(Intent targetIntent) { 1274 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class)); 1275 } 1276 requestHomeNotice()1277 public static void requestHomeNotice() { 1278 sShowNoHomeNotice = true; 1279 } 1280 1281 @Override onQueryTextSubmit(String query)1282 public boolean onQueryTextSubmit(String query) { 1283 switchToSearchResultsFragmentIfNeeded(); 1284 mSearchQuery = query; 1285 return mSearchResultsFragment.onQueryTextSubmit(query); 1286 } 1287 1288 @Override onQueryTextChange(String newText)1289 public boolean onQueryTextChange(String newText) { 1290 mSearchQuery = newText; 1291 if (mSearchResultsFragment == null) { 1292 return false; 1293 } 1294 return mSearchResultsFragment.onQueryTextChange(newText); 1295 } 1296 1297 @Override onClose()1298 public boolean onClose() { 1299 return false; 1300 } 1301 1302 @Override onMenuItemActionExpand(MenuItem item)1303 public boolean onMenuItemActionExpand(MenuItem item) { 1304 if (item.getItemId() == mSearchMenuItem.getItemId()) { 1305 switchToSearchResultsFragmentIfNeeded(); 1306 } 1307 return true; 1308 } 1309 1310 @Override onMenuItemActionCollapse(MenuItem item)1311 public boolean onMenuItemActionCollapse(MenuItem item) { 1312 if (item.getItemId() == mSearchMenuItem.getItemId()) { 1313 if (mSearchMenuItemExpanded) { 1314 revertToInitialFragment(); 1315 } 1316 } 1317 return true; 1318 } 1319 switchToSearchResultsFragmentIfNeeded()1320 private void switchToSearchResultsFragmentIfNeeded() { 1321 if (mSearchResultsFragment != null) { 1322 return; 1323 } 1324 Fragment current = getFragmentManager().findFragmentById(R.id.main_content); 1325 if (current != null && current instanceof SearchResultsSummary) { 1326 mSearchResultsFragment = (SearchResultsSummary) current; 1327 } else { 1328 mSearchResultsFragment = (SearchResultsSummary) switchToFragment( 1329 SearchResultsSummary.class.getName(), null, false, true, 1330 R.string.search_results_title, null, true); 1331 } 1332 mSearchResultsFragment.setSearchView(mSearchView); 1333 mSearchMenuItemExpanded = true; 1334 } 1335 needToRevertToInitialFragment()1336 public void needToRevertToInitialFragment() { 1337 mNeedToRevertToInitialFragment = true; 1338 } 1339 revertToInitialFragment()1340 private void revertToInitialFragment() { 1341 mNeedToRevertToInitialFragment = false; 1342 mSearchResultsFragment = null; 1343 mSearchMenuItemExpanded = false; 1344 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS, 1345 FragmentManager.POP_BACK_STACK_INCLUSIVE); 1346 if (mSearchMenuItem != null) { 1347 mSearchMenuItem.collapseActionView(); 1348 } 1349 } 1350 getResultIntentData()1351 public Intent getResultIntentData() { 1352 return mResultIntentData; 1353 } 1354 setResultIntentData(Intent resultIntentData)1355 public void setResultIntentData(Intent resultIntentData) { 1356 mResultIntentData = resultIntentData; 1357 } 1358 } 1359