blob: 329e4bf87684c1987c829d33b04773fc49ae1052 [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
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
17package com.android.settings;
18
Svetoslav853e4712014-04-14 10:10:25 -070019import android.accessibilityservice.AccessibilityService;
20import android.accessibilityservice.AccessibilityServiceInfo;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080021import android.accounts.Account;
22import android.accounts.AccountManager;
23import android.accounts.OnAccountsUpdateListener;
24import android.app.ActionBar;
25import android.app.Activity;
26import android.app.Fragment;
27import android.app.FragmentManager;
28import android.app.FragmentTransaction;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080029import android.app.admin.DevicePolicyManager;
30import android.content.BroadcastReceiver;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070031import android.content.ComponentName;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080032import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.SharedPreferences;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.res.Configuration;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080041import android.content.res.TypedArray;
42import android.content.res.XmlResourceParser;
Svetoslav990159a2014-04-14 17:14:59 -070043import android.database.ContentObserver;
44import android.hardware.input.InputManager;
45import android.net.Uri;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080046import android.nfc.NfcAdapter;
47import android.os.Bundle;
48import android.os.Handler;
49import android.os.INetworkManagementService;
Svetoslav853e4712014-04-14 10:10:25 -070050import android.os.Looper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080051import android.os.Message;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080052import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.UserHandle;
55import android.os.UserManager;
56import android.preference.Preference;
57import android.preference.PreferenceFragment;
58import android.preference.PreferenceManager;
59import android.preference.PreferenceScreen;
Svetoslav853e4712014-04-14 10:10:25 -070060import android.print.PrintManager;
61import android.printservice.PrintService;
62import android.printservice.PrintServiceInfo;
Svetoslav990159a2014-04-14 17:14:59 -070063import android.provider.UserDictionary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080064import android.text.TextUtils;
65import android.util.AttributeSet;
66import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080067import android.util.TypedValue;
68import android.util.Xml;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070069import android.view.Menu;
70import android.view.MenuInflater;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080071import android.view.MenuItem;
72import android.view.View;
73import android.view.View.OnClickListener;
Svetoslav853e4712014-04-14 10:10:25 -070074import android.view.accessibility.AccessibilityManager;
Svetoslav990159a2014-04-14 17:14:59 -070075import android.view.inputmethod.InputMethodInfo;
76import android.view.inputmethod.InputMethodManager;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080077import android.widget.Button;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080078import android.widget.ListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080079
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070080import android.widget.SearchView;
Svetoslav853e4712014-04-14 10:10:25 -070081import com.android.internal.content.PackageMonitor;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080082import com.android.internal.util.ArrayUtils;
83import com.android.internal.util.XmlUtils;
84import com.android.settings.accessibility.AccessibilitySettings;
85import com.android.settings.accessibility.CaptionPropertiesFragment;
86import com.android.settings.accounts.AccountSyncSettings;
87import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.accounts.ManageAccountsSettings;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070089import com.android.settings.applications.InstalledAppDetails;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080090import com.android.settings.applications.ManageApplications;
91import com.android.settings.applications.ProcessStatsUi;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080092import com.android.settings.bluetooth.BluetoothSettings;
93import com.android.settings.dashboard.DashboardSummary;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -070094import com.android.settings.dashboard.Header;
95import com.android.settings.dashboard.HeaderAdapter;
96import com.android.settings.dashboard.NoHomeDialogFragment;
97import com.android.settings.dashboard.SearchResultsSummary;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080098import com.android.settings.deviceinfo.Memory;
99import com.android.settings.deviceinfo.UsbSettings;
100import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Megliofa7dc242014-03-12 19:24:43 -0700101import com.android.settings.search.Index;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800102import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
103import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
104import com.android.settings.inputmethod.SpellCheckersSettings;
105import com.android.settings.inputmethod.UserDictionaryList;
106import com.android.settings.location.LocationSettings;
107import com.android.settings.nfc.AndroidBeam;
108import com.android.settings.nfc.PaymentSettings;
109import com.android.settings.print.PrintJobSettingsFragment;
110import com.android.settings.print.PrintSettingsFragment;
Svetoslav853e4712014-04-14 10:10:25 -0700111import com.android.settings.search.SearchIndexableRaw;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800112import com.android.settings.tts.TextToSpeechSettings;
113import com.android.settings.users.UserSettings;
114import com.android.settings.vpn2.VpnSettings;
115import com.android.settings.wfd.WifiDisplaySettings;
116import com.android.settings.wifi.AdvancedWifiSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800117import com.android.settings.wifi.WifiSettings;
118import com.android.settings.wifi.p2p.WifiP2pSettings;
119import org.xmlpull.v1.XmlPullParser;
120import org.xmlpull.v1.XmlPullParserException;
121
122import java.io.IOException;
123import java.util.ArrayList;
124import java.util.Collections;
125import java.util.Comparator;
126import java.util.HashMap;
127import java.util.List;
128
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700129import static com.android.settings.dashboard.Header.HEADER_ID_UNDEFINED;
130
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800131public class SettingsActivity extends Activity
132 implements PreferenceManager.OnPreferenceTreeClickListener,
133 PreferenceFragment.OnPreferenceStartFragmentCallback,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700134 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
135 SearchView.OnQueryTextListener, SearchView.OnCloseListener,
136 MenuItem.OnActionExpandListener {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800137
138 private static final String LOG_TAG = "Settings";
139
140 // Constants for state save/restore
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700141 private static final String SAVE_KEY_HEADERS = ":settings:headers";
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700142 private static final String SAVE_KEY_SEARCH_MENU_EXPANDED = ":settings:search_menu_expanded";
143 private static final String SAVE_KEY_SEARCH_QUERY = ":settings:search_query";
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700144 private static final String SAVE_KEY_SHOW_HOME_AS_UP = ":settings:show_home_as_up";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800145
146 /**
147 * When starting this activity, the invoking Intent can contain this extra
148 * string to specify which fragment should be initially displayed.
149 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
150 * will call isValidFragment() to confirm that the fragment class name is valid for this
151 * activity.
152 */
153 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
154
155 /**
156 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
157 * this extra can also be specified to supply a Bundle of arguments to pass
158 * to that fragment when it is instantiated during the initial creation
159 * of the activity.
160 */
161 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
162
163 /**
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700164 * Fragment "key" argument passed thru {@link #EXTRA_SHOW_FRAGMENT_ARGUMENTS}
165 */
166 public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
167
168 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800169 * When starting this activity, the invoking Intent can contain this extra
170 * boolean that the header list should not be displayed. This is most often
171 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
172 * the activity to display a specific fragment that the user has navigated
173 * to.
174 */
175 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
176
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800177 public static final String BACK_STACK_PREFS = ":settings:prefs";
178
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800179 // extras that allow any preference activity to be launched as part of a wizard
180
181 // show Back and Next buttons? takes boolean parameter
182 // Back will then return RESULT_CANCELED and Next RESULT_OK
183 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
184
185 // add a Skip button?
186 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
187
188 // specify custom text for the Back or Next buttons, or cause a button to not appear
189 // at all by setting it to null
190 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
191 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
192
193 /**
194 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
195 * this extra can also be specify to supply the title to be shown for
196 * that fragment.
197 */
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700198 public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800199
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800200 private static final String META_DATA_KEY_FRAGMENT_CLASS =
201 "com.android.settings.FRAGMENT_CLASS";
202
203 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
204
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700205 private static final String EMPTY_QUERY = "";
206
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800207 private static boolean sShowNoHomeNotice = false;
208
209 private String mFragmentClass;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800210 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800211
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800212 private CharSequence mInitialTitle;
213
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800214 // Show only these settings for restricted users
215 private int[] SETTINGS_FOR_RESTRICTED = {
216 R.id.wireless_section,
217 R.id.wifi_settings,
218 R.id.bluetooth_settings,
219 R.id.data_usage_settings,
220 R.id.wireless_settings,
221 R.id.device_section,
222 R.id.sound_settings,
223 R.id.display_settings,
224 R.id.storage_settings,
225 R.id.application_settings,
226 R.id.battery_settings,
227 R.id.personal_section,
228 R.id.location_settings,
229 R.id.security_settings,
230 R.id.language_settings,
231 R.id.user_settings,
232 R.id.account_settings,
233 R.id.account_add,
234 R.id.system_section,
235 R.id.date_time_settings,
236 R.id.about_settings,
237 R.id.accessibility_settings,
238 R.id.print_settings,
239 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800240 R.id.home_settings,
241 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800242 };
243
244 private static final String[] ENTRY_FRAGMENTS = {
245 WirelessSettings.class.getName(),
246 WifiSettings.class.getName(),
247 AdvancedWifiSettings.class.getName(),
248 BluetoothSettings.class.getName(),
249 TetherSettings.class.getName(),
250 WifiP2pSettings.class.getName(),
251 VpnSettings.class.getName(),
252 DateTimeSettings.class.getName(),
253 LocalePicker.class.getName(),
254 InputMethodAndLanguageSettings.class.getName(),
255 SpellCheckersSettings.class.getName(),
256 UserDictionaryList.class.getName(),
257 UserDictionarySettings.class.getName(),
258 SoundSettings.class.getName(),
259 DisplaySettings.class.getName(),
260 DeviceInfoSettings.class.getName(),
261 ManageApplications.class.getName(),
262 ProcessStatsUi.class.getName(),
263 NotificationStation.class.getName(),
264 LocationSettings.class.getName(),
265 SecuritySettings.class.getName(),
266 PrivacySettings.class.getName(),
267 DeviceAdminSettings.class.getName(),
268 AccessibilitySettings.class.getName(),
269 CaptionPropertiesFragment.class.getName(),
270 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
271 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
272 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
273 TextToSpeechSettings.class.getName(),
274 Memory.class.getName(),
275 DevelopmentSettings.class.getName(),
276 UsbSettings.class.getName(),
277 AndroidBeam.class.getName(),
278 WifiDisplaySettings.class.getName(),
279 PowerUsageSummary.class.getName(),
280 AccountSyncSettings.class.getName(),
281 CryptKeeperSettings.class.getName(),
282 DataUsageSummary.class.getName(),
283 DreamSettings.class.getName(),
284 UserSettings.class.getName(),
285 NotificationAccessSettings.class.getName(),
286 ManageAccountsSettings.class.getName(),
287 PrintSettingsFragment.class.getName(),
288 PrintJobSettingsFragment.class.getName(),
289 TrustedCredentialsSettings.class.getName(),
290 PaymentSettings.class.getName(),
291 KeyboardLayoutPickerFragment.class.getName(),
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700292 ZenModeSettings.class.getName(),
293 NotificationSettings.class.getName(),
294 ChooseLockPassword.ChooseLockPasswordFragment.class.getName(),
295 ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
296 InstalledAppDetails.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800297 };
298
299 private SharedPreferences mDevelopmentPreferences;
300 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
301
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800302 private AuthenticatorHelper mAuthenticatorHelper;
303 private boolean mListeningToAccountUpdates;
304
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800305 private boolean mBatteryPresent = true;
306 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
307
308 @Override
309 public void onReceive(Context context, Intent intent) {
310 String action = intent.getAction();
311 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
312 boolean batteryPresent = Utils.isBatteryPresent(intent);
313
314 if (mBatteryPresent != batteryPresent) {
315 mBatteryPresent = batteryPresent;
316 invalidateHeaders();
317 }
318 }
319 }
320 };
321
Svetoslav990159a2014-04-14 17:14:59 -0700322 private final DynamicIndexableContentMonitor mDynamicIndexableContentMonitor =
323 new DynamicIndexableContentMonitor();
Svetoslav853e4712014-04-14 10:10:25 -0700324
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700325 private Button mNextButton;
326 private ActionBar mActionBar;
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700327 private boolean mDisplayHomeAsUpEnabled;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700328
329 private SearchView mSearchView;
330 private MenuItem mSearchMenuItem;
331 private boolean mSearchMenuItemExpanded = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700332 private SearchResultsSummary mSearchResultsFragment;
333 private String mSearchQuery;
334
335 // Headers
336 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800337 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800338 private HeaderAdapter mHeaderAdapter;
339
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800340 private static final int MSG_BUILD_HEADERS = 1;
341 private Handler mHandler = new Handler() {
342 @Override
343 public void handleMessage(Message msg) {
344 switch (msg.what) {
345 case MSG_BUILD_HEADERS: {
346 mHeaders.clear();
347 onBuildHeaders(mHeaders);
348 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800349 } break;
350 }
351 }
352 };
353
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700354 private boolean mNeedToRevertToInitialFragment = false;
355
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800356 @Override
357 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
358 // Override the fragment title for Wallpaper settings
359 int titleRes = pref.getTitleRes();
360 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
361 titleRes = R.string.wallpaper_settings_fragment_title;
362 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
363 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
364 if (UserManager.get(this).isLinkedUser()) {
365 titleRes = R.string.profile_info_settings_title;
366 } else {
367 titleRes = R.string.user_info_settings_title;
368 }
369 }
370 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
371 null, 0);
372 return true;
373 }
374
375 @Override
376 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
377 return false;
378 }
379
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800380 private void invalidateHeaders() {
381 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
382 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
383 }
384 }
385
386 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800387 public void onConfigurationChanged(Configuration newConfig) {
388 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800389 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800390 }
391
392 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700393 protected void onStart() {
394 super.onStart();
395
396 if (mNeedToRevertToInitialFragment) {
397 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800398 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800399 }
400
401 @Override
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700402 public boolean onCreateOptionsMenu(Menu menu) {
403 MenuInflater inflater = getMenuInflater();
404 inflater.inflate(R.menu.options_menu, menu);
405
406 // Cache the search query (can be overriden by the OnQueryTextListener)
407 final String query = mSearchQuery;
408
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700409 mSearchMenuItem = menu.findItem(R.id.search);
410 mSearchView = (SearchView) mSearchMenuItem.getActionView();
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700411
Fabrice Di Meglio95937822014-03-31 19:46:42 -0700412 mSearchMenuItem.setOnActionExpandListener(this);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700413 mSearchView.setOnQueryTextListener(this);
414 mSearchView.setOnCloseListener(this);
415
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700416 if (mSearchMenuItemExpanded) {
417 mSearchMenuItem.expandActionView();
418 }
419 mSearchView.setQuery(query, true /* submit */);
420
421 return true;
422 }
423
424 @Override
425 protected void onCreate(Bundle savedState) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800426 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
427 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
428 }
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800429 Index.getInstance(this).update();
430
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800431 mAuthenticatorHelper = new AuthenticatorHelper();
432 mAuthenticatorHelper.updateAuthDescriptions(this);
433 mAuthenticatorHelper.onAccountsUpdated(this, null);
434
435 DevicePolicyManager dpm =
436 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800437
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700438 mHeaderAdapter = new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800439
440 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
441 Context.MODE_PRIVATE);
442
443 getMetaData();
444
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700445 super.onCreate(savedState);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800446
447 setContentView(R.layout.settings_main);
448
449 getFragmentManager().addOnBackStackChangedListener(this);
450
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700451 mDisplayHomeAsUpEnabled = true;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800452
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700453 String initialFragmentName = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
454 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800455
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700456 if (savedState != null) {
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700457 // We are restarting from a previous saved state; used that to initialize, instead
458 // of starting fresh.
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700459 mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
460 mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800461
Fabrice Di Meglio1800a9f2014-04-03 19:31:07 -0700462 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
463 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
464 setTitle(mInitialTitle);
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800465
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700466 ArrayList<Header> headers = savedState.getParcelableArrayList(SAVE_KEY_HEADERS);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800467 if (headers != null) {
468 mHeaders.addAll(headers);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700469 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800470 }
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700471
472 mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800473 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800474 // We need to build the Headers in all cases
475 onBuildHeaders(mHeaders);
476
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700477 if (initialFragmentName != null) {
478 final ComponentName cn = getIntent().getComponent();
479 // No UP is we are launched thru a Settings shortcut
480 if (!cn.getClassName().equals(SubSettings.class.getName())) {
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700481 mDisplayHomeAsUpEnabled = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700482 }
483 final String initialTitle = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
484 mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800485 setTitle(mInitialTitle);
Fabrice Di Meglioc1457322014-04-04 19:07:50 -0700486
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700487 switchToFragment( initialFragmentName, initialArguments, true, false,
488 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000489 } else {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700490 // No UP if we are displaying the Headers
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700491 mDisplayHomeAsUpEnabled = false;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000492 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700493 mInitialTitle = getText(R.string.dashboard_title);
494 switchToFragment(DashboardSummary.class.getName(), null, false, false,
495 mInitialTitle, false);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000496 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800497 }
498 }
499
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700500 mActionBar = getActionBar();
501 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700502 mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700503
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800504 // see if we should show Back/Next buttons
505 Intent intent = getIntent();
506 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
507
508 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
509 if (buttonBar != null) {
510 buttonBar.setVisibility(View.VISIBLE);
511
512 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
513 backButton.setOnClickListener(new OnClickListener() {
514 public void onClick(View v) {
515 setResult(RESULT_CANCELED);
516 finish();
517 }
518 });
519 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
520 skipButton.setOnClickListener(new OnClickListener() {
521 public void onClick(View v) {
522 setResult(RESULT_OK);
523 finish();
524 }
525 });
526 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
527 mNextButton.setOnClickListener(new OnClickListener() {
528 public void onClick(View v) {
529 setResult(RESULT_OK);
530 finish();
531 }
532 });
533
534 // set our various button parameters
535 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
536 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
537 if (TextUtils.isEmpty(buttonText)) {
538 mNextButton.setVisibility(View.GONE);
539 }
540 else {
541 mNextButton.setText(buttonText);
542 }
543 }
544 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
545 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
546 if (TextUtils.isEmpty(buttonText)) {
547 backButton.setVisibility(View.GONE);
548 }
549 else {
550 backButton.setText(buttonText);
551 }
552 }
553 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
554 skipButton.setVisibility(View.VISIBLE);
555 }
556 }
557 }
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800558 }
559
560 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800561 public void onBackStackChanged() {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700562 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800563 }
564
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700565 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800566 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700567
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800568 if (count == 0) {
569 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700570 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800571 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700572
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800573 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
574 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700575
576 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800577 }
578
579 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
580 final CharSequence title;
581 final int titleRes = bse.getBreadCrumbTitleRes();
582 if (titleRes > 0) {
583 title = getText(titleRes);
584 } else {
585 title = bse.getBreadCrumbTitle();
586 }
587 if (title != null) {
588 setTitle(title);
589 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800590 }
591
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800592 @Override
593 protected void onSaveInstanceState(Bundle outState) {
594 super.onSaveInstanceState(outState);
595
596 if (mHeaders.size() > 0) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700597 outState.putParcelableArrayList(SAVE_KEY_HEADERS, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800598 }
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700599
Fabrice Di Megliob731dd02014-04-03 18:40:38 -0700600 outState.putBoolean(SAVE_KEY_SHOW_HOME_AS_UP, mDisplayHomeAsUpEnabled);
601
Fabrice Di Megliod6985df2014-04-03 16:43:26 -0700602 // The option menus are created if the ActionBar is visible and they are also created
603 // asynchronously. If you launch Settings with an Intent action like
604 // android.intent.action.POWER_USAGE_SUMMARY and at the same time your device is locked
605 // thru a LockScreen, onCreateOptionsMenu() is not yet called and references to the search
606 // menu item and search view are null.
607 boolean isExpanded = (mSearchMenuItem != null) && mSearchMenuItem.isActionViewExpanded();
608 outState.putBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED, isExpanded);
609
610 String query = (mSearchView != null) ? mSearchView.getQuery().toString() : EMPTY_QUERY;
611 outState.putString(SAVE_KEY_SEARCH_QUERY, query);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800612 }
613
614 @Override
615 public void onResume() {
616 super.onResume();
617
618 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
619 @Override
620 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
621 invalidateHeaders();
622 }
623 };
624 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
625 mDevelopmentPreferencesListener);
626
Matthew Xiea504c4d2014-02-14 16:32:32 -0800627 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800628 invalidateHeaders();
629
630 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Svetoslav853e4712014-04-14 10:10:25 -0700631
Svetoslav990159a2014-04-14 17:14:59 -0700632 mDynamicIndexableContentMonitor.register(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800633 }
634
635 @Override
636 public void onPause() {
637 super.onPause();
638
639 unregisterReceiver(mBatteryInfoReceiver);
640
641 mHeaderAdapter.pause();
642
643 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
644 mDevelopmentPreferencesListener);
645
646 mDevelopmentPreferencesListener = null;
Svetoslav853e4712014-04-14 10:10:25 -0700647
Svetoslav990159a2014-04-14 17:14:59 -0700648 mDynamicIndexableContentMonitor.unregister();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800649 }
650
651 @Override
652 public void onDestroy() {
653 super.onDestroy();
654 if (mListeningToAccountUpdates) {
655 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
656 }
657 }
658
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800659 protected boolean isValidFragment(String fragmentName) {
660 // Almost all fragments are wrapped in this,
661 // except for a few that have their own activities.
662 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
663 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
664 }
665 return false;
666 }
667
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800668 /**
669 * When in two-pane mode, switch to the fragment pane to show the given
670 * preference fragment.
671 *
672 * @param header The new header to display.
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700673 * @param position The position of the Header in the list.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800674 */
Fabrice Di Megliobbdada82014-04-04 10:16:59 -0700675 private void onHeaderClick(Header header, int position) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800676 if (header == null) {
677 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800678 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700679 if (header.fragment != null) {
680 startWithFragment(header.fragment, header.fragmentArguments, null, 0,
681 header.getTitle(getResources()));
682 } else if (header.intent != null) {
683 startActivity(header.intent);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800684 }
685 }
686
687 /**
688 * Called to determine whether the header list should be hidden.
689 * The default implementation returns the
690 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
691 * This is set to false, for example, when the activity is being re-launched
692 * to show a particular preference activity.
693 */
694 public boolean onIsHidingHeaders() {
695 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
696 }
697
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800698 @Override
699 public Intent getIntent() {
700 Intent superIntent = super.getIntent();
701 String startingFragment = getStartingFragmentClass(superIntent);
702 // This is called from super.onCreate, isMultiPane() is not yet reliable
703 // Do not use onIsHidingHeaders either, which relies itself on this method
704 if (startingFragment != null) {
705 Intent modIntent = new Intent(superIntent);
706 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
707 Bundle args = superIntent.getExtras();
708 if (args != null) {
709 args = new Bundle(args);
710 } else {
711 args = new Bundle();
712 }
713 args.putParcelable("intent", superIntent);
714 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
715 return modIntent;
716 }
717 return superIntent;
718 }
719
720 /**
721 * Checks if the component name in the intent is different from the Settings class and
722 * returns the class name to load as a fragment.
723 */
724 private String getStartingFragmentClass(Intent intent) {
725 if (mFragmentClass != null) return mFragmentClass;
726
727 String intentClass = intent.getComponent().getClassName();
728 if (intentClass.equals(getClass().getName())) return null;
729
730 if ("com.android.settings.ManageApplications".equals(intentClass)
731 || "com.android.settings.RunningServices".equals(intentClass)
732 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
733 // Old names of manage apps.
734 intentClass = com.android.settings.applications.ManageApplications.class.getName();
735 }
736
737 return intentClass;
738 }
739
740 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000741 * Start a new fragment containing a preference panel. If the preferences
742 * are being displayed in multi-pane mode, the given fragment class will
743 * be instantiated and placed in the appropriate pane. If running in
744 * single-pane mode, a new activity will be launched in which to show the
745 * fragment.
746 *
747 * @param fragmentClass Full name of the class implementing the fragment.
748 * @param args Any desired arguments to supply to the fragment.
749 * @param titleRes Optional resource identifier of the title of this
750 * fragment.
751 * @param titleText Optional text of the title of this fragment.
752 * @param resultTo Optional fragment that result data should be sent to.
753 * If non-null, resultTo.onActivityResult() will be called when this
754 * preference panel is done. The launched panel must use
755 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
756 * @param resultRequestCode If resultTo is non-null, this is the caller's
757 * request code to be received with the resut.
758 */
759 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700760 CharSequence titleText, Fragment resultTo, int resultRequestCode) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700761 String title;
762 if (titleRes > 0) {
763 title = getString(titleRes);
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700764 } else if (titleText != null) {
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700765 title = titleText.toString();
Fabrice Di Meglio821a0722014-04-08 13:56:06 -0700766 } else {
767 // There not much we can do in that case
768 title = "";
Fabrice Di Meglio911fb2a2014-04-04 17:55:57 -0700769 }
770 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000771 }
772
773 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800774 * Called by a preference panel fragment to finish itself.
775 *
776 * @param caller The fragment that is asking to be finished.
777 * @param resultCode Optional result code to send back to the original
778 * launching fragment.
779 * @param resultData Optional result data to send back to the original
780 * launching fragment.
781 */
782 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
783 setResult(resultCode, resultData);
784 }
785
786 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000787 * Start a new fragment.
788 *
789 * @param fragment The fragment to start
790 * @param push If true, the current fragment will be pushed onto the back stack. If false,
791 * the current fragment will be replaced.
792 */
793 public void startPreferenceFragment(Fragment fragment, boolean push) {
794 FragmentTransaction transaction = getFragmentManager().beginTransaction();
795 transaction.replace(R.id.prefs, fragment);
796 if (push) {
797 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
798 transaction.addToBackStack(BACK_STACK_PREFS);
799 } else {
800 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
801 }
802 transaction.commitAllowingStateLoss();
803 }
804
805 /**
Fabrice Di Megliod25314d2014-03-21 19:24:43 -0700806 * Switch to a specific Fragment with taking care of validation, Title and BackStack
807 */
808 private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
809 boolean addToBackStack, CharSequence title, boolean withTransition) {
810 if (validate && !isValidFragment(fragmentName)) {
811 throw new IllegalArgumentException("Invalid fragment for this activity: "
812 + fragmentName);
813 }
814 Fragment f = Fragment.instantiate(this, fragmentName, args);
815 FragmentTransaction transaction = getFragmentManager().beginTransaction();
816 transaction.replace(R.id.prefs, f);
817 if (withTransition) {
818 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
819 }
820 if (addToBackStack) {
821 transaction.addToBackStack(SettingsActivity.BACK_STACK_PREFS);
822 }
823 if (title != null) {
824 transaction.setBreadCrumbTitle(title);
825 }
826 transaction.commitAllowingStateLoss();
827 return f;
828 }
829
830 /**
831 * Start a new instance of this activity, showing only the given fragment.
832 * When launched in this mode, the given preference fragment will be instantiated and fill the
833 * entire activity.
834 *
835 * @param fragmentName The name of the fragment to display.
836 * @param args Optional arguments to supply to the fragment.
837 * @param resultTo Option fragment that should receive the result of
838 * the activity launch.
839 * @param resultRequestCode If resultTo is non-null, this is the request
840 * code in which to report the result.
841 * @param title String to display for the title of this set of preferences.
842 */
843 public void startWithFragment(String fragmentName, Bundle args,
844 Fragment resultTo, int resultRequestCode, CharSequence title) {
845 Intent intent = onBuildStartFragmentIntent(fragmentName, args, title);
846 if (resultTo == null) {
847 startActivity(intent);
848 } else {
849 resultTo.startActivityForResult(intent, resultRequestCode);
850 }
851 }
852
853 /**
854 * Build an Intent to launch a new activity showing the selected fragment.
855 * The implementation constructs an Intent that re-launches the current activity with the
856 * appropriate arguments to display the fragment.
857 *
858 * @param fragmentName The name of the fragment to display.
859 * @param args Optional arguments to supply to the fragment.
860 * @param title Optional title to show for this item.
861 * @return Returns an Intent that can be launched to display the given
862 * fragment.
863 */
864 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args, CharSequence title) {
865 Intent intent = new Intent(Intent.ACTION_MAIN);
866 intent.setClass(this, SubSettings.class);
867 intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentName);
868 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
869 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, title);
870 intent.putExtra(EXTRA_NO_HEADERS, true);
871 return intent;
872 }
873
874 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800875 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800876 *
877 * @param headers The list in which to place the headers.
878 */
879 private void onBuildHeaders(List<Header> headers) {
880 loadHeadersFromResource(R.xml.settings_headers, headers);
881 updateHeaderList(headers);
882 }
883
884 /**
885 * Parse the given XML file as a header description, adding each
886 * parsed Header into the target list.
887 *
888 * @param resid The XML resource to load and parse.
889 * @param target The list in which the parsed headers should be placed.
890 */
891 private void loadHeadersFromResource(int resid, List<Header> target) {
892 XmlResourceParser parser = null;
893 try {
894 parser = getResources().getXml(resid);
895 AttributeSet attrs = Xml.asAttributeSet(parser);
896
897 int type;
898 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
899 && type != XmlPullParser.START_TAG) {
900 // Parse next until start tag is found
901 }
902
903 String nodeName = parser.getName();
904 if (!"preference-headers".equals(nodeName)) {
905 throw new RuntimeException(
906 "XML document must start with <preference-headers> tag; found"
907 + nodeName + " at " + parser.getPositionDescription());
908 }
909
910 Bundle curBundle = null;
911
912 final int outerDepth = parser.getDepth();
913 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
914 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
915 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
916 continue;
917 }
918
919 nodeName = parser.getName();
920 if ("header".equals(nodeName)) {
921 Header header = new Header();
922
923 TypedArray sa = obtainStyledAttributes(
924 attrs, com.android.internal.R.styleable.PreferenceHeader);
925 header.id = sa.getResourceId(
926 com.android.internal.R.styleable.PreferenceHeader_id,
927 (int)HEADER_ID_UNDEFINED);
928 TypedValue tv = sa.peekValue(
929 com.android.internal.R.styleable.PreferenceHeader_title);
930 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
931 if (tv.resourceId != 0) {
932 header.titleRes = tv.resourceId;
933 } else {
934 header.title = tv.string;
935 }
936 }
937 tv = sa.peekValue(
938 com.android.internal.R.styleable.PreferenceHeader_summary);
939 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
940 if (tv.resourceId != 0) {
941 header.summaryRes = tv.resourceId;
942 } else {
943 header.summary = tv.string;
944 }
945 }
946 header.iconRes = sa.getResourceId(
947 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
948 header.fragment = sa.getString(
949 com.android.internal.R.styleable.PreferenceHeader_fragment);
950 sa.recycle();
951
952 if (curBundle == null) {
953 curBundle = new Bundle();
954 }
955
956 final int innerDepth = parser.getDepth();
957 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
958 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
959 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
960 continue;
961 }
962
963 String innerNodeName = parser.getName();
964 if (innerNodeName.equals("extra")) {
965 getResources().parseBundleExtra("extra", attrs, curBundle);
966 XmlUtils.skipCurrentTag(parser);
967
968 } else if (innerNodeName.equals("intent")) {
969 header.intent = Intent.parseIntent(getResources(), parser, attrs);
970
971 } else {
972 XmlUtils.skipCurrentTag(parser);
973 }
974 }
975
976 if (curBundle.size() > 0) {
977 header.fragmentArguments = curBundle;
978 curBundle = null;
979 }
980
981 target.add(header);
982 } else {
983 XmlUtils.skipCurrentTag(parser);
984 }
985 }
986
987 } catch (XmlPullParserException e) {
988 throw new RuntimeException("Error parsing headers", e);
989 } catch (IOException e) {
990 throw new RuntimeException("Error parsing headers", e);
991 } finally {
992 if (parser != null) parser.close();
993 }
994 }
995
996 private void updateHeaderList(List<Header> target) {
997 final boolean showDev = mDevelopmentPreferences.getBoolean(
998 DevelopmentSettings.PREF_SHOW,
999 android.os.Build.TYPE.equals("eng"));
1000 int i = 0;
1001
1002 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1003 mHeaderIndexMap.clear();
1004 while (i < target.size()) {
1005 Header header = target.get(i);
1006 // Ids are integers, so downcasting
1007 int id = (int) header.id;
1008 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1009 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1010 } else if (id == R.id.wifi_settings) {
1011 // Remove WiFi Settings if WiFi service is not available.
1012 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1013 target.remove(i);
1014 }
1015 } else if (id == R.id.bluetooth_settings) {
1016 // Remove Bluetooth Settings if Bluetooth service is not available.
1017 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1018 target.remove(i);
1019 }
1020 } else if (id == R.id.data_usage_settings) {
1021 // Remove data usage when kernel module not enabled
1022 final INetworkManagementService netManager = INetworkManagementService.Stub
1023 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1024 try {
1025 if (!netManager.isBandwidthControlEnabled()) {
1026 target.remove(i);
1027 }
1028 } catch (RemoteException e) {
1029 // ignored
1030 }
1031 } else if (id == R.id.battery_settings) {
1032 // Remove battery settings when battery is not available. (e.g. TV)
1033
1034 if (!mBatteryPresent) {
1035 target.remove(i);
1036 }
1037 } else if (id == R.id.account_settings) {
1038 int headerIndex = i + 1;
1039 i = insertAccountsHeaders(target, headerIndex);
1040 } else if (id == R.id.home_settings) {
1041 if (!updateHomeSettingHeaders(header)) {
1042 target.remove(i);
1043 }
1044 } else if (id == R.id.user_settings) {
1045 if (!UserHandle.MU_ENABLED
1046 || !UserManager.supportsMultipleUsers()
1047 || Utils.isMonkeyRunning()) {
1048 target.remove(i);
1049 }
1050 } else if (id == R.id.nfc_payment_settings) {
1051 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1052 target.remove(i);
1053 } else {
1054 // Only show if NFC is on and we have the HCE feature
1055 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1056 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1057 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1058 target.remove(i);
1059 }
1060 }
1061 } else if (id == R.id.development_settings) {
1062 if (!showDev) {
1063 target.remove(i);
1064 }
1065 } else if (id == R.id.account_add) {
1066 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1067 target.remove(i);
1068 }
1069 }
1070
1071 if (i < target.size() && target.get(i) == header
1072 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1073 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1074 target.remove(i);
1075 }
1076
1077 // Increment if the current one wasn't removed by the Utils code.
1078 if (i < target.size() && target.get(i) == header) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001079 mHeaderIndexMap.put(id, i);
1080 i++;
1081 }
1082 }
1083 }
1084
1085 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1086 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1087 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1088 for (String accountType : accountTypes) {
1089 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1090 if (label == null) {
1091 continue;
1092 }
1093
1094 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1095 boolean skipToAccount = accounts.length == 1
1096 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1097 Header accHeader = new Header();
1098 accHeader.title = label;
1099 if (accHeader.extras == null) {
1100 accHeader.extras = new Bundle();
1101 }
1102 if (skipToAccount) {
1103 accHeader.fragment = AccountSyncSettings.class.getName();
1104 accHeader.fragmentArguments = new Bundle();
1105 // Need this for the icon
1106 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1107 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1108 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1109 accounts[0]);
1110 } else {
1111 accHeader.fragment = ManageAccountsSettings.class.getName();
1112 accHeader.fragmentArguments = new Bundle();
1113 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1114 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1115 accountType);
1116 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1117 label.toString());
1118 }
1119 accountHeaders.add(accHeader);
1120 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1121 }
1122
1123 // Sort by label
1124 Collections.sort(accountHeaders, new Comparator<Header>() {
1125 @Override
1126 public int compare(Header h1, Header h2) {
1127 return h1.title.toString().compareTo(h2.title.toString());
1128 }
1129 });
1130
1131 for (Header header : accountHeaders) {
1132 target.add(headerIndex++, header);
1133 }
1134 if (!mListeningToAccountUpdates) {
1135 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1136 mListeningToAccountUpdates = true;
1137 }
1138 return headerIndex;
1139 }
1140
1141 private boolean updateHomeSettingHeaders(Header header) {
1142 // Once we decide to show Home settings, keep showing it forever
1143 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1144 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1145 return true;
1146 }
1147
1148 try {
1149 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1150 getPackageManager().getHomeActivities(homeApps);
1151 if (homeApps.size() < 2) {
1152 // When there's only one available home app, omit this settings
1153 // category entirely at the top level UI. If the user just
1154 // uninstalled the penultimate home app candidiate, we also
1155 // now tell them about why they aren't seeing 'Home' in the list.
1156 if (sShowNoHomeNotice) {
1157 sShowNoHomeNotice = false;
1158 NoHomeDialogFragment.show(this);
1159 }
1160 return false;
1161 } else {
1162 // Okay, we're allowing the Home settings category. Tell it, when
1163 // invoked via this front door, that we'll need to be told about the
1164 // case when the user uninstalls all but one home app.
1165 if (header.fragmentArguments == null) {
1166 header.fragmentArguments = new Bundle();
1167 }
1168 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1169 }
1170 } catch (Exception e) {
1171 // Can't look up the home activity; bail on configuring the icon
1172 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1173 }
1174
1175 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1176 return true;
1177 }
1178
1179 private void getMetaData() {
1180 try {
1181 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1182 PackageManager.GET_META_DATA);
1183 if (ai == null || ai.metaData == null) return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001184 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1185 } catch (NameNotFoundException nnfe) {
1186 // No recovery
1187 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1188 }
1189 }
1190
1191 // give subclasses access to the Next button
1192 public boolean hasNextButton() {
1193 return mNextButton != null;
1194 }
1195
1196 public Button getNextButton() {
1197 return mNextButton;
1198 }
1199
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001200 public HeaderAdapter getHeaderAdapter() {
1201 return mHeaderAdapter;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001202 }
1203
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001204 public void onListItemClick(ListView l, View v, int position, long id) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001205 if (!isResumed()) {
1206 return;
1207 }
1208 Object item = mHeaderAdapter.getItem(position);
1209 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001210 mSelectedHeader = (Header) item;
Fabrice Di Megliobbdada82014-04-04 10:16:59 -07001211 onHeaderClick(mSelectedHeader, position);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001212 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001213 }
1214 }
1215
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001216 @Override
1217 public boolean shouldUpRecreateTask(Intent targetIntent) {
1218 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1219 }
1220
1221 @Override
1222 public void onAccountsUpdated(Account[] accounts) {
1223 // TODO: watch for package upgrades to invalidate cache; see 7206643
1224 mAuthenticatorHelper.updateAuthDescriptions(this);
1225 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1226 invalidateHeaders();
1227 }
1228
1229 public static void requestHomeNotice() {
1230 sShowNoHomeNotice = true;
1231 }
1232
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001233 @Override
1234 public boolean onQueryTextSubmit(String query) {
1235 switchToSearchResultsFragmentIfNeeded();
1236 mSearchQuery = query;
1237 return mSearchResultsFragment.onQueryTextSubmit(query);
1238 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001239
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001240 @Override
1241 public boolean onQueryTextChange(String newText) {
1242 mSearchQuery = newText;
1243 return false;
1244 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001245
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001246 @Override
1247 public boolean onClose() {
1248 return false;
1249 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001250
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001251 @Override
1252 public boolean onMenuItemActionExpand(MenuItem item) {
1253 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001254 switchToSearchResultsFragmentIfNeeded();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001255 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001256 return true;
1257 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001258
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001259 @Override
1260 public boolean onMenuItemActionCollapse(MenuItem item) {
1261 if (item.getItemId() == mSearchMenuItem.getItemId()) {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001262 if (mSearchMenuItemExpanded) {
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001263 revertToInitialFragment();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001264 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001265 }
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001266 return true;
1267 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001268
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001269 private void switchToSearchResultsFragmentIfNeeded() {
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001270 if (mSearchResultsFragment != null) {
1271 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001272 }
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001273 Fragment current = getFragmentManager().findFragmentById(R.id.prefs);
1274 if (current != null && current instanceof SearchResultsSummary) {
1275 mSearchResultsFragment = (SearchResultsSummary) current;
1276 } else {
1277 String title = getString(R.string.search_results_title);
1278 mSearchResultsFragment = (SearchResultsSummary) switchToFragment(
1279 SearchResultsSummary.class.getName(), null, false, true, title, true);
1280 }
1281 mSearchMenuItemExpanded = true;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001282 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001283
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001284 public void needToRevertToInitialFragment() {
1285 mNeedToRevertToInitialFragment = true;
1286 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001287
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001288 private void revertToInitialFragment() {
1289 mNeedToRevertToInitialFragment = false;
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001290 mSearchResultsFragment = null;
Fabrice Di Megliobb16fd82014-04-04 14:48:05 -07001291 mSearchMenuItemExpanded = false;
1292 getFragmentManager().popBackStackImmediate(SettingsActivity.BACK_STACK_PREFS,
1293 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Megliod25314d2014-03-21 19:24:43 -07001294 mSearchMenuItem.collapseActionView();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001295 }
Svetoslav853e4712014-04-14 10:10:25 -07001296
Svetoslav990159a2014-04-14 17:14:59 -07001297 private static final class DynamicIndexableContentMonitor extends PackageMonitor implements
1298 InputManager.InputDeviceListener {
1299
Svetoslav853e4712014-04-14 10:10:25 -07001300 private static final Intent ACCESSIBILITY_SERVICE_INTENT =
1301 new Intent(AccessibilityService.SERVICE_INTERFACE);
1302
1303 private static final Intent PRINT_SERVICE_INTENT =
1304 new Intent(PrintService.SERVICE_INTERFACE);
1305
Svetoslav990159a2014-04-14 17:14:59 -07001306 private static final Intent IME_SERVICE_INTENT =
1307 new Intent("android.view.InputMethod");
1308
Svetoslav853e4712014-04-14 10:10:25 -07001309 private static final long DELAY_PROCESS_PACKAGE_CHANGE = 2000;
1310
1311 private static final int MSG_PACKAGE_AVAILABLE = 1;
1312 private static final int MSG_PACKAGE_UNAVAILABLE = 2;
1313
1314 private final List<String> mAccessibilityServices = new ArrayList<String>();
1315 private final List<String> mPrintServices = new ArrayList<String>();
Svetoslav990159a2014-04-14 17:14:59 -07001316 private final List<String> mImeServices = new ArrayList<String>();
Svetoslav853e4712014-04-14 10:10:25 -07001317
1318 private final Handler mHandler = new Handler() {
1319 @Override
1320 public void handleMessage(Message msg) {
1321 switch (msg.what) {
1322 case MSG_PACKAGE_AVAILABLE: {
1323 String packageName = (String) msg.obj;
1324 handlePackageAvailable(packageName);
1325 } break;
1326
1327 case MSG_PACKAGE_UNAVAILABLE: {
1328 String packageName = (String) msg.obj;
1329 handlePackageUnavailable(packageName);
1330 } break;
1331 }
1332 }
1333 };
1334
Svetoslav990159a2014-04-14 17:14:59 -07001335 private final ContentObserver mContentObserver = new MyContentObserver(mHandler);
1336
Svetoslav853e4712014-04-14 10:10:25 -07001337 private Context mContext;
1338
1339 public void register(Context context) {
1340 mContext = context;
1341
1342 // Cache accessibility service packages to know when they go away.
1343 AccessibilityManager accessibilityManager = (AccessibilityManager)
1344 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
1345 List<AccessibilityServiceInfo> accessibilityServices = accessibilityManager
1346 .getInstalledAccessibilityServiceList();
1347 final int accessibilityServiceCount = accessibilityServices.size();
1348 for (int i = 0; i < accessibilityServiceCount; i++) {
1349 AccessibilityServiceInfo accessibilityService = accessibilityServices.get(i);
1350 mAccessibilityServices.add(accessibilityService.getResolveInfo()
1351 .serviceInfo.packageName);
1352 }
1353
1354 // Cache print service packages to know when they go away.
1355 PrintManager printManager = (PrintManager)
1356 mContext.getSystemService(Context.PRINT_SERVICE);
1357 List<PrintServiceInfo> printServices = printManager.getInstalledPrintServices();
1358 final int serviceCount = printServices.size();
1359 for (int i = 0; i < serviceCount; i++) {
1360 PrintServiceInfo printService = printServices.get(i);
1361 mPrintServices.add(printService.getResolveInfo()
1362 .serviceInfo.packageName);
1363 }
1364
Svetoslav990159a2014-04-14 17:14:59 -07001365 // Cache IME service packages to know when they go away.
1366 InputMethodManager imeManager = (InputMethodManager)
1367 mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
1368 List<InputMethodInfo> inputMethods = imeManager.getInputMethodList();
1369 final int inputMethodCount = inputMethods.size();
1370 for (int i = 0; i < inputMethodCount; i++) {
1371 InputMethodInfo inputMethod = inputMethods.get(i);
1372 mImeServices.add(inputMethod.getServiceInfo().packageName);
1373 }
1374
1375 // Watch for related content URIs.
1376 mContext.getContentResolver().registerContentObserver(
1377 UserDictionary.Words.CONTENT_URI, true, mContentObserver);
1378
1379 // Watch for input device changes.
1380 InputManager inputManager = (InputManager) context.getSystemService(
1381 Context.INPUT_SERVICE);
1382 inputManager.registerInputDeviceListener(this, mHandler);
1383
1384 // Start tracking packages.
Svetoslav853e4712014-04-14 10:10:25 -07001385 register(context, Looper.getMainLooper(), UserHandle.CURRENT, false);
1386 }
1387
1388 public void unregister() {
1389 super.unregister();
Svetoslav990159a2014-04-14 17:14:59 -07001390
1391 InputManager inputManager = (InputManager) mContext.getSystemService(
1392 Context.INPUT_SERVICE);
1393 inputManager.unregisterInputDeviceListener(this);
1394
1395 mContext.getContentResolver().unregisterContentObserver(mContentObserver);
1396
Svetoslav853e4712014-04-14 10:10:25 -07001397 mAccessibilityServices.clear();
1398 mPrintServices.clear();
Svetoslav990159a2014-04-14 17:14:59 -07001399 mImeServices.clear();
Svetoslav853e4712014-04-14 10:10:25 -07001400 }
1401
1402 // Covers installed, appeared external storage with the package, upgraded.
1403 @Override
1404 public void onPackageAppeared(String packageName, int uid) {
1405 postMessage(MSG_PACKAGE_AVAILABLE, packageName);
1406 }
1407
1408 // Covers uninstalled, removed external storage with the package.
1409 @Override
1410 public void onPackageDisappeared(String packageName, int uid) {
1411 postMessage(MSG_PACKAGE_UNAVAILABLE, packageName);
1412 }
1413
1414 // Covers enabled, disabled.
1415 @Override
1416 public void onPackageModified(String packageName) {
1417 super.onPackageModified(packageName);
1418 final int state = mContext.getPackageManager().getApplicationEnabledSetting(
1419 packageName);
1420 if (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
1421 || state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
1422 postMessage(MSG_PACKAGE_AVAILABLE, packageName);
1423 } else {
1424 postMessage(MSG_PACKAGE_UNAVAILABLE, packageName);
1425 }
1426 }
1427
Svetoslav990159a2014-04-14 17:14:59 -07001428 @Override
1429 public void onInputDeviceAdded(int deviceId) {
1430 Index.getInstance(mContext).updateFromClassNameResource(
1431 InputMethodAndLanguageSettings.class.getName(), false, true);
1432 }
1433
1434 @Override
1435 public void onInputDeviceRemoved(int deviceId) {
1436 onInputDeviceChanged(deviceId);
1437 }
1438
1439 @Override
1440 public void onInputDeviceChanged(int deviceId) {
1441 Index.getInstance(mContext).updateFromClassNameResource(
1442 InputMethodAndLanguageSettings.class.getName(), true, true);
1443 }
1444
Svetoslav853e4712014-04-14 10:10:25 -07001445 private void postMessage(int what, String packageName) {
1446 Message message = mHandler.obtainMessage(what, packageName);
1447 mHandler.sendMessageDelayed(message, DELAY_PROCESS_PACKAGE_CHANGE);
1448 }
1449
1450 private void handlePackageAvailable(String packageName) {
1451 if (!mAccessibilityServices.contains(packageName)) {
1452 Intent intent = ACCESSIBILITY_SERVICE_INTENT;
1453 intent.setPackage(packageName);
1454 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) {
1455 mAccessibilityServices.add(packageName);
1456 Index.getInstance(mContext).updateFromClassNameResource(
1457 AccessibilitySettings.class.getName(), false, true);
1458 }
1459 intent.setPackage(null);
1460 }
1461
1462 if (!mPrintServices.contains(packageName)) {
1463 Intent intent = PRINT_SERVICE_INTENT;
1464 intent.setPackage(packageName);
1465 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) {
1466 mPrintServices.add(packageName);
1467 Index.getInstance(mContext).updateFromClassNameResource(
1468 PrintSettingsFragment.class.getName(), false, true);
1469 }
1470 intent.setPackage(null);
1471 }
Svetoslav990159a2014-04-14 17:14:59 -07001472
1473 if (!mImeServices.contains(packageName)) {
1474 Intent intent = IME_SERVICE_INTENT;
1475 intent.setPackage(packageName);
1476 if (!mContext.getPackageManager().queryIntentServices(intent, 0).isEmpty()) {
1477 mImeServices.add(packageName);
1478 Index.getInstance(mContext).updateFromClassNameResource(
1479 InputMethodAndLanguageSettings.class.getName(), false, true);
1480 }
1481 intent.setPackage(null);
1482 }
Svetoslav853e4712014-04-14 10:10:25 -07001483 }
1484
1485 private void handlePackageUnavailable(String packageName) {
1486 final int accessibilityIndex = mAccessibilityServices.indexOf(packageName);
1487 if (accessibilityIndex >= 0) {
1488 mAccessibilityServices.remove(accessibilityIndex);
1489 Index.getInstance(mContext).updateFromClassNameResource(
1490 AccessibilitySettings.class.getName(), true, true);
1491 }
1492
1493 final int printIndex = mPrintServices.indexOf(packageName);
1494 if (printIndex >= 0) {
1495 mPrintServices.remove(printIndex);
1496 Index.getInstance(mContext).updateFromClassNameResource(
1497 PrintSettingsFragment.class.getName(), true, true);
1498 }
1499 }
Svetoslav990159a2014-04-14 17:14:59 -07001500
1501 private final class MyContentObserver extends ContentObserver {
1502
1503 public MyContentObserver(Handler handler) {
1504 super(handler);
1505 }
1506
1507 @Override
1508 public void onChange(boolean selfChange, Uri uri) {
1509 if (UserDictionary.Words.CONTENT_URI.equals(uri)) {
1510 Index.getInstance(mContext).updateFromClassNameResource(
1511 InputMethodAndLanguageSettings.class.getName(), true, true);
1512 }
1513 };
1514 }
Svetoslav853e4712014-04-14 10:10:25 -07001515 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001516}