blob: db7eb2b80eae8f51fbc543591fde13e663d71952 [file] [log] [blame]
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001/*
2 * Copyright (C) 2008 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
Amith Yamasani56821db2012-06-05 13:20:45 -070019import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
Christopher Tatee0d934f2013-10-01 12:32:27 -070022import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.app.DialogFragment;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -040026import android.app.admin.DevicePolicyManager;
Jaewan Kima3fe77b2013-06-04 21:17:40 +090027import android.content.BroadcastReceiver;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -080028import android.content.ComponentName;
Gilles Debunnee78c1872011-06-20 15:00:07 -070029import android.content.Context;
Amith Yamasani379d9b02010-09-27 12:03:59 -070030import android.content.Intent;
Jaewan Kima3fe77b2013-06-04 21:17:40 +090031import android.content.IntentFilter;
Dianne Hackborncaefa9b2012-10-10 15:05:42 -070032import android.content.SharedPreferences;
Amith Yamasani5203bdf2010-11-04 09:59:44 -070033import android.content.pm.ActivityInfo;
34import android.content.pm.PackageManager;
35import android.content.pm.PackageManager.NameNotFoundException;
Christopher Tatee238a472013-08-27 14:21:46 -070036import android.content.pm.ResolveInfo;
Amith Yamasanid1ab8282012-05-18 09:50:08 -070037import android.graphics.drawable.Drawable;
Martijn Coenen81ab8172013-09-30 12:07:02 -070038import android.nfc.NfcAdapter;
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -070039import android.os.Bundle;
Jeff Sharkey34e964d2012-04-21 15:41:48 -070040import android.os.INetworkManagementService;
41import android.os.RemoteException;
42import android.os.ServiceManager;
Dianne Hackbornbb06a422012-08-16 11:01:57 -070043import android.os.UserHandle;
Jeff Sharkey44202462012-09-19 13:13:45 -070044import android.os.UserManager;
Amith Yamasania4379d62011-07-22 10:34:58 -070045import android.preference.Preference;
Amith Yamasani02cf71a2010-09-21 15:48:52 -070046import android.preference.PreferenceActivity;
Amith Yamasania4379d62011-07-22 10:34:58 -070047import android.preference.PreferenceFragment;
Gilles Debunnee78c1872011-06-20 15:00:07 -070048import android.text.TextUtils;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -080049import android.util.Log;
Gilles Debunnee78c1872011-06-20 15:00:07 -070050import android.view.LayoutInflater;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -080051import android.view.View;
52import android.view.View.OnClickListener;
Gilles Debunnee78c1872011-06-20 15:00:07 -070053import android.view.ViewGroup;
54import android.widget.ArrayAdapter;
Amith Yamasani9e3a4702011-01-11 09:09:26 -080055import android.widget.Button;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -040056import android.widget.ImageButton;
Gilles Debunnee78c1872011-06-20 15:00:07 -070057import android.widget.ImageView;
58import android.widget.ListAdapter;
59import android.widget.Switch;
60import android.widget.TextView;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080061
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -080062import com.android.internal.util.ArrayUtils;
Amith Yamasania677ee22013-07-26 13:38:41 -070063import com.android.settings.accessibility.AccessibilitySettings;
Alan Viverette341ff662013-07-18 17:49:33 -070064import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
Alan Viverette9b625f02013-09-11 13:03:14 -070065import com.android.settings.accessibility.ToggleCaptioningPreferenceFragment;
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -080066import com.android.settings.accounts.AccountSyncSettings;
67import com.android.settings.accounts.AuthenticatorHelper;
68import com.android.settings.accounts.ManageAccountsSettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070069import com.android.settings.applications.AppOpsSummary;
70import com.android.settings.applications.ManageApplications;
Dianne Hackbornc6d658e2013-08-08 12:57:53 -070071import com.android.settings.applications.ProcessStatsUi;
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -080072import com.android.settings.bluetooth.BluetoothEnabler;
73import com.android.settings.bluetooth.BluetoothSettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070074import com.android.settings.deviceinfo.Memory;
75import com.android.settings.deviceinfo.UsbSettings;
76import com.android.settings.fuelgauge.PowerUsageSummary;
77import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
Amith Yamasani3a14ae22013-08-30 15:43:44 -070078import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
Amith Yamasania677ee22013-07-26 13:38:41 -070079import com.android.settings.inputmethod.SpellCheckersSettings;
80import com.android.settings.inputmethod.UserDictionaryList;
Amith Yamasanie9904202013-08-01 10:26:14 -070081import com.android.settings.location.LocationSettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070082import com.android.settings.nfc.AndroidBeam;
Martijn Coenen53083ab2013-08-09 13:37:38 -070083import com.android.settings.nfc.PaymentSettings;
Svetoslavd9f156d2013-10-09 21:59:04 -070084import com.android.settings.print.PrintJobSettingsFragment;
Svetoslav2d531652013-09-03 21:32:40 -070085import com.android.settings.print.PrintServiceSettingsFragment;
86import com.android.settings.print.PrintSettingsFragment;
Amith Yamasania677ee22013-07-26 13:38:41 -070087import com.android.settings.tts.TextToSpeechSettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070088import com.android.settings.users.UserSettings;
89import com.android.settings.vpn2.VpnSettings;
Jeff Sharkey0c3634c2013-02-20 12:35:16 -080090import com.android.settings.wfd.WifiDisplaySettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070091import com.android.settings.wifi.AdvancedWifiSettings;
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -080092import com.android.settings.wifi.WifiEnabler;
93import com.android.settings.wifi.WifiSettings;
94import com.android.settings.wifi.p2p.WifiP2pSettings;
95
Gilles Debunnee78c1872011-06-20 15:00:07 -070096import java.util.ArrayList;
Amith Yamasanid1ab8282012-05-18 09:50:08 -070097import java.util.Collections;
98import java.util.Comparator;
Amith Yamasani5203bdf2010-11-04 09:59:44 -070099import java.util.HashMap;
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700100import java.util.List;
Amith Yamasanid7993472010-08-18 13:59:28 -0700101
102/**
103 * Top-level settings activity to handle single pane and double pane UI layout.
104 */
Amith Yamasani56821db2012-06-05 13:20:45 -0700105public class Settings extends PreferenceActivity
106 implements ButtonBarHandler, OnAccountsUpdateListener {
Amith Yamasanid7993472010-08-18 13:59:28 -0700107
Gilles Debunnee78c1872011-06-20 15:00:07 -0700108 private static final String LOG_TAG = "Settings";
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700109
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700110 private static final String META_DATA_KEY_HEADER_ID =
Gilles Debunnee78c1872011-06-20 15:00:07 -0700111 "com.android.settings.TOP_LEVEL_HEADER_ID";
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700112 private static final String META_DATA_KEY_FRAGMENT_CLASS =
Gilles Debunnee78c1872011-06-20 15:00:07 -0700113 "com.android.settings.FRAGMENT_CLASS";
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800114 private static final String META_DATA_KEY_PARENT_TITLE =
115 "com.android.settings.PARENT_FRAGMENT_TITLE";
116 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
117 "com.android.settings.PARENT_FRAGMENT_CLASS";
118
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800119 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700120
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800121 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
122 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700123
Christopher Tatee0d934f2013-10-01 12:32:27 -0700124 static final int DIALOG_ONLY_ONE_HOME = 1;
125
126 private static boolean sShowNoHomeNotice = false;
127
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700128 private String mFragmentClass;
129 private int mTopLevelHeaderId;
Amith Yamasani3965ae62010-11-15 14:45:19 -0800130 private Header mFirstHeader;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800131 private Header mCurrentHeader;
132 private Header mParentHeader;
133 private boolean mInLocalHeaderSwitch;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700134
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700135 // Show only these settings for restricted users
136 private int[] SETTINGS_FOR_RESTRICTED = {
Amith Yamasani32630392012-08-10 19:31:39 -0700137 R.id.wireless_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700138 R.id.wifi_settings,
139 R.id.bluetooth_settings,
Jeff Sharkey38305fb2012-09-14 16:26:51 -0700140 R.id.data_usage_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700141 R.id.wireless_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700142 R.id.device_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700143 R.id.sound_settings,
144 R.id.display_settings,
Jeff Sharkey90c8b202012-08-30 15:20:10 -0700145 R.id.storage_settings,
Dianne Hackborn271c8b02012-08-20 17:24:39 -0700146 R.id.application_settings,
Dianne Hackborn52e56a22012-09-12 17:02:23 -0700147 R.id.battery_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700148 R.id.personal_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700149 R.id.location_settings,
Amith Yamasani3deeeb72012-04-05 14:44:48 -0700150 R.id.security_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700151 R.id.language_settings,
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700152 R.id.user_settings,
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700153 R.id.account_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700154 R.id.account_add,
155 R.id.system_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700156 R.id.date_time_settings,
Jeff Brown9e143f52012-09-19 20:46:07 -0700157 R.id.about_settings,
Svetoslav Ganovd4c7b492013-06-11 21:15:11 -0700158 R.id.accessibility_settings,
Martijn Coenen26515da2013-08-01 18:13:33 -0700159 R.id.print_settings,
Christopher Tatee238a472013-08-27 14:21:46 -0700160 R.id.nfc_payment_settings,
161 R.id.home_settings
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700162 };
163
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700164 private SharedPreferences mDevelopmentPreferences;
165 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
166
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700167 // TODO: Update Call Settings based on airplane mode state.
Amith Yamasanib61cf512010-09-12 08:17:50 -0700168
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700169 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
170
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700171 private AuthenticatorHelper mAuthenticatorHelper;
172 private Header mLastHeader;
Amith Yamasani86708a82012-06-06 20:23:08 -0700173 private boolean mListeningToAccountUpdates;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700174
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900175 private boolean mBatteryPresent = true;
176 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
177
178 @Override
179 public void onReceive(Context context, Intent intent) {
180 String action = intent.getAction();
181 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
182 boolean batteryPresent = Utils.isBatteryPresent(intent);
183
184 if (mBatteryPresent != batteryPresent) {
185 mBatteryPresent = batteryPresent;
186 invalidateHeaders();
187 }
188 }
189 }
190 };
191
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700192 @Override
193 protected void onCreate(Bundle savedInstanceState) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800194 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
195 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700196 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700197
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700198 mAuthenticatorHelper = new AuthenticatorHelper();
199 mAuthenticatorHelper.updateAuthDescriptions(this);
200 mAuthenticatorHelper.onAccountsUpdated(this, null);
201
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700202 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
203 Context.MODE_PRIVATE);
204
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700205 getMetaData();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800206 mInLocalHeaderSwitch = true;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700207 super.onCreate(savedInstanceState);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800208 mInLocalHeaderSwitch = false;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700209
Gilles Debunne3661b622011-06-27 11:19:16 -0700210 if (!onIsHidingHeaders() && onIsMultiPane()) {
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700211 highlightHeader(mTopLevelHeaderId);
Amith Yamasani72aa19d2010-12-09 06:07:12 -0800212 // Force the title so that it doesn't get overridden by a direct launch of
213 // a specific settings screen.
214 setTitle(R.string.settings_label);
215 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800216
217 // Retrieve any saved state
218 if (savedInstanceState != null) {
219 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
220 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
221 }
222
223 // If the current header was saved, switch to it
224 if (savedInstanceState != null && mCurrentHeader != null) {
225 //switchToHeaderLocal(mCurrentHeader);
226 showBreadCrumbs(mCurrentHeader.title, null);
227 }
228
229 if (mParentHeader != null) {
230 setParentTitle(mParentHeader.title, null, new OnClickListener() {
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400231 @Override
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800232 public void onClick(View v) {
233 switchToParent(mParentHeader.fragment);
234 }
235 });
236 }
Gilles Debunnedc7101f2011-06-27 12:00:49 -0700237
Amith Yamasani3d384f42012-05-11 15:22:04 -0700238 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
239 if (onIsMultiPane()) {
240 getActionBar().setDisplayHomeAsUpEnabled(false);
241 getActionBar().setHomeButtonEnabled(false);
242 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800243 }
244
245 @Override
246 protected void onSaveInstanceState(Bundle outState) {
247 super.onSaveInstanceState(outState);
248
249 // Save the current fragment, if it is the same as originally launched
250 if (mCurrentHeader != null) {
251 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
252 }
253 if (mParentHeader != null) {
254 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
255 }
256 }
257
Gilles Debunnee78c1872011-06-20 15:00:07 -0700258 @Override
259 public void onResume() {
260 super.onResume();
261
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700262 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
263 @Override
264 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
265 invalidateHeaders();
266 }
267 };
268 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
269 mDevelopmentPreferencesListener);
270
Gilles Debunnee78c1872011-06-20 15:00:07 -0700271 ListAdapter listAdapter = getListAdapter();
272 if (listAdapter instanceof HeaderAdapter) {
273 ((HeaderAdapter) listAdapter).resume();
274 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700275 invalidateHeaders();
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900276
277 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Gilles Debunnee78c1872011-06-20 15:00:07 -0700278 }
279
280 @Override
281 public void onPause() {
282 super.onPause();
283
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900284 unregisterReceiver(mBatteryInfoReceiver);
285
Gilles Debunnee78c1872011-06-20 15:00:07 -0700286 ListAdapter listAdapter = getListAdapter();
287 if (listAdapter instanceof HeaderAdapter) {
288 ((HeaderAdapter) listAdapter).pause();
Amith Yamasani86708a82012-06-06 20:23:08 -0700289 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700290
291 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
292 mDevelopmentPreferencesListener);
293 mDevelopmentPreferencesListener = null;
Amith Yamasani86708a82012-06-06 20:23:08 -0700294 }
295
296 @Override
297 public void onDestroy() {
298 super.onDestroy();
299 if (mListeningToAccountUpdates) {
Amith Yamasani56821db2012-06-05 13:20:45 -0700300 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700301 }
302 }
303
Amith Yamasani56f51a82013-08-05 10:07:23 -0700304 @Override
305 public boolean onIsMultiPane() {
306 return false;
307 }
308
Amith Yamasania677ee22013-07-26 13:38:41 -0700309 private static final String[] ENTRY_FRAGMENTS = {
310 WirelessSettings.class.getName(),
311 WifiSettings.class.getName(),
312 AdvancedWifiSettings.class.getName(),
313 BluetoothSettings.class.getName(),
314 TetherSettings.class.getName(),
315 WifiP2pSettings.class.getName(),
316 VpnSettings.class.getName(),
317 DateTimeSettings.class.getName(),
318 LocalePicker.class.getName(),
319 InputMethodAndLanguageSettings.class.getName(),
320 SpellCheckersSettings.class.getName(),
321 UserDictionaryList.class.getName(),
322 UserDictionarySettings.class.getName(),
323 SoundSettings.class.getName(),
324 DisplaySettings.class.getName(),
325 DeviceInfoSettings.class.getName(),
326 ManageApplications.class.getName(),
Dianne Hackbornc6d658e2013-08-08 12:57:53 -0700327 ProcessStatsUi.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700328 NotificationStation.class.getName(),
329 AppOpsSummary.class.getName(),
330 LocationSettings.class.getName(),
331 SecuritySettings.class.getName(),
332 PrivacySettings.class.getName(),
333 DeviceAdminSettings.class.getName(),
334 AccessibilitySettings.class.getName(),
Alan Viverette9b625f02013-09-11 13:03:14 -0700335 ToggleCaptioningPreferenceFragment.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700336 TextToSpeechSettings.class.getName(),
337 Memory.class.getName(),
338 DevelopmentSettings.class.getName(),
339 UsbSettings.class.getName(),
340 AndroidBeam.class.getName(),
341 WifiDisplaySettings.class.getName(),
342 PowerUsageSummary.class.getName(),
343 AccountSyncSettings.class.getName(),
344 CryptKeeperSettings.class.getName(),
345 DataUsageSummary.class.getName(),
346 DreamSettings.class.getName(),
347 UserSettings.class.getName(),
348 NotificationAccessSettings.class.getName(),
Amith Yamasaniee226f92013-08-05 11:02:16 -0700349 ManageAccountsSettings.class.getName(),
Svetoslav2d531652013-09-03 21:32:40 -0700350 PrintSettingsFragment.class.getName(),
Svetoslavd9f156d2013-10-09 21:59:04 -0700351 PrintJobSettingsFragment.class.getName(),
Martijn Coenen53083ab2013-08-09 13:37:38 -0700352 TrustedCredentialsSettings.class.getName(),
Amith Yamasani3a14ae22013-08-30 15:43:44 -0700353 PaymentSettings.class.getName(),
354 KeyboardLayoutPickerFragment.class.getName()
Amith Yamasania677ee22013-07-26 13:38:41 -0700355 };
356
357 @Override
358 protected boolean isValidFragment(String fragmentName) {
359 // Almost all fragments are wrapped in this,
360 // except for a few that have their own activities.
361 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
362 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
363 }
364 return false;
365 }
366
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800367 private void switchToHeaderLocal(Header header) {
368 mInLocalHeaderSwitch = true;
369 switchToHeader(header);
370 mInLocalHeaderSwitch = false;
371 }
372
373 @Override
374 public void switchToHeader(Header header) {
375 if (!mInLocalHeaderSwitch) {
376 mCurrentHeader = null;
377 mParentHeader = null;
378 }
379 super.switchToHeader(header);
380 }
381
382 /**
383 * Switch to parent fragment and store the grand parent's info
Jake Hamby2748fc22011-01-12 15:06:28 -0800384 * @param className name of the activity wrapper for the parent fragment.
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800385 */
386 private void switchToParent(String className) {
387 final ComponentName cn = new ComponentName(this, className);
388 try {
389 final PackageManager pm = getPackageManager();
390 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
391
392 if (parentInfo != null && parentInfo.metaData != null) {
393 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
394 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
395 Header parentHeader = new Header();
396 parentHeader.fragment = fragmentClass;
397 parentHeader.title = fragmentTitle;
398 mCurrentHeader = parentHeader;
399
400 switchToHeaderLocal(parentHeader);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700401 highlightHeader(mTopLevelHeaderId);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800402
403 mParentHeader = new Header();
404 mParentHeader.fragment
405 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
406 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
407 }
408 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700409 Log.w(LOG_TAG, "Could not find parent activity : " + className);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800410 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700411 }
412
Amith Yamasani3965ae62010-11-15 14:45:19 -0800413 @Override
414 public void onNewIntent(Intent intent) {
415 super.onNewIntent(intent);
416
417 // If it is not launched from history, then reset to top-level
Amith Yamasanief617232012-10-09 16:57:25 -0700418 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
419 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
420 switchToHeaderLocal(mFirstHeader);
421 }
422 getListView().setSelectionFromTop(0, 0);
Amith Yamasani3965ae62010-11-15 14:45:19 -0800423 }
424 }
425
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700426 private void highlightHeader(int id) {
427 if (id != 0) {
428 Integer index = mHeaderIndexMap.get(id);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700429 if (index != null) {
430 getListView().setItemChecked(index, true);
Amith Yamasanief617232012-10-09 16:57:25 -0700431 if (isMultiPane()) {
432 getListView().smoothScrollToPosition(index);
433 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700434 }
435 }
436 }
437
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700438 @Override
439 public Intent getIntent() {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700440 Intent superIntent = super.getIntent();
441 String startingFragment = getStartingFragmentClass(superIntent);
Gilles Debunne3661b622011-06-27 11:19:16 -0700442 // This is called from super.onCreate, isMultiPane() is not yet reliable
443 // Do not use onIsHidingHeaders either, which relies itself on this method
444 if (startingFragment != null && !onIsMultiPane()) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700445 Intent modIntent = new Intent(superIntent);
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700446 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700447 Bundle args = superIntent.getExtras();
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700448 if (args != null) {
449 args = new Bundle(args);
450 } else {
451 args = new Bundle();
452 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700453 args.putParcelable("intent", superIntent);
454 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700455 return modIntent;
456 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700457 return superIntent;
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700458 }
459
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700460 /**
Amith Yamasani379d9b02010-09-27 12:03:59 -0700461 * Checks if the component name in the intent is different from the Settings class and
462 * returns the class name to load as a fragment.
463 */
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700464 protected String getStartingFragmentClass(Intent intent) {
465 if (mFragmentClass != null) return mFragmentClass;
466
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700467 String intentClass = intent.getComponent().getClassName();
Amith Yamasani379d9b02010-09-27 12:03:59 -0700468 if (intentClass.equals(getClass().getName())) return null;
469
Dianne Hackbornee293792010-11-01 12:32:33 -0700470 if ("com.android.settings.ManageApplications".equals(intentClass)
471 || "com.android.settings.RunningServices".equals(intentClass)
472 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700473 // Old names of manage apps.
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700474 intentClass = com.android.settings.applications.ManageApplications.class.getName();
475 }
476
Amith Yamasani379d9b02010-09-27 12:03:59 -0700477 return intentClass;
478 }
479
480 /**
481 * Override initial header when an activity-alias is causing Settings to be launched
482 * for a specific fragment encoded in the android:name parameter.
483 */
484 @Override
485 public Header onGetInitialHeader() {
486 String fragmentClass = getStartingFragmentClass(super.getIntent());
487 if (fragmentClass != null) {
488 Header header = new Header();
489 header.fragment = fragmentClass;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800490 header.title = getTitle();
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700491 header.fragmentArguments = getIntent().getExtras();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800492 mCurrentHeader = header;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700493 return header;
494 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700495
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700496 return mFirstHeader;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700497 }
498
Dianne Hackbornb7258182011-03-15 16:23:55 -0700499 @Override
Dianne Hackborn48147dc2011-03-18 12:29:41 -0700500 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
501 int titleRes, int shortTitleRes) {
502 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
503 titleRes, shortTitleRes);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700504
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800505 // Some fragments want split ActionBar; these should stay in sync with
506 // uiOptions for fragments also defined as activities in manifest.
507 if (WifiSettings.class.getName().equals(fragmentName) ||
508 WifiP2pSettings.class.getName().equals(fragmentName) ||
509 BluetoothSettings.class.getName().equals(fragmentName) ||
510 DreamSettings.class.getName().equals(fragmentName) ||
Lifu Tangd5fbbc42013-08-01 17:23:10 -0700511 LocationSettings.class.getName().equals(fragmentName) ||
Svetoslav2d531652013-09-03 21:32:40 -0700512 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
513 PrintSettingsFragment.class.getName().equals(fragmentName) ||
514 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800515 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700516 }
517
Dianne Hackbornb7258182011-03-15 16:23:55 -0700518 intent.setClass(this, SubSettings.class);
519 return intent;
520 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700521
Amith Yamasani379d9b02010-09-27 12:03:59 -0700522 /**
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700523 * Populate the activity with the top-level headers.
524 */
Amith Yamasanid7993472010-08-18 13:59:28 -0700525 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -0700526 public void onBuildHeaders(List<Header> headers) {
Amith Yamasani2972c012013-10-02 11:48:46 -0700527 if (!onIsHidingHeaders()) {
528 loadHeadersFromResource(R.xml.settings_headers, headers);
529 updateHeaderList(headers);
530 }
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700531 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700532
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700533 private void updateHeaderList(List<Header> target) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700534 final boolean showDev = mDevelopmentPreferences.getBoolean(
535 DevelopmentSettings.PREF_SHOW,
536 android.os.Build.TYPE.equals("eng"));
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700537 int i = 0;
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400538
539 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700540 mHeaderIndexMap.clear();
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700541 while (i < target.size()) {
542 Header header = target.get(i);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700543 // Ids are integers, so downcasting
544 int id = (int) header.id;
Chris Wren2bc32ae2012-09-24 14:23:46 -0400545 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700546 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
Gilles Debunne2454f492011-06-21 16:16:06 -0700547 } else if (id == R.id.wifi_settings) {
548 // Remove WiFi Settings if WiFi service is not available.
549 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700550 target.remove(i);
Gilles Debunne2454f492011-06-21 16:16:06 -0700551 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700552 } else if (id == R.id.bluetooth_settings) {
553 // Remove Bluetooth Settings if Bluetooth service is not available.
Gilles Debunne2454f492011-06-21 16:16:06 -0700554 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700555 target.remove(i);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700556 }
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700557 } else if (id == R.id.data_usage_settings) {
558 // Remove data usage when kernel module not enabled
559 final INetworkManagementService netManager = INetworkManagementService.Stub
560 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
561 try {
562 if (!netManager.isBandwidthControlEnabled()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700563 target.remove(i);
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700564 }
565 } catch (RemoteException e) {
566 // ignored
567 }
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900568 } else if (id == R.id.battery_settings) {
569 // Remove battery settings when battery is not available. (e.g. TV)
570
571 if (!mBatteryPresent) {
572 target.remove(i);
573 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700574 } else if (id == R.id.account_settings) {
575 int headerIndex = i + 1;
576 i = insertAccountsHeaders(target, headerIndex);
Christopher Tatee238a472013-08-27 14:21:46 -0700577 } else if (id == R.id.home_settings) {
Christopher Tate8243c9a2013-09-19 14:59:17 -0700578 if (!updateHomeSettingHeaders(header)) {
579 target.remove(i);
580 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700581 } else if (id == R.id.user_settings) {
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700582 if (!UserHandle.MU_ENABLED
Jeff Sharkey44202462012-09-19 13:13:45 -0700583 || !UserManager.supportsMultipleUsers()
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700584 || Utils.isMonkeyRunning()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700585 target.remove(i);
586 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700587 } else if (id == R.id.nfc_payment_settings) {
Martijn Coenen81ab8172013-09-30 12:07:02 -0700588 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
Martijn Coenen26515da2013-08-01 18:13:33 -0700589 target.remove(i);
Martijn Coenen81ab8172013-09-30 12:07:02 -0700590 } else {
591 // Only show if NFC is on and we have the HCE feature
592 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
593 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
594 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
595 target.remove(i);
596 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700597 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700598 } else if (id == R.id.development_settings) {
599 if (!showDev) {
600 target.remove(i);
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700601 }
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400602 } else if (id == R.id.account_add) {
603 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
604 target.remove(i);
605 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700606 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700607
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900608 if (i < target.size() && target.get(i) == header
Amith Yamasanib99f8642012-10-10 21:41:12 -0700609 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700610 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700611 target.remove(i);
Amith Yamasanid7993472010-08-18 13:59:28 -0700612 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700613
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700614 // Increment if the current one wasn't removed by the Utils code.
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900615 if (i < target.size() && target.get(i) == header) {
Amith Yamasani3965ae62010-11-15 14:45:19 -0800616 // Hold on to the first header, when we need to reset to the top-level
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700617 if (mFirstHeader == null &&
618 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
619 mFirstHeader = header;
620 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700621 mHeaderIndexMap.put(id, i);
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700622 i++;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700623 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700624 }
625 }
626
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700627 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
628 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
629 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
630 for (String accountType : accountTypes) {
631 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
Brian Muramatsuc28af522012-06-28 14:25:14 -0700632 if (label == null) {
633 continue;
634 }
635
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700636 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
637 boolean skipToAccount = accounts.length == 1
638 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700639 Header accHeader = new Header();
640 accHeader.title = label;
641 if (accHeader.extras == null) {
642 accHeader.extras = new Bundle();
643 }
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700644 if (skipToAccount) {
645 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
646 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
647 accHeader.fragment = AccountSyncSettings.class.getName();
648 accHeader.fragmentArguments = new Bundle();
649 // Need this for the icon
650 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
651 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
652 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
653 accounts[0]);
654 } else {
655 accHeader.breadCrumbTitle = label;
656 accHeader.breadCrumbShortTitle = label;
657 accHeader.fragment = ManageAccountsSettings.class.getName();
658 accHeader.fragmentArguments = new Bundle();
659 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
660 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
661 accountType);
662 if (!isMultiPane()) {
663 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
664 label.toString());
665 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700666 }
667 accountHeaders.add(accHeader);
Amith Yamasani2972c012013-10-02 11:48:46 -0700668 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700669 }
670
671 // Sort by label
672 Collections.sort(accountHeaders, new Comparator<Header>() {
673 @Override
674 public int compare(Header h1, Header h2) {
675 return h1.title.toString().compareTo(h2.title.toString());
676 }
677 });
678
679 for (Header header : accountHeaders) {
680 target.add(headerIndex++, header);
681 }
Amith Yamasani86708a82012-06-06 20:23:08 -0700682 if (!mListeningToAccountUpdates) {
683 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
684 mListeningToAccountUpdates = true;
685 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700686 return headerIndex;
687 }
688
Christopher Tate8243c9a2013-09-19 14:59:17 -0700689 private boolean updateHomeSettingHeaders(Header header) {
Christopher Tatedc7be102013-10-08 11:30:19 -0700690 // Once we decide to show Home settings, keep showing it forever
691 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
692 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
693 return true;
694 }
695
Christopher Tatee238a472013-08-27 14:21:46 -0700696 try {
Jeff Sharkeyd572a032013-09-23 17:41:16 -0700697 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
698 getPackageManager().getHomeActivities(homeApps);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700699 if (homeApps.size() < 2) {
700 // When there's only one available home app, omit this settings
Christopher Tatee0d934f2013-10-01 12:32:27 -0700701 // category entirely at the top level UI. If the user just
702 // uninstalled the penultimate home app candidiate, we also
703 // now tell them about why they aren't seeing 'Home' in the list.
704 if (sShowNoHomeNotice) {
705 sShowNoHomeNotice = false;
706 NoHomeDialogFragment.show(this);
707 }
Christopher Tate8243c9a2013-09-19 14:59:17 -0700708 return false;
Christopher Tatee0d934f2013-10-01 12:32:27 -0700709 } else {
710 // Okay, we're allowing the Home settings category. Tell it, when
711 // invoked via this front door, that we'll need to be told about the
712 // case when the user uninstalls all but one home app.
713 if (header.fragmentArguments == null) {
714 header.fragmentArguments = new Bundle();
715 }
716 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700717 }
Christopher Tatee238a472013-08-27 14:21:46 -0700718 } catch (Exception e) {
719 // Can't look up the home activity; bail on configuring the icon
720 Log.w(LOG_TAG, "Problem looking up home activity!", e);
721 }
Christopher Tatedc7be102013-10-08 11:30:19 -0700722
723 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
Christopher Tate8243c9a2013-09-19 14:59:17 -0700724 return true;
Christopher Tatee238a472013-08-27 14:21:46 -0700725 }
726
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700727 private void getMetaData() {
728 try {
729 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
730 PackageManager.GET_META_DATA);
731 if (ai == null || ai.metaData == null) return;
732 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
733 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700734
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800735 // Check if it has a parent specified and create a Header object
736 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
737 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
738 if (parentFragmentClass != null) {
739 mParentHeader = new Header();
740 mParentHeader.fragment = parentFragmentClass;
741 if (parentHeaderTitleRes != 0) {
742 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
743 }
744 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700745 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700746 // No recovery
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700747 }
748 }
749
Amith Yamasani9e3a4702011-01-11 09:09:26 -0800750 @Override
751 public boolean hasNextButton() {
752 return super.hasNextButton();
753 }
754
755 @Override
756 public Button getNextButton() {
757 return super.getNextButton();
758 }
759
Christopher Tatee0d934f2013-10-01 12:32:27 -0700760 public static class NoHomeDialogFragment extends DialogFragment {
761 public static void show(Activity parent) {
762 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
763 dialog.show(parent.getFragmentManager(), null);
764 }
765
766 @Override
767 public Dialog onCreateDialog(Bundle savedInstanceState) {
768 return new AlertDialog.Builder(getActivity())
769 .setMessage(R.string.only_one_home_message)
770 .setPositiveButton(android.R.string.ok, null)
771 .create();
772 }
773 }
774
Gilles Debunnee78c1872011-06-20 15:00:07 -0700775 private static class HeaderAdapter extends ArrayAdapter<Header> {
776 static final int HEADER_TYPE_CATEGORY = 0;
777 static final int HEADER_TYPE_NORMAL = 1;
778 static final int HEADER_TYPE_SWITCH = 2;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400779 static final int HEADER_TYPE_BUTTON = 3;
780 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700781
782 private final WifiEnabler mWifiEnabler;
783 private final BluetoothEnabler mBluetoothEnabler;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700784 private AuthenticatorHelper mAuthHelper;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400785 private DevicePolicyManager mDevicePolicyManager;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700786
787 private static class HeaderViewHolder {
788 ImageView icon;
789 TextView title;
790 TextView summary;
791 Switch switch_;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400792 ImageButton button_;
793 View divider_;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700794 }
795
796 private LayoutInflater mInflater;
797
798 static int getHeaderType(Header header) {
799 if (header.fragment == null && header.intent == null) {
800 return HEADER_TYPE_CATEGORY;
801 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
802 return HEADER_TYPE_SWITCH;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400803 } else if (header.id == R.id.security_settings) {
804 return HEADER_TYPE_BUTTON;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700805 } else {
806 return HEADER_TYPE_NORMAL;
807 }
808 }
809
810 @Override
811 public int getItemViewType(int position) {
812 Header header = getItem(position);
813 return getHeaderType(header);
814 }
815
816 @Override
817 public boolean areAllItemsEnabled() {
818 return false; // because of categories
819 }
820
821 @Override
822 public boolean isEnabled(int position) {
823 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
824 }
825
826 @Override
827 public int getViewTypeCount() {
828 return HEADER_TYPE_COUNT;
829 }
830
831 @Override
832 public boolean hasStableIds() {
833 return true;
834 }
835
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700836 public HeaderAdapter(Context context, List<Header> objects,
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400837 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700838 super(context, 0, objects);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700839
840 mAuthHelper = authenticatorHelper;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700841 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700842
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700843 // Temp Switches provided as placeholder until the adapter replaces these with actual
Gilles Debunnee78c1872011-06-20 15:00:07 -0700844 // Switches inflated from their layouts. Must be done before adapter is set in super
845 mWifiEnabler = new WifiEnabler(context, new Switch(context));
846 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400847 mDevicePolicyManager = dpm;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700848 }
849
850 @Override
851 public View getView(int position, View convertView, ViewGroup parent) {
852 HeaderViewHolder holder;
853 Header header = getItem(position);
854 int headerType = getHeaderType(header);
855 View view = null;
856
857 if (convertView == null) {
858 holder = new HeaderViewHolder();
859 switch (headerType) {
860 case HEADER_TYPE_CATEGORY:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700861 view = new TextView(getContext(), null,
862 android.R.attr.listSeparatorTextViewStyle);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700863 holder.title = (TextView) view;
864 break;
865
866 case HEADER_TYPE_SWITCH:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700867 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
868 false);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700869 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700870 holder.title = (TextView)
871 view.findViewById(com.android.internal.R.id.title);
872 holder.summary = (TextView)
873 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700874 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
875 break;
876
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400877 case HEADER_TYPE_BUTTON:
878 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
879 false);
880 holder.icon = (ImageView) view.findViewById(R.id.icon);
881 holder.title = (TextView)
882 view.findViewById(com.android.internal.R.id.title);
883 holder.summary = (TextView)
884 view.findViewById(com.android.internal.R.id.summary);
885 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
886 holder.divider_ = view.findViewById(R.id.divider);
887 break;
888
Gilles Debunnee78c1872011-06-20 15:00:07 -0700889 case HEADER_TYPE_NORMAL:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700890 view = mInflater.inflate(
Amith Yamasanic8a93172012-06-08 13:35:47 -0700891 R.layout.preference_header_item, parent,
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700892 false);
Amith Yamasanic8a93172012-06-08 13:35:47 -0700893 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700894 holder.title = (TextView)
895 view.findViewById(com.android.internal.R.id.title);
896 holder.summary = (TextView)
897 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700898 break;
899 }
900 view.setTag(holder);
901 } else {
902 view = convertView;
903 holder = (HeaderViewHolder) view.getTag();
904 }
905
906 // All view fields must be updated every time, because the view may be recycled
907 switch (headerType) {
908 case HEADER_TYPE_CATEGORY:
909 holder.title.setText(header.getTitle(getContext().getResources()));
910 break;
911
912 case HEADER_TYPE_SWITCH:
913 // Would need a different treatment if the main menu had more switches
914 if (header.id == R.id.wifi_settings) {
915 mWifiEnabler.setSwitch(holder.switch_);
916 } else {
917 mBluetoothEnabler.setSwitch(holder.switch_);
918 }
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400919 updateCommonHeaderView(header, holder);
920 break;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700921
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400922 case HEADER_TYPE_BUTTON:
923 if (header.id == R.id.security_settings) {
924 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
925 if (hasCert) {
926 holder.button_.setVisibility(View.VISIBLE);
927 holder.divider_.setVisibility(View.VISIBLE);
928 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
929 if (isManaged) {
Maggie Benthall273155c2013-09-18 16:53:23 -0400930 holder.button_.setImageResource(R.drawable.ic_settings_about);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400931 } else {
932 holder.button_.setImageResource(
933 android.R.drawable.stat_notify_error);
934 }
935 holder.button_.setOnClickListener(new OnClickListener() {
936 @Override
937 public void onClick(View v) {
938 Intent intent = new Intent(
939 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
940 getContext().startActivity(intent);
941 }
942 });
943 } else {
944 holder.button_.setVisibility(View.GONE);
945 holder.divider_.setVisibility(View.GONE);
946 }
947 }
948 updateCommonHeaderView(header, holder);
949 break;
950
Gilles Debunnee78c1872011-06-20 15:00:07 -0700951 case HEADER_TYPE_NORMAL:
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400952 updateCommonHeaderView(header, holder);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700953 break;
954 }
955
956 return view;
957 }
958
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400959 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
960 if (header.extras != null
961 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
962 String accType = header.extras.getString(
963 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
964 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
965 setHeaderIcon(holder, icon);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400966 } else {
967 holder.icon.setImageResource(header.iconRes);
968 }
969 holder.title.setText(header.getTitle(getContext().getResources()));
970 CharSequence summary = header.getSummary(getContext().getResources());
971 if (!TextUtils.isEmpty(summary)) {
972 holder.summary.setVisibility(View.VISIBLE);
973 holder.summary.setText(summary);
974 } else {
975 holder.summary.setVisibility(View.GONE);
976 }
977 }
978
Christopher Tatee238a472013-08-27 14:21:46 -0700979 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
980 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
981 lp.width = getContext().getResources().getDimensionPixelSize(
982 R.dimen.header_icon_width);
983 lp.height = lp.width;
984 holder.icon.setLayoutParams(lp);
985 holder.icon.setImageDrawable(icon);
986 }
987
Gilles Debunnee78c1872011-06-20 15:00:07 -0700988 public void resume() {
989 mWifiEnabler.resume();
990 mBluetoothEnabler.resume();
991 }
Brian Muramatsuc28af522012-06-28 14:25:14 -0700992
Gilles Debunnee78c1872011-06-20 15:00:07 -0700993 public void pause() {
994 mWifiEnabler.pause();
995 mBluetoothEnabler.pause();
996 }
997 }
998
999 @Override
Amith Yamasanid1ab8282012-05-18 09:50:08 -07001000 public void onHeaderClick(Header header, int position) {
1001 boolean revert = false;
1002 if (header.id == R.id.account_add) {
1003 revert = true;
1004 }
1005
1006 super.onHeaderClick(header, position);
1007
1008 if (revert && mLastHeader != null) {
1009 highlightHeader((int) mLastHeader.id);
1010 } else {
1011 mLastHeader = header;
1012 }
1013 }
1014
1015 @Override
Amith Yamasania4379d62011-07-22 10:34:58 -07001016 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
1017 // Override the fragment title for Wallpaper settings
Amith Yamasanidfb65432011-11-29 16:38:14 -08001018 int titleRes = pref.getTitleRes();
Amith Yamasania4379d62011-07-22 10:34:58 -07001019 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
Amith Yamasanidfb65432011-11-29 16:38:14 -08001020 titleRes = R.string.wallpaper_settings_fragment_title;
Amith Yamasani8666b9e2012-09-27 14:50:13 -07001021 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
1022 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
Amith Yamasanib0171712013-04-28 22:13:22 -07001023 if (UserManager.get(this).isLinkedUser()) {
1024 titleRes = R.string.profile_info_settings_title;
1025 } else {
1026 titleRes = R.string.user_info_settings_title;
1027 }
Amith Yamasania4379d62011-07-22 10:34:58 -07001028 }
Jean Chalard7dabe452012-05-10 18:08:07 +09001029 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
1030 null, 0);
Amith Yamasania4379d62011-07-22 10:34:58 -07001031 return true;
1032 }
1033
Maggie Benthall0c5a4012013-03-14 17:41:27 -04001034 @Override
Amith Yamasani3d384f42012-05-11 15:22:04 -07001035 public boolean shouldUpRecreateTask(Intent targetIntent) {
1036 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1037 }
1038
Amith Yamasania4379d62011-07-22 10:34:58 -07001039 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -07001040 public void setListAdapter(ListAdapter adapter) {
Amith Yamasaniea7b28c2012-06-20 13:51:40 -07001041 if (adapter == null) {
1042 super.setListAdapter(null);
1043 } else {
Maggie Benthall9d6c40e2013-09-05 15:33:58 -04001044 DevicePolicyManager dpm =
1045 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1046 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
Gilles Debunnee78c1872011-06-20 15:00:07 -07001047 }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001048 }
1049
Amith Yamasani56821db2012-06-05 13:20:45 -07001050 @Override
1051 public void onAccountsUpdated(Account[] accounts) {
Jeff Sharkey9ff79c12012-10-03 16:48:17 -07001052 // TODO: watch for package upgrades to invalidate cache; see 7206643
1053 mAuthenticatorHelper.updateAuthDescriptions(this);
Amith Yamasani56821db2012-06-05 13:20:45 -07001054 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1055 invalidateHeaders();
1056 }
1057
Christopher Tatee0d934f2013-10-01 12:32:27 -07001058 public static void requestHomeNotice() {
1059 sShowNoHomeNotice = true;
1060 }
1061
Amith Yamasani5203bdf2010-11-04 09:59:44 -07001062 /*
1063 * Settings subclasses for launching independently.
1064 */
Gilles Debunnee78c1872011-06-20 15:00:07 -07001065 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1066 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1067 public static class TetherSettingsActivity extends Settings { /* empty */ }
1068 public static class VpnSettingsActivity extends Settings { /* empty */ }
1069 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1070 public static class StorageSettingsActivity extends Settings { /* empty */ }
1071 public static class WifiSettingsActivity extends Settings { /* empty */ }
repo syncb98463f2011-06-30 10:58:43 -07001072 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001073 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
Jeff Browne46c5f32012-04-05 11:42:18 -07001074 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001075 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
satoke077d2b2011-07-25 09:38:11 +09001076 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001077 public static class LocalePickerActivity extends Settings { /* empty */ }
1078 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1079 public static class SoundSettingsActivity extends Settings { /* empty */ }
1080 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1081 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1082 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1083 public static class ManageApplicationsActivity extends Settings { /* empty */ }
Dianne Hackborna522a8e2013-01-15 19:02:05 -08001084 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001085 public static class StorageUseActivity extends Settings { /* empty */ }
1086 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1087 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
Alan Viverette9b625f02013-09-11 13:03:14 -07001088 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001089 public static class SecuritySettingsActivity extends Settings { /* empty */ }
Gilles Debunnea6a8a142011-06-09 11:56:17 -07001090 public static class LocationSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001091 public static class PrivacySettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001092 public static class RunningServicesActivity extends Settings { /* empty */ }
1093 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1094 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1095 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1096 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1097 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1098 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1099 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001100 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001101 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
Jeff Hamilton3d670de2011-09-21 16:44:36 -05001102 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
Jeff Brown9e143f52012-09-19 20:46:07 -07001103 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
Daniel Sandler7dee75c2012-11-27 22:41:30 -05001104 public static class DreamSettingsActivity extends Settings { /* empty */ }
Daniel Sandler328e2d22013-01-17 13:14:02 -05001105 public static class NotificationStationActivity extends Settings { /* empty */ }
Amith Yamasani233592b2013-03-01 17:05:23 -08001106 public static class UserSettingsActivity extends Settings { /* empty */ }
Daniel Sandler79b9bfe2013-04-04 14:30:04 -04001107 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
Amith Yamasania677ee22013-07-26 13:38:41 -07001108 public static class UsbSettingsActivity extends Settings { /* empty */ }
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -04001109 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
Martijn Coenen53083ab2013-08-09 13:37:38 -07001110 public static class PaymentSettingsActivity extends Settings { /* empty */ }
Svetoslav Ganov74648ac2013-09-04 23:52:28 -07001111 public static class PrintSettingsActivity extends Settings { /* empty */ }
Svetoslavd9f156d2013-10-09 21:59:04 -07001112 public static class PrintJobSettingsActivity extends Settings { /* empty */ }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001113}