blob: 663d27804ad5cf961423be05f3ed7a2a3dd59cb4 [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(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700329 LocationSettings.class.getName(),
330 SecuritySettings.class.getName(),
331 PrivacySettings.class.getName(),
332 DeviceAdminSettings.class.getName(),
333 AccessibilitySettings.class.getName(),
Alan Viverette9b625f02013-09-11 13:03:14 -0700334 ToggleCaptioningPreferenceFragment.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700335 TextToSpeechSettings.class.getName(),
336 Memory.class.getName(),
337 DevelopmentSettings.class.getName(),
338 UsbSettings.class.getName(),
339 AndroidBeam.class.getName(),
340 WifiDisplaySettings.class.getName(),
341 PowerUsageSummary.class.getName(),
342 AccountSyncSettings.class.getName(),
343 CryptKeeperSettings.class.getName(),
344 DataUsageSummary.class.getName(),
345 DreamSettings.class.getName(),
346 UserSettings.class.getName(),
347 NotificationAccessSettings.class.getName(),
Amith Yamasaniee226f92013-08-05 11:02:16 -0700348 ManageAccountsSettings.class.getName(),
Svetoslav2d531652013-09-03 21:32:40 -0700349 PrintSettingsFragment.class.getName(),
Svetoslavd9f156d2013-10-09 21:59:04 -0700350 PrintJobSettingsFragment.class.getName(),
Martijn Coenen53083ab2013-08-09 13:37:38 -0700351 TrustedCredentialsSettings.class.getName(),
Amith Yamasani3a14ae22013-08-30 15:43:44 -0700352 PaymentSettings.class.getName(),
353 KeyboardLayoutPickerFragment.class.getName()
Amith Yamasania677ee22013-07-26 13:38:41 -0700354 };
355
356 @Override
357 protected boolean isValidFragment(String fragmentName) {
358 // Almost all fragments are wrapped in this,
359 // except for a few that have their own activities.
360 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
361 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
362 }
363 return false;
364 }
365
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800366 private void switchToHeaderLocal(Header header) {
367 mInLocalHeaderSwitch = true;
368 switchToHeader(header);
369 mInLocalHeaderSwitch = false;
370 }
371
372 @Override
373 public void switchToHeader(Header header) {
374 if (!mInLocalHeaderSwitch) {
375 mCurrentHeader = null;
376 mParentHeader = null;
377 }
378 super.switchToHeader(header);
379 }
380
381 /**
382 * Switch to parent fragment and store the grand parent's info
Jake Hamby2748fc22011-01-12 15:06:28 -0800383 * @param className name of the activity wrapper for the parent fragment.
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800384 */
385 private void switchToParent(String className) {
386 final ComponentName cn = new ComponentName(this, className);
387 try {
388 final PackageManager pm = getPackageManager();
389 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
390
391 if (parentInfo != null && parentInfo.metaData != null) {
392 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
393 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
394 Header parentHeader = new Header();
395 parentHeader.fragment = fragmentClass;
396 parentHeader.title = fragmentTitle;
397 mCurrentHeader = parentHeader;
398
399 switchToHeaderLocal(parentHeader);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700400 highlightHeader(mTopLevelHeaderId);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800401
402 mParentHeader = new Header();
403 mParentHeader.fragment
404 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
405 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
406 }
407 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700408 Log.w(LOG_TAG, "Could not find parent activity : " + className);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800409 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700410 }
411
Amith Yamasani3965ae62010-11-15 14:45:19 -0800412 @Override
413 public void onNewIntent(Intent intent) {
414 super.onNewIntent(intent);
415
416 // If it is not launched from history, then reset to top-level
Amith Yamasanief617232012-10-09 16:57:25 -0700417 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
418 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
419 switchToHeaderLocal(mFirstHeader);
420 }
421 getListView().setSelectionFromTop(0, 0);
Amith Yamasani3965ae62010-11-15 14:45:19 -0800422 }
423 }
424
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700425 private void highlightHeader(int id) {
426 if (id != 0) {
427 Integer index = mHeaderIndexMap.get(id);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700428 if (index != null) {
429 getListView().setItemChecked(index, true);
Amith Yamasanief617232012-10-09 16:57:25 -0700430 if (isMultiPane()) {
431 getListView().smoothScrollToPosition(index);
432 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700433 }
434 }
435 }
436
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700437 @Override
438 public Intent getIntent() {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700439 Intent superIntent = super.getIntent();
440 String startingFragment = getStartingFragmentClass(superIntent);
Gilles Debunne3661b622011-06-27 11:19:16 -0700441 // This is called from super.onCreate, isMultiPane() is not yet reliable
442 // Do not use onIsHidingHeaders either, which relies itself on this method
443 if (startingFragment != null && !onIsMultiPane()) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700444 Intent modIntent = new Intent(superIntent);
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700445 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700446 Bundle args = superIntent.getExtras();
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700447 if (args != null) {
448 args = new Bundle(args);
449 } else {
450 args = new Bundle();
451 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700452 args.putParcelable("intent", superIntent);
453 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700454 return modIntent;
455 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700456 return superIntent;
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700457 }
458
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700459 /**
Amith Yamasani379d9b02010-09-27 12:03:59 -0700460 * Checks if the component name in the intent is different from the Settings class and
461 * returns the class name to load as a fragment.
462 */
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700463 protected String getStartingFragmentClass(Intent intent) {
464 if (mFragmentClass != null) return mFragmentClass;
465
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700466 String intentClass = intent.getComponent().getClassName();
Amith Yamasani379d9b02010-09-27 12:03:59 -0700467 if (intentClass.equals(getClass().getName())) return null;
468
Dianne Hackbornee293792010-11-01 12:32:33 -0700469 if ("com.android.settings.ManageApplications".equals(intentClass)
470 || "com.android.settings.RunningServices".equals(intentClass)
471 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700472 // Old names of manage apps.
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700473 intentClass = com.android.settings.applications.ManageApplications.class.getName();
474 }
475
Amith Yamasani379d9b02010-09-27 12:03:59 -0700476 return intentClass;
477 }
478
479 /**
480 * Override initial header when an activity-alias is causing Settings to be launched
481 * for a specific fragment encoded in the android:name parameter.
482 */
483 @Override
484 public Header onGetInitialHeader() {
485 String fragmentClass = getStartingFragmentClass(super.getIntent());
486 if (fragmentClass != null) {
487 Header header = new Header();
488 header.fragment = fragmentClass;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800489 header.title = getTitle();
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700490 header.fragmentArguments = getIntent().getExtras();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800491 mCurrentHeader = header;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700492 return header;
493 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700494
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700495 return mFirstHeader;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700496 }
497
Dianne Hackbornb7258182011-03-15 16:23:55 -0700498 @Override
Dianne Hackborn48147dc2011-03-18 12:29:41 -0700499 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
500 int titleRes, int shortTitleRes) {
501 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
502 titleRes, shortTitleRes);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700503
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800504 // Some fragments want split ActionBar; these should stay in sync with
505 // uiOptions for fragments also defined as activities in manifest.
506 if (WifiSettings.class.getName().equals(fragmentName) ||
507 WifiP2pSettings.class.getName().equals(fragmentName) ||
508 BluetoothSettings.class.getName().equals(fragmentName) ||
509 DreamSettings.class.getName().equals(fragmentName) ||
Lifu Tangd5fbbc42013-08-01 17:23:10 -0700510 LocationSettings.class.getName().equals(fragmentName) ||
Svetoslav2d531652013-09-03 21:32:40 -0700511 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
512 PrintSettingsFragment.class.getName().equals(fragmentName) ||
513 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800514 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700515 }
516
Dianne Hackbornb7258182011-03-15 16:23:55 -0700517 intent.setClass(this, SubSettings.class);
518 return intent;
519 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700520
Amith Yamasani379d9b02010-09-27 12:03:59 -0700521 /**
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700522 * Populate the activity with the top-level headers.
523 */
Amith Yamasanid7993472010-08-18 13:59:28 -0700524 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -0700525 public void onBuildHeaders(List<Header> headers) {
Amith Yamasani2972c012013-10-02 11:48:46 -0700526 if (!onIsHidingHeaders()) {
527 loadHeadersFromResource(R.xml.settings_headers, headers);
528 updateHeaderList(headers);
529 }
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700530 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700531
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700532 private void updateHeaderList(List<Header> target) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700533 final boolean showDev = mDevelopmentPreferences.getBoolean(
534 DevelopmentSettings.PREF_SHOW,
535 android.os.Build.TYPE.equals("eng"));
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700536 int i = 0;
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400537
538 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700539 mHeaderIndexMap.clear();
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700540 while (i < target.size()) {
541 Header header = target.get(i);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700542 // Ids are integers, so downcasting
543 int id = (int) header.id;
Chris Wren2bc32ae2012-09-24 14:23:46 -0400544 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700545 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
Gilles Debunne2454f492011-06-21 16:16:06 -0700546 } else if (id == R.id.wifi_settings) {
547 // Remove WiFi Settings if WiFi service is not available.
548 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700549 target.remove(i);
Gilles Debunne2454f492011-06-21 16:16:06 -0700550 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700551 } else if (id == R.id.bluetooth_settings) {
552 // Remove Bluetooth Settings if Bluetooth service is not available.
Gilles Debunne2454f492011-06-21 16:16:06 -0700553 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700554 target.remove(i);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700555 }
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700556 } else if (id == R.id.data_usage_settings) {
557 // Remove data usage when kernel module not enabled
558 final INetworkManagementService netManager = INetworkManagementService.Stub
559 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
560 try {
561 if (!netManager.isBandwidthControlEnabled()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700562 target.remove(i);
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700563 }
564 } catch (RemoteException e) {
565 // ignored
566 }
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900567 } else if (id == R.id.battery_settings) {
568 // Remove battery settings when battery is not available. (e.g. TV)
569
570 if (!mBatteryPresent) {
571 target.remove(i);
572 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700573 } else if (id == R.id.account_settings) {
574 int headerIndex = i + 1;
575 i = insertAccountsHeaders(target, headerIndex);
Christopher Tatee238a472013-08-27 14:21:46 -0700576 } else if (id == R.id.home_settings) {
Christopher Tate8243c9a2013-09-19 14:59:17 -0700577 if (!updateHomeSettingHeaders(header)) {
578 target.remove(i);
579 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700580 } else if (id == R.id.user_settings) {
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700581 if (!UserHandle.MU_ENABLED
Jeff Sharkey44202462012-09-19 13:13:45 -0700582 || !UserManager.supportsMultipleUsers()
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700583 || Utils.isMonkeyRunning()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700584 target.remove(i);
585 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700586 } else if (id == R.id.nfc_payment_settings) {
Martijn Coenen81ab8172013-09-30 12:07:02 -0700587 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
Martijn Coenen26515da2013-08-01 18:13:33 -0700588 target.remove(i);
Martijn Coenen81ab8172013-09-30 12:07:02 -0700589 } else {
590 // Only show if NFC is on and we have the HCE feature
591 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
592 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
593 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
594 target.remove(i);
595 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700596 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700597 } else if (id == R.id.development_settings) {
598 if (!showDev) {
599 target.remove(i);
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700600 }
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400601 } else if (id == R.id.account_add) {
602 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
603 target.remove(i);
604 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700605 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700606
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900607 if (i < target.size() && target.get(i) == header
Amith Yamasanib99f8642012-10-10 21:41:12 -0700608 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700609 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700610 target.remove(i);
Amith Yamasanid7993472010-08-18 13:59:28 -0700611 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700612
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700613 // Increment if the current one wasn't removed by the Utils code.
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900614 if (i < target.size() && target.get(i) == header) {
Amith Yamasani3965ae62010-11-15 14:45:19 -0800615 // Hold on to the first header, when we need to reset to the top-level
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700616 if (mFirstHeader == null &&
617 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
618 mFirstHeader = header;
619 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700620 mHeaderIndexMap.put(id, i);
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700621 i++;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700622 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700623 }
624 }
625
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700626 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
627 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
628 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
629 for (String accountType : accountTypes) {
630 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
Brian Muramatsuc28af522012-06-28 14:25:14 -0700631 if (label == null) {
632 continue;
633 }
634
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700635 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
636 boolean skipToAccount = accounts.length == 1
637 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700638 Header accHeader = new Header();
639 accHeader.title = label;
640 if (accHeader.extras == null) {
641 accHeader.extras = new Bundle();
642 }
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700643 if (skipToAccount) {
644 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
645 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
646 accHeader.fragment = AccountSyncSettings.class.getName();
647 accHeader.fragmentArguments = new Bundle();
648 // Need this for the icon
649 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
650 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
651 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
652 accounts[0]);
653 } else {
654 accHeader.breadCrumbTitle = label;
655 accHeader.breadCrumbShortTitle = label;
656 accHeader.fragment = ManageAccountsSettings.class.getName();
657 accHeader.fragmentArguments = new Bundle();
658 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
659 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
660 accountType);
661 if (!isMultiPane()) {
662 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
663 label.toString());
664 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700665 }
666 accountHeaders.add(accHeader);
Amith Yamasani2972c012013-10-02 11:48:46 -0700667 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700668 }
669
670 // Sort by label
671 Collections.sort(accountHeaders, new Comparator<Header>() {
672 @Override
673 public int compare(Header h1, Header h2) {
674 return h1.title.toString().compareTo(h2.title.toString());
675 }
676 });
677
678 for (Header header : accountHeaders) {
679 target.add(headerIndex++, header);
680 }
Amith Yamasani86708a82012-06-06 20:23:08 -0700681 if (!mListeningToAccountUpdates) {
682 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
683 mListeningToAccountUpdates = true;
684 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700685 return headerIndex;
686 }
687
Christopher Tate8243c9a2013-09-19 14:59:17 -0700688 private boolean updateHomeSettingHeaders(Header header) {
Christopher Tatedc7be102013-10-08 11:30:19 -0700689 // Once we decide to show Home settings, keep showing it forever
690 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
691 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
692 return true;
693 }
694
Christopher Tatee238a472013-08-27 14:21:46 -0700695 try {
Jeff Sharkeyd572a032013-09-23 17:41:16 -0700696 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
697 getPackageManager().getHomeActivities(homeApps);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700698 if (homeApps.size() < 2) {
699 // When there's only one available home app, omit this settings
Christopher Tatee0d934f2013-10-01 12:32:27 -0700700 // category entirely at the top level UI. If the user just
701 // uninstalled the penultimate home app candidiate, we also
702 // now tell them about why they aren't seeing 'Home' in the list.
703 if (sShowNoHomeNotice) {
704 sShowNoHomeNotice = false;
705 NoHomeDialogFragment.show(this);
706 }
Christopher Tate8243c9a2013-09-19 14:59:17 -0700707 return false;
Christopher Tatee0d934f2013-10-01 12:32:27 -0700708 } else {
709 // Okay, we're allowing the Home settings category. Tell it, when
710 // invoked via this front door, that we'll need to be told about the
711 // case when the user uninstalls all but one home app.
712 if (header.fragmentArguments == null) {
713 header.fragmentArguments = new Bundle();
714 }
715 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700716 }
Christopher Tatee238a472013-08-27 14:21:46 -0700717 } catch (Exception e) {
718 // Can't look up the home activity; bail on configuring the icon
719 Log.w(LOG_TAG, "Problem looking up home activity!", e);
720 }
Christopher Tatedc7be102013-10-08 11:30:19 -0700721
722 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
Christopher Tate8243c9a2013-09-19 14:59:17 -0700723 return true;
Christopher Tatee238a472013-08-27 14:21:46 -0700724 }
725
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700726 private void getMetaData() {
727 try {
728 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
729 PackageManager.GET_META_DATA);
730 if (ai == null || ai.metaData == null) return;
731 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
732 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700733
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800734 // Check if it has a parent specified and create a Header object
735 final int parentHeaderTitleRes = ai.metaData.getInt(META_DATA_KEY_PARENT_TITLE);
736 String parentFragmentClass = ai.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
737 if (parentFragmentClass != null) {
738 mParentHeader = new Header();
739 mParentHeader.fragment = parentFragmentClass;
740 if (parentHeaderTitleRes != 0) {
741 mParentHeader.title = getResources().getString(parentHeaderTitleRes);
742 }
743 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700744 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700745 // No recovery
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700746 }
747 }
748
Amith Yamasani9e3a4702011-01-11 09:09:26 -0800749 @Override
750 public boolean hasNextButton() {
751 return super.hasNextButton();
752 }
753
754 @Override
755 public Button getNextButton() {
756 return super.getNextButton();
757 }
758
Christopher Tatee0d934f2013-10-01 12:32:27 -0700759 public static class NoHomeDialogFragment extends DialogFragment {
760 public static void show(Activity parent) {
761 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
762 dialog.show(parent.getFragmentManager(), null);
763 }
764
765 @Override
766 public Dialog onCreateDialog(Bundle savedInstanceState) {
767 return new AlertDialog.Builder(getActivity())
768 .setMessage(R.string.only_one_home_message)
769 .setPositiveButton(android.R.string.ok, null)
770 .create();
771 }
772 }
773
Gilles Debunnee78c1872011-06-20 15:00:07 -0700774 private static class HeaderAdapter extends ArrayAdapter<Header> {
775 static final int HEADER_TYPE_CATEGORY = 0;
776 static final int HEADER_TYPE_NORMAL = 1;
777 static final int HEADER_TYPE_SWITCH = 2;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400778 static final int HEADER_TYPE_BUTTON = 3;
779 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700780
781 private final WifiEnabler mWifiEnabler;
782 private final BluetoothEnabler mBluetoothEnabler;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700783 private AuthenticatorHelper mAuthHelper;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400784 private DevicePolicyManager mDevicePolicyManager;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700785
786 private static class HeaderViewHolder {
787 ImageView icon;
788 TextView title;
789 TextView summary;
790 Switch switch_;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400791 ImageButton button_;
792 View divider_;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700793 }
794
795 private LayoutInflater mInflater;
796
797 static int getHeaderType(Header header) {
798 if (header.fragment == null && header.intent == null) {
799 return HEADER_TYPE_CATEGORY;
800 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
801 return HEADER_TYPE_SWITCH;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400802 } else if (header.id == R.id.security_settings) {
803 return HEADER_TYPE_BUTTON;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700804 } else {
805 return HEADER_TYPE_NORMAL;
806 }
807 }
808
809 @Override
810 public int getItemViewType(int position) {
811 Header header = getItem(position);
812 return getHeaderType(header);
813 }
814
815 @Override
816 public boolean areAllItemsEnabled() {
817 return false; // because of categories
818 }
819
820 @Override
821 public boolean isEnabled(int position) {
822 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
823 }
824
825 @Override
826 public int getViewTypeCount() {
827 return HEADER_TYPE_COUNT;
828 }
829
830 @Override
831 public boolean hasStableIds() {
832 return true;
833 }
834
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700835 public HeaderAdapter(Context context, List<Header> objects,
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400836 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700837 super(context, 0, objects);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700838
839 mAuthHelper = authenticatorHelper;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700840 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700841
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700842 // Temp Switches provided as placeholder until the adapter replaces these with actual
Gilles Debunnee78c1872011-06-20 15:00:07 -0700843 // Switches inflated from their layouts. Must be done before adapter is set in super
844 mWifiEnabler = new WifiEnabler(context, new Switch(context));
845 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400846 mDevicePolicyManager = dpm;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700847 }
848
849 @Override
850 public View getView(int position, View convertView, ViewGroup parent) {
851 HeaderViewHolder holder;
852 Header header = getItem(position);
853 int headerType = getHeaderType(header);
854 View view = null;
855
856 if (convertView == null) {
857 holder = new HeaderViewHolder();
858 switch (headerType) {
859 case HEADER_TYPE_CATEGORY:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700860 view = new TextView(getContext(), null,
861 android.R.attr.listSeparatorTextViewStyle);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700862 holder.title = (TextView) view;
863 break;
864
865 case HEADER_TYPE_SWITCH:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700866 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
867 false);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700868 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700869 holder.title = (TextView)
870 view.findViewById(com.android.internal.R.id.title);
871 holder.summary = (TextView)
872 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700873 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
874 break;
875
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400876 case HEADER_TYPE_BUTTON:
877 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
878 false);
879 holder.icon = (ImageView) view.findViewById(R.id.icon);
880 holder.title = (TextView)
881 view.findViewById(com.android.internal.R.id.title);
882 holder.summary = (TextView)
883 view.findViewById(com.android.internal.R.id.summary);
884 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
885 holder.divider_ = view.findViewById(R.id.divider);
886 break;
887
Gilles Debunnee78c1872011-06-20 15:00:07 -0700888 case HEADER_TYPE_NORMAL:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700889 view = mInflater.inflate(
Amith Yamasanic8a93172012-06-08 13:35:47 -0700890 R.layout.preference_header_item, parent,
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700891 false);
Amith Yamasanic8a93172012-06-08 13:35:47 -0700892 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700893 holder.title = (TextView)
894 view.findViewById(com.android.internal.R.id.title);
895 holder.summary = (TextView)
896 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700897 break;
898 }
899 view.setTag(holder);
900 } else {
901 view = convertView;
902 holder = (HeaderViewHolder) view.getTag();
903 }
904
905 // All view fields must be updated every time, because the view may be recycled
906 switch (headerType) {
907 case HEADER_TYPE_CATEGORY:
908 holder.title.setText(header.getTitle(getContext().getResources()));
909 break;
910
911 case HEADER_TYPE_SWITCH:
912 // Would need a different treatment if the main menu had more switches
913 if (header.id == R.id.wifi_settings) {
914 mWifiEnabler.setSwitch(holder.switch_);
915 } else {
916 mBluetoothEnabler.setSwitch(holder.switch_);
917 }
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400918 updateCommonHeaderView(header, holder);
919 break;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700920
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400921 case HEADER_TYPE_BUTTON:
922 if (header.id == R.id.security_settings) {
923 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
924 if (hasCert) {
925 holder.button_.setVisibility(View.VISIBLE);
926 holder.divider_.setVisibility(View.VISIBLE);
927 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
928 if (isManaged) {
Maggie Benthall273155c2013-09-18 16:53:23 -0400929 holder.button_.setImageResource(R.drawable.ic_settings_about);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400930 } else {
931 holder.button_.setImageResource(
932 android.R.drawable.stat_notify_error);
933 }
934 holder.button_.setOnClickListener(new OnClickListener() {
935 @Override
936 public void onClick(View v) {
937 Intent intent = new Intent(
938 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
939 getContext().startActivity(intent);
940 }
941 });
942 } else {
943 holder.button_.setVisibility(View.GONE);
944 holder.divider_.setVisibility(View.GONE);
945 }
946 }
947 updateCommonHeaderView(header, holder);
948 break;
949
Gilles Debunnee78c1872011-06-20 15:00:07 -0700950 case HEADER_TYPE_NORMAL:
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400951 updateCommonHeaderView(header, holder);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700952 break;
953 }
954
955 return view;
956 }
957
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400958 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
959 if (header.extras != null
960 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
961 String accType = header.extras.getString(
962 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
963 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
964 setHeaderIcon(holder, icon);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400965 } else {
966 holder.icon.setImageResource(header.iconRes);
967 }
968 holder.title.setText(header.getTitle(getContext().getResources()));
969 CharSequence summary = header.getSummary(getContext().getResources());
970 if (!TextUtils.isEmpty(summary)) {
971 holder.summary.setVisibility(View.VISIBLE);
972 holder.summary.setText(summary);
973 } else {
974 holder.summary.setVisibility(View.GONE);
975 }
976 }
977
Christopher Tatee238a472013-08-27 14:21:46 -0700978 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
979 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
980 lp.width = getContext().getResources().getDimensionPixelSize(
981 R.dimen.header_icon_width);
982 lp.height = lp.width;
983 holder.icon.setLayoutParams(lp);
984 holder.icon.setImageDrawable(icon);
985 }
986
Gilles Debunnee78c1872011-06-20 15:00:07 -0700987 public void resume() {
988 mWifiEnabler.resume();
989 mBluetoothEnabler.resume();
990 }
Brian Muramatsuc28af522012-06-28 14:25:14 -0700991
Gilles Debunnee78c1872011-06-20 15:00:07 -0700992 public void pause() {
993 mWifiEnabler.pause();
994 mBluetoothEnabler.pause();
995 }
996 }
997
998 @Override
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700999 public void onHeaderClick(Header header, int position) {
1000 boolean revert = false;
1001 if (header.id == R.id.account_add) {
1002 revert = true;
1003 }
1004
1005 super.onHeaderClick(header, position);
1006
1007 if (revert && mLastHeader != null) {
1008 highlightHeader((int) mLastHeader.id);
1009 } else {
1010 mLastHeader = header;
1011 }
1012 }
1013
1014 @Override
Amith Yamasania4379d62011-07-22 10:34:58 -07001015 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
1016 // Override the fragment title for Wallpaper settings
Amith Yamasanidfb65432011-11-29 16:38:14 -08001017 int titleRes = pref.getTitleRes();
Amith Yamasania4379d62011-07-22 10:34:58 -07001018 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
Amith Yamasanidfb65432011-11-29 16:38:14 -08001019 titleRes = R.string.wallpaper_settings_fragment_title;
Amith Yamasani8666b9e2012-09-27 14:50:13 -07001020 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
1021 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
Amith Yamasanib0171712013-04-28 22:13:22 -07001022 if (UserManager.get(this).isLinkedUser()) {
1023 titleRes = R.string.profile_info_settings_title;
1024 } else {
1025 titleRes = R.string.user_info_settings_title;
1026 }
Amith Yamasania4379d62011-07-22 10:34:58 -07001027 }
Jean Chalard7dabe452012-05-10 18:08:07 +09001028 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
1029 null, 0);
Amith Yamasania4379d62011-07-22 10:34:58 -07001030 return true;
1031 }
1032
Maggie Benthall0c5a4012013-03-14 17:41:27 -04001033 @Override
Amith Yamasani3d384f42012-05-11 15:22:04 -07001034 public boolean shouldUpRecreateTask(Intent targetIntent) {
1035 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1036 }
1037
Amith Yamasania4379d62011-07-22 10:34:58 -07001038 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -07001039 public void setListAdapter(ListAdapter adapter) {
Amith Yamasaniea7b28c2012-06-20 13:51:40 -07001040 if (adapter == null) {
1041 super.setListAdapter(null);
1042 } else {
Maggie Benthall9d6c40e2013-09-05 15:33:58 -04001043 DevicePolicyManager dpm =
1044 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1045 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
Gilles Debunnee78c1872011-06-20 15:00:07 -07001046 }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001047 }
1048
Amith Yamasani56821db2012-06-05 13:20:45 -07001049 @Override
1050 public void onAccountsUpdated(Account[] accounts) {
Jeff Sharkey9ff79c12012-10-03 16:48:17 -07001051 // TODO: watch for package upgrades to invalidate cache; see 7206643
1052 mAuthenticatorHelper.updateAuthDescriptions(this);
Amith Yamasani56821db2012-06-05 13:20:45 -07001053 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1054 invalidateHeaders();
1055 }
1056
Christopher Tatee0d934f2013-10-01 12:32:27 -07001057 public static void requestHomeNotice() {
1058 sShowNoHomeNotice = true;
1059 }
1060
Amith Yamasani5203bdf2010-11-04 09:59:44 -07001061 /*
1062 * Settings subclasses for launching independently.
1063 */
Gilles Debunnee78c1872011-06-20 15:00:07 -07001064 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1065 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1066 public static class TetherSettingsActivity extends Settings { /* empty */ }
1067 public static class VpnSettingsActivity extends Settings { /* empty */ }
1068 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1069 public static class StorageSettingsActivity extends Settings { /* empty */ }
1070 public static class WifiSettingsActivity extends Settings { /* empty */ }
repo syncb98463f2011-06-30 10:58:43 -07001071 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001072 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
Jeff Browne46c5f32012-04-05 11:42:18 -07001073 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001074 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
satoke077d2b2011-07-25 09:38:11 +09001075 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001076 public static class LocalePickerActivity extends Settings { /* empty */ }
1077 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1078 public static class SoundSettingsActivity extends Settings { /* empty */ }
1079 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1080 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1081 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1082 public static class ManageApplicationsActivity extends Settings { /* empty */ }
Amith Yamasania88bdd32013-11-25 11:10:00 -08001083 public static class AppOpsSummaryActivity extends Settings {
1084 @Override
1085 public boolean isValidFragment(String className) {
1086 if (AppOpsSummary.class.getName().equals(className)) {
1087 return true;
1088 }
1089 return super.isValidFragment(className);
1090 }
1091 }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001092 public static class StorageUseActivity extends Settings { /* empty */ }
1093 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1094 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
Alan Viverette9b625f02013-09-11 13:03:14 -07001095 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001096 public static class SecuritySettingsActivity extends Settings { /* empty */ }
Gilles Debunnea6a8a142011-06-09 11:56:17 -07001097 public static class LocationSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001098 public static class PrivacySettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001099 public static class RunningServicesActivity extends Settings { /* empty */ }
1100 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1101 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1102 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1103 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1104 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1105 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1106 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001107 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001108 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
Jeff Hamilton3d670de2011-09-21 16:44:36 -05001109 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
Jeff Brown9e143f52012-09-19 20:46:07 -07001110 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
Daniel Sandler7dee75c2012-11-27 22:41:30 -05001111 public static class DreamSettingsActivity extends Settings { /* empty */ }
Daniel Sandler328e2d22013-01-17 13:14:02 -05001112 public static class NotificationStationActivity extends Settings { /* empty */ }
Amith Yamasani233592b2013-03-01 17:05:23 -08001113 public static class UserSettingsActivity extends Settings { /* empty */ }
Daniel Sandler79b9bfe2013-04-04 14:30:04 -04001114 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
Amith Yamasania677ee22013-07-26 13:38:41 -07001115 public static class UsbSettingsActivity extends Settings { /* empty */ }
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -04001116 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
Martijn Coenen53083ab2013-08-09 13:37:38 -07001117 public static class PaymentSettingsActivity extends Settings { /* empty */ }
Svetoslav Ganov74648ac2013-09-04 23:52:28 -07001118 public static class PrintSettingsActivity extends Settings { /* empty */ }
Svetoslavd9f156d2013-10-09 21:59:04 -07001119 public static class PrintJobSettingsActivity extends Settings { /* empty */ }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001120}