blob: 2b8bde29e506fd9586314204686a8e3194a8f074 [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;
Svetoslav2d531652013-09-03 21:32:40 -070084import com.android.settings.print.PrintServiceSettingsFragment;
85import com.android.settings.print.PrintSettingsFragment;
Amith Yamasania677ee22013-07-26 13:38:41 -070086import com.android.settings.tts.TextToSpeechSettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070087import com.android.settings.users.UserSettings;
88import com.android.settings.vpn2.VpnSettings;
Jeff Sharkey0c3634c2013-02-20 12:35:16 -080089import com.android.settings.wfd.WifiDisplaySettings;
Amith Yamasania677ee22013-07-26 13:38:41 -070090import com.android.settings.wifi.AdvancedWifiSettings;
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -080091import com.android.settings.wifi.WifiEnabler;
92import com.android.settings.wifi.WifiSettings;
93import com.android.settings.wifi.p2p.WifiP2pSettings;
94
Gilles Debunnee78c1872011-06-20 15:00:07 -070095import java.util.ArrayList;
Amith Yamasanid1ab8282012-05-18 09:50:08 -070096import java.util.Collections;
97import java.util.Comparator;
Amith Yamasani5203bdf2010-11-04 09:59:44 -070098import java.util.HashMap;
Amith Yamasani02cf71a2010-09-21 15:48:52 -070099import java.util.List;
Amith Yamasanid7993472010-08-18 13:59:28 -0700100
101/**
102 * Top-level settings activity to handle single pane and double pane UI layout.
103 */
Amith Yamasani56821db2012-06-05 13:20:45 -0700104public class Settings extends PreferenceActivity
105 implements ButtonBarHandler, OnAccountsUpdateListener {
Amith Yamasanid7993472010-08-18 13:59:28 -0700106
Gilles Debunnee78c1872011-06-20 15:00:07 -0700107 private static final String LOG_TAG = "Settings";
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700108
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700109 private static final String META_DATA_KEY_HEADER_ID =
Gilles Debunnee78c1872011-06-20 15:00:07 -0700110 "com.android.settings.TOP_LEVEL_HEADER_ID";
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700111 private static final String META_DATA_KEY_FRAGMENT_CLASS =
Gilles Debunnee78c1872011-06-20 15:00:07 -0700112 "com.android.settings.FRAGMENT_CLASS";
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800113 private static final String META_DATA_KEY_PARENT_TITLE =
114 "com.android.settings.PARENT_FRAGMENT_TITLE";
115 private static final String META_DATA_KEY_PARENT_FRAGMENT_CLASS =
116 "com.android.settings.PARENT_FRAGMENT_CLASS";
117
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800118 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700119
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800120 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
121 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700122
Christopher Tatee0d934f2013-10-01 12:32:27 -0700123 static final int DIALOG_ONLY_ONE_HOME = 1;
124
125 private static boolean sShowNoHomeNotice = false;
126
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700127 private String mFragmentClass;
128 private int mTopLevelHeaderId;
Amith Yamasani3965ae62010-11-15 14:45:19 -0800129 private Header mFirstHeader;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800130 private Header mCurrentHeader;
131 private Header mParentHeader;
132 private boolean mInLocalHeaderSwitch;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700133
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700134 // Show only these settings for restricted users
135 private int[] SETTINGS_FOR_RESTRICTED = {
Amith Yamasani32630392012-08-10 19:31:39 -0700136 R.id.wireless_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700137 R.id.wifi_settings,
138 R.id.bluetooth_settings,
Jeff Sharkey38305fb2012-09-14 16:26:51 -0700139 R.id.data_usage_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700140 R.id.wireless_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700141 R.id.device_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700142 R.id.sound_settings,
143 R.id.display_settings,
Jeff Sharkey90c8b202012-08-30 15:20:10 -0700144 R.id.storage_settings,
Dianne Hackborn271c8b02012-08-20 17:24:39 -0700145 R.id.application_settings,
Dianne Hackborn52e56a22012-09-12 17:02:23 -0700146 R.id.battery_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700147 R.id.personal_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700148 R.id.location_settings,
Amith Yamasani3deeeb72012-04-05 14:44:48 -0700149 R.id.security_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700150 R.id.language_settings,
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700151 R.id.user_settings,
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700152 R.id.account_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700153 R.id.account_add,
154 R.id.system_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700155 R.id.date_time_settings,
Jeff Brown9e143f52012-09-19 20:46:07 -0700156 R.id.about_settings,
Svetoslav Ganovd4c7b492013-06-11 21:15:11 -0700157 R.id.accessibility_settings,
Martijn Coenen26515da2013-08-01 18:13:33 -0700158 R.id.print_settings,
Christopher Tatee238a472013-08-27 14:21:46 -0700159 R.id.nfc_payment_settings,
160 R.id.home_settings
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700161 };
162
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700163 private SharedPreferences mDevelopmentPreferences;
164 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
165
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700166 // TODO: Update Call Settings based on airplane mode state.
Amith Yamasanib61cf512010-09-12 08:17:50 -0700167
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700168 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
169
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700170 private AuthenticatorHelper mAuthenticatorHelper;
171 private Header mLastHeader;
Amith Yamasani86708a82012-06-06 20:23:08 -0700172 private boolean mListeningToAccountUpdates;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700173
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900174 private boolean mBatteryPresent = true;
175 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
176
177 @Override
178 public void onReceive(Context context, Intent intent) {
179 String action = intent.getAction();
180 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
181 boolean batteryPresent = Utils.isBatteryPresent(intent);
182
183 if (mBatteryPresent != batteryPresent) {
184 mBatteryPresent = batteryPresent;
185 invalidateHeaders();
186 }
187 }
188 }
189 };
190
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700191 @Override
192 protected void onCreate(Bundle savedInstanceState) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800193 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
194 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700195 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700196
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700197 mAuthenticatorHelper = new AuthenticatorHelper();
198 mAuthenticatorHelper.updateAuthDescriptions(this);
199 mAuthenticatorHelper.onAccountsUpdated(this, null);
200
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700201 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
202 Context.MODE_PRIVATE);
203
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700204 getMetaData();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800205 mInLocalHeaderSwitch = true;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700206 super.onCreate(savedInstanceState);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800207 mInLocalHeaderSwitch = false;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700208
Gilles Debunne3661b622011-06-27 11:19:16 -0700209 if (!onIsHidingHeaders() && onIsMultiPane()) {
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700210 highlightHeader(mTopLevelHeaderId);
Amith Yamasani72aa19d2010-12-09 06:07:12 -0800211 // Force the title so that it doesn't get overridden by a direct launch of
212 // a specific settings screen.
213 setTitle(R.string.settings_label);
214 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800215
216 // Retrieve any saved state
217 if (savedInstanceState != null) {
218 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
219 mParentHeader = savedInstanceState.getParcelable(SAVE_KEY_PARENT_HEADER);
220 }
221
222 // If the current header was saved, switch to it
223 if (savedInstanceState != null && mCurrentHeader != null) {
224 //switchToHeaderLocal(mCurrentHeader);
225 showBreadCrumbs(mCurrentHeader.title, null);
226 }
227
228 if (mParentHeader != null) {
229 setParentTitle(mParentHeader.title, null, new OnClickListener() {
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400230 @Override
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800231 public void onClick(View v) {
232 switchToParent(mParentHeader.fragment);
233 }
234 });
235 }
Gilles Debunnedc7101f2011-06-27 12:00:49 -0700236
Amith Yamasani3d384f42012-05-11 15:22:04 -0700237 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
238 if (onIsMultiPane()) {
239 getActionBar().setDisplayHomeAsUpEnabled(false);
240 getActionBar().setHomeButtonEnabled(false);
241 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800242 }
243
244 @Override
245 protected void onSaveInstanceState(Bundle outState) {
246 super.onSaveInstanceState(outState);
247
248 // Save the current fragment, if it is the same as originally launched
249 if (mCurrentHeader != null) {
250 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
251 }
252 if (mParentHeader != null) {
253 outState.putParcelable(SAVE_KEY_PARENT_HEADER, mParentHeader);
254 }
255 }
256
Gilles Debunnee78c1872011-06-20 15:00:07 -0700257 @Override
258 public void onResume() {
259 super.onResume();
260
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700261 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
262 @Override
263 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
264 invalidateHeaders();
265 }
266 };
267 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
268 mDevelopmentPreferencesListener);
269
Gilles Debunnee78c1872011-06-20 15:00:07 -0700270 ListAdapter listAdapter = getListAdapter();
271 if (listAdapter instanceof HeaderAdapter) {
272 ((HeaderAdapter) listAdapter).resume();
273 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700274 invalidateHeaders();
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900275
276 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Gilles Debunnee78c1872011-06-20 15:00:07 -0700277 }
278
279 @Override
280 public void onPause() {
281 super.onPause();
282
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900283 unregisterReceiver(mBatteryInfoReceiver);
284
Gilles Debunnee78c1872011-06-20 15:00:07 -0700285 ListAdapter listAdapter = getListAdapter();
286 if (listAdapter instanceof HeaderAdapter) {
287 ((HeaderAdapter) listAdapter).pause();
Amith Yamasani86708a82012-06-06 20:23:08 -0700288 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700289
290 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
291 mDevelopmentPreferencesListener);
292 mDevelopmentPreferencesListener = null;
Amith Yamasani86708a82012-06-06 20:23:08 -0700293 }
294
295 @Override
296 public void onDestroy() {
297 super.onDestroy();
298 if (mListeningToAccountUpdates) {
Amith Yamasani56821db2012-06-05 13:20:45 -0700299 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700300 }
301 }
302
Amith Yamasani56f51a82013-08-05 10:07:23 -0700303 @Override
304 public boolean onIsMultiPane() {
305 return false;
306 }
307
Amith Yamasania677ee22013-07-26 13:38:41 -0700308 private static final String[] ENTRY_FRAGMENTS = {
309 WirelessSettings.class.getName(),
310 WifiSettings.class.getName(),
311 AdvancedWifiSettings.class.getName(),
312 BluetoothSettings.class.getName(),
313 TetherSettings.class.getName(),
314 WifiP2pSettings.class.getName(),
315 VpnSettings.class.getName(),
316 DateTimeSettings.class.getName(),
317 LocalePicker.class.getName(),
318 InputMethodAndLanguageSettings.class.getName(),
319 SpellCheckersSettings.class.getName(),
320 UserDictionaryList.class.getName(),
321 UserDictionarySettings.class.getName(),
322 SoundSettings.class.getName(),
323 DisplaySettings.class.getName(),
324 DeviceInfoSettings.class.getName(),
325 ManageApplications.class.getName(),
Dianne Hackbornc6d658e2013-08-08 12:57:53 -0700326 ProcessStatsUi.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700327 NotificationStation.class.getName(),
328 AppOpsSummary.class.getName(),
329 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(),
Martijn Coenen53083ab2013-08-09 13:37:38 -0700350 TrustedCredentialsSettings.class.getName(),
Amith Yamasani3a14ae22013-08-30 15:43:44 -0700351 PaymentSettings.class.getName(),
352 KeyboardLayoutPickerFragment.class.getName()
Amith Yamasania677ee22013-07-26 13:38:41 -0700353 };
354
355 @Override
356 protected boolean isValidFragment(String fragmentName) {
357 // Almost all fragments are wrapped in this,
358 // except for a few that have their own activities.
359 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
360 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
361 }
362 return false;
363 }
364
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800365 private void switchToHeaderLocal(Header header) {
366 mInLocalHeaderSwitch = true;
367 switchToHeader(header);
368 mInLocalHeaderSwitch = false;
369 }
370
371 @Override
372 public void switchToHeader(Header header) {
373 if (!mInLocalHeaderSwitch) {
374 mCurrentHeader = null;
375 mParentHeader = null;
376 }
377 super.switchToHeader(header);
378 }
379
380 /**
381 * Switch to parent fragment and store the grand parent's info
Jake Hamby2748fc22011-01-12 15:06:28 -0800382 * @param className name of the activity wrapper for the parent fragment.
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800383 */
384 private void switchToParent(String className) {
385 final ComponentName cn = new ComponentName(this, className);
386 try {
387 final PackageManager pm = getPackageManager();
388 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
389
390 if (parentInfo != null && parentInfo.metaData != null) {
391 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
392 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
393 Header parentHeader = new Header();
394 parentHeader.fragment = fragmentClass;
395 parentHeader.title = fragmentTitle;
396 mCurrentHeader = parentHeader;
397
398 switchToHeaderLocal(parentHeader);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700399 highlightHeader(mTopLevelHeaderId);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800400
401 mParentHeader = new Header();
402 mParentHeader.fragment
403 = parentInfo.metaData.getString(META_DATA_KEY_PARENT_FRAGMENT_CLASS);
404 mParentHeader.title = parentInfo.metaData.getString(META_DATA_KEY_PARENT_TITLE);
405 }
406 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700407 Log.w(LOG_TAG, "Could not find parent activity : " + className);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800408 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700409 }
410
Amith Yamasani3965ae62010-11-15 14:45:19 -0800411 @Override
412 public void onNewIntent(Intent intent) {
413 super.onNewIntent(intent);
414
415 // If it is not launched from history, then reset to top-level
Amith Yamasanief617232012-10-09 16:57:25 -0700416 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
417 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
418 switchToHeaderLocal(mFirstHeader);
419 }
420 getListView().setSelectionFromTop(0, 0);
Amith Yamasani3965ae62010-11-15 14:45:19 -0800421 }
422 }
423
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700424 private void highlightHeader(int id) {
425 if (id != 0) {
426 Integer index = mHeaderIndexMap.get(id);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700427 if (index != null) {
428 getListView().setItemChecked(index, true);
Amith Yamasanief617232012-10-09 16:57:25 -0700429 if (isMultiPane()) {
430 getListView().smoothScrollToPosition(index);
431 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700432 }
433 }
434 }
435
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700436 @Override
437 public Intent getIntent() {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700438 Intent superIntent = super.getIntent();
439 String startingFragment = getStartingFragmentClass(superIntent);
Gilles Debunne3661b622011-06-27 11:19:16 -0700440 // This is called from super.onCreate, isMultiPane() is not yet reliable
441 // Do not use onIsHidingHeaders either, which relies itself on this method
442 if (startingFragment != null && !onIsMultiPane()) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700443 Intent modIntent = new Intent(superIntent);
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700444 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700445 Bundle args = superIntent.getExtras();
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700446 if (args != null) {
447 args = new Bundle(args);
448 } else {
449 args = new Bundle();
450 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700451 args.putParcelable("intent", superIntent);
452 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700453 return modIntent;
454 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700455 return superIntent;
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700456 }
457
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700458 /**
Amith Yamasani379d9b02010-09-27 12:03:59 -0700459 * Checks if the component name in the intent is different from the Settings class and
460 * returns the class name to load as a fragment.
461 */
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700462 protected String getStartingFragmentClass(Intent intent) {
463 if (mFragmentClass != null) return mFragmentClass;
464
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700465 String intentClass = intent.getComponent().getClassName();
Amith Yamasani379d9b02010-09-27 12:03:59 -0700466 if (intentClass.equals(getClass().getName())) return null;
467
Dianne Hackbornee293792010-11-01 12:32:33 -0700468 if ("com.android.settings.ManageApplications".equals(intentClass)
469 || "com.android.settings.RunningServices".equals(intentClass)
470 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700471 // Old names of manage apps.
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700472 intentClass = com.android.settings.applications.ManageApplications.class.getName();
473 }
474
Amith Yamasani379d9b02010-09-27 12:03:59 -0700475 return intentClass;
476 }
477
478 /**
479 * Override initial header when an activity-alias is causing Settings to be launched
480 * for a specific fragment encoded in the android:name parameter.
481 */
482 @Override
483 public Header onGetInitialHeader() {
484 String fragmentClass = getStartingFragmentClass(super.getIntent());
485 if (fragmentClass != null) {
486 Header header = new Header();
487 header.fragment = fragmentClass;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800488 header.title = getTitle();
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700489 header.fragmentArguments = getIntent().getExtras();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800490 mCurrentHeader = header;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700491 return header;
492 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700493
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700494 return mFirstHeader;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700495 }
496
Dianne Hackbornb7258182011-03-15 16:23:55 -0700497 @Override
Dianne Hackborn48147dc2011-03-18 12:29:41 -0700498 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
499 int titleRes, int shortTitleRes) {
500 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
501 titleRes, shortTitleRes);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700502
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800503 // Some fragments want split ActionBar; these should stay in sync with
504 // uiOptions for fragments also defined as activities in manifest.
505 if (WifiSettings.class.getName().equals(fragmentName) ||
506 WifiP2pSettings.class.getName().equals(fragmentName) ||
Jeff Sharkey0c3634c2013-02-20 12:35:16 -0800507 WifiDisplaySettings.class.getName().equals(fragmentName) ||
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800508 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 */ }
Dianne Hackborna522a8e2013-01-15 19:02:05 -08001083 public static class AppOpsSummaryActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001084 public static class StorageUseActivity extends Settings { /* empty */ }
1085 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1086 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
Alan Viverette9b625f02013-09-11 13:03:14 -07001087 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001088 public static class SecuritySettingsActivity extends Settings { /* empty */ }
Gilles Debunnea6a8a142011-06-09 11:56:17 -07001089 public static class LocationSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001090 public static class PrivacySettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001091 public static class RunningServicesActivity extends Settings { /* empty */ }
1092 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1093 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1094 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1095 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1096 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1097 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1098 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001099 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001100 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
Jeff Hamilton3d670de2011-09-21 16:44:36 -05001101 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
Jeff Brown9e143f52012-09-19 20:46:07 -07001102 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
Daniel Sandler7dee75c2012-11-27 22:41:30 -05001103 public static class DreamSettingsActivity extends Settings { /* empty */ }
Daniel Sandler328e2d22013-01-17 13:14:02 -05001104 public static class NotificationStationActivity extends Settings { /* empty */ }
Amith Yamasani233592b2013-03-01 17:05:23 -08001105 public static class UserSettingsActivity extends Settings { /* empty */ }
Daniel Sandler79b9bfe2013-04-04 14:30:04 -04001106 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
Amith Yamasania677ee22013-07-26 13:38:41 -07001107 public static class UsbSettingsActivity extends Settings { /* empty */ }
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -04001108 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
Martijn Coenen53083ab2013-08-09 13:37:38 -07001109 public static class PaymentSettingsActivity extends Settings { /* empty */ }
Svetoslav Ganov74648ac2013-09-04 23:52:28 -07001110 public static class PrintSettingsActivity extends Settings { /* empty */ }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001111}