blob: 271bf2d31bc1dfbcec8fbb871034ef96dcd8ffaa [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 Viverette4098dba2013-12-04 17:19:24 -080064import com.android.settings.accessibility.CaptionPropertiesFragment;
Alan Viverette341ff662013-07-18 17:49:33 -070065import com.android.settings.accessibility.ToggleAccessibilityServicePreferenceFragment;
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
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800115 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700116
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800117 private static final String SAVE_KEY_CURRENT_HEADER = "com.android.settings.CURRENT_HEADER";
118 private static final String SAVE_KEY_PARENT_HEADER = "com.android.settings.PARENT_HEADER";
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700119
Christopher Tatee0d934f2013-10-01 12:32:27 -0700120 static final int DIALOG_ONLY_ONE_HOME = 1;
121
122 private static boolean sShowNoHomeNotice = false;
123
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700124 private String mFragmentClass;
125 private int mTopLevelHeaderId;
Amith Yamasani3965ae62010-11-15 14:45:19 -0800126 private Header mFirstHeader;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800127 private Header mCurrentHeader;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800128 private boolean mInLocalHeaderSwitch;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700129
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700130 // Show only these settings for restricted users
131 private int[] SETTINGS_FOR_RESTRICTED = {
Amith Yamasani32630392012-08-10 19:31:39 -0700132 R.id.wireless_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700133 R.id.wifi_settings,
134 R.id.bluetooth_settings,
Jeff Sharkey38305fb2012-09-14 16:26:51 -0700135 R.id.data_usage_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700136 R.id.wireless_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700137 R.id.device_section,
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700138 R.id.sound_settings,
139 R.id.display_settings,
Jeff Sharkey90c8b202012-08-30 15:20:10 -0700140 R.id.storage_settings,
Dianne Hackborn271c8b02012-08-20 17:24:39 -0700141 R.id.application_settings,
Dianne Hackborn52e56a22012-09-12 17:02:23 -0700142 R.id.battery_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700143 R.id.personal_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700144 R.id.location_settings,
Amith Yamasani3deeeb72012-04-05 14:44:48 -0700145 R.id.security_settings,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700146 R.id.language_settings,
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700147 R.id.user_settings,
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700148 R.id.account_settings,
Amith Yamasani32630392012-08-10 19:31:39 -0700149 R.id.account_add,
150 R.id.system_section,
Amith Yamasani9627a8e2012-09-23 12:54:14 -0700151 R.id.date_time_settings,
Jeff Brown9e143f52012-09-19 20:46:07 -0700152 R.id.about_settings,
Svetoslav Ganovd4c7b492013-06-11 21:15:11 -0700153 R.id.accessibility_settings,
Martijn Coenen26515da2013-08-01 18:13:33 -0700154 R.id.print_settings,
Christopher Tatee238a472013-08-27 14:21:46 -0700155 R.id.nfc_payment_settings,
156 R.id.home_settings
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700157 };
158
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700159 private SharedPreferences mDevelopmentPreferences;
160 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
161
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700162 // TODO: Update Call Settings based on airplane mode state.
Amith Yamasanib61cf512010-09-12 08:17:50 -0700163
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700164 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
165
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700166 private AuthenticatorHelper mAuthenticatorHelper;
167 private Header mLastHeader;
Amith Yamasani86708a82012-06-06 20:23:08 -0700168 private boolean mListeningToAccountUpdates;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700169
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900170 private boolean mBatteryPresent = true;
171 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
172
173 @Override
174 public void onReceive(Context context, Intent intent) {
175 String action = intent.getAction();
176 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
177 boolean batteryPresent = Utils.isBatteryPresent(intent);
178
179 if (mBatteryPresent != batteryPresent) {
180 mBatteryPresent = batteryPresent;
181 invalidateHeaders();
182 }
183 }
184 }
185 };
186
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700187 @Override
188 protected void onCreate(Bundle savedInstanceState) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800189 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
190 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
Jeff Sharkey54d0af52011-08-11 18:26:57 -0700191 }
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700192
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700193 mAuthenticatorHelper = new AuthenticatorHelper();
194 mAuthenticatorHelper.updateAuthDescriptions(this);
195 mAuthenticatorHelper.onAccountsUpdated(this, null);
196
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700197 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
198 Context.MODE_PRIVATE);
199
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700200 getMetaData();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800201 mInLocalHeaderSwitch = true;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700202 super.onCreate(savedInstanceState);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800203 mInLocalHeaderSwitch = false;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700204
Gilles Debunne3661b622011-06-27 11:19:16 -0700205 if (!onIsHidingHeaders() && onIsMultiPane()) {
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700206 highlightHeader(mTopLevelHeaderId);
Amith Yamasani72aa19d2010-12-09 06:07:12 -0800207 // Force the title so that it doesn't get overridden by a direct launch of
208 // a specific settings screen.
209 setTitle(R.string.settings_label);
210 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800211
212 // Retrieve any saved state
213 if (savedInstanceState != null) {
214 mCurrentHeader = savedInstanceState.getParcelable(SAVE_KEY_CURRENT_HEADER);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800215 }
216
217 // If the current header was saved, switch to it
218 if (savedInstanceState != null && mCurrentHeader != null) {
219 //switchToHeaderLocal(mCurrentHeader);
220 showBreadCrumbs(mCurrentHeader.title, null);
221 }
222
Amith Yamasani3d384f42012-05-11 15:22:04 -0700223 // Override up navigation for multi-pane, since we handle it in the fragment breadcrumbs
224 if (onIsMultiPane()) {
225 getActionBar().setDisplayHomeAsUpEnabled(false);
226 getActionBar().setHomeButtonEnabled(false);
227 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800228 }
229
230 @Override
231 protected void onSaveInstanceState(Bundle outState) {
232 super.onSaveInstanceState(outState);
233
234 // Save the current fragment, if it is the same as originally launched
235 if (mCurrentHeader != null) {
236 outState.putParcelable(SAVE_KEY_CURRENT_HEADER, mCurrentHeader);
237 }
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800238 }
239
Gilles Debunnee78c1872011-06-20 15:00:07 -0700240 @Override
241 public void onResume() {
242 super.onResume();
243
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700244 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
245 @Override
246 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
247 invalidateHeaders();
248 }
249 };
250 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
251 mDevelopmentPreferencesListener);
252
Gilles Debunnee78c1872011-06-20 15:00:07 -0700253 ListAdapter listAdapter = getListAdapter();
254 if (listAdapter instanceof HeaderAdapter) {
255 ((HeaderAdapter) listAdapter).resume();
256 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700257 invalidateHeaders();
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900258
259 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Gilles Debunnee78c1872011-06-20 15:00:07 -0700260 }
261
262 @Override
263 public void onPause() {
264 super.onPause();
265
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900266 unregisterReceiver(mBatteryInfoReceiver);
267
Gilles Debunnee78c1872011-06-20 15:00:07 -0700268 ListAdapter listAdapter = getListAdapter();
269 if (listAdapter instanceof HeaderAdapter) {
270 ((HeaderAdapter) listAdapter).pause();
Amith Yamasani86708a82012-06-06 20:23:08 -0700271 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700272
273 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
274 mDevelopmentPreferencesListener);
275 mDevelopmentPreferencesListener = null;
Amith Yamasani86708a82012-06-06 20:23:08 -0700276 }
277
278 @Override
279 public void onDestroy() {
280 super.onDestroy();
281 if (mListeningToAccountUpdates) {
Amith Yamasani56821db2012-06-05 13:20:45 -0700282 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700283 }
284 }
285
Amith Yamasani56f51a82013-08-05 10:07:23 -0700286 @Override
287 public boolean onIsMultiPane() {
288 return false;
289 }
290
Amith Yamasania677ee22013-07-26 13:38:41 -0700291 private static final String[] ENTRY_FRAGMENTS = {
292 WirelessSettings.class.getName(),
293 WifiSettings.class.getName(),
294 AdvancedWifiSettings.class.getName(),
295 BluetoothSettings.class.getName(),
296 TetherSettings.class.getName(),
297 WifiP2pSettings.class.getName(),
298 VpnSettings.class.getName(),
299 DateTimeSettings.class.getName(),
300 LocalePicker.class.getName(),
301 InputMethodAndLanguageSettings.class.getName(),
302 SpellCheckersSettings.class.getName(),
303 UserDictionaryList.class.getName(),
304 UserDictionarySettings.class.getName(),
305 SoundSettings.class.getName(),
306 DisplaySettings.class.getName(),
307 DeviceInfoSettings.class.getName(),
308 ManageApplications.class.getName(),
Dianne Hackbornc6d658e2013-08-08 12:57:53 -0700309 ProcessStatsUi.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700310 NotificationStation.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700311 LocationSettings.class.getName(),
312 SecuritySettings.class.getName(),
313 PrivacySettings.class.getName(),
314 DeviceAdminSettings.class.getName(),
315 AccessibilitySettings.class.getName(),
Alan Viverette4098dba2013-12-04 17:19:24 -0800316 CaptionPropertiesFragment.class.getName(),
Alan Viverettefc0ab212013-10-02 16:36:33 -0700317 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
318 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
319 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
Amith Yamasania677ee22013-07-26 13:38:41 -0700320 TextToSpeechSettings.class.getName(),
321 Memory.class.getName(),
322 DevelopmentSettings.class.getName(),
323 UsbSettings.class.getName(),
324 AndroidBeam.class.getName(),
325 WifiDisplaySettings.class.getName(),
326 PowerUsageSummary.class.getName(),
327 AccountSyncSettings.class.getName(),
328 CryptKeeperSettings.class.getName(),
329 DataUsageSummary.class.getName(),
330 DreamSettings.class.getName(),
331 UserSettings.class.getName(),
332 NotificationAccessSettings.class.getName(),
Amith Yamasaniee226f92013-08-05 11:02:16 -0700333 ManageAccountsSettings.class.getName(),
Svetoslav2d531652013-09-03 21:32:40 -0700334 PrintSettingsFragment.class.getName(),
Svetoslavd9f156d2013-10-09 21:59:04 -0700335 PrintJobSettingsFragment.class.getName(),
Martijn Coenen53083ab2013-08-09 13:37:38 -0700336 TrustedCredentialsSettings.class.getName(),
Amith Yamasani3a14ae22013-08-30 15:43:44 -0700337 PaymentSettings.class.getName(),
338 KeyboardLayoutPickerFragment.class.getName()
Amith Yamasania677ee22013-07-26 13:38:41 -0700339 };
340
341 @Override
342 protected boolean isValidFragment(String fragmentName) {
343 // Almost all fragments are wrapped in this,
344 // except for a few that have their own activities.
345 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
346 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
347 }
348 return false;
349 }
350
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800351 private void switchToHeaderLocal(Header header) {
352 mInLocalHeaderSwitch = true;
353 switchToHeader(header);
354 mInLocalHeaderSwitch = false;
355 }
356
357 @Override
358 public void switchToHeader(Header header) {
359 if (!mInLocalHeaderSwitch) {
360 mCurrentHeader = null;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800361 }
362 super.switchToHeader(header);
363 }
364
365 /**
366 * Switch to parent fragment and store the grand parent's info
Jake Hamby2748fc22011-01-12 15:06:28 -0800367 * @param className name of the activity wrapper for the parent fragment.
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800368 */
369 private void switchToParent(String className) {
370 final ComponentName cn = new ComponentName(this, className);
371 try {
372 final PackageManager pm = getPackageManager();
373 final ActivityInfo parentInfo = pm.getActivityInfo(cn, PackageManager.GET_META_DATA);
374
375 if (parentInfo != null && parentInfo.metaData != null) {
376 String fragmentClass = parentInfo.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
377 CharSequence fragmentTitle = parentInfo.loadLabel(pm);
378 Header parentHeader = new Header();
379 parentHeader.fragment = fragmentClass;
380 parentHeader.title = fragmentTitle;
381 mCurrentHeader = parentHeader;
382
383 switchToHeaderLocal(parentHeader);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700384 highlightHeader(mTopLevelHeaderId);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800385 }
386 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700387 Log.w(LOG_TAG, "Could not find parent activity : " + className);
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800388 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700389 }
390
Amith Yamasani3965ae62010-11-15 14:45:19 -0800391 @Override
392 public void onNewIntent(Intent intent) {
393 super.onNewIntent(intent);
394
395 // If it is not launched from history, then reset to top-level
Amith Yamasanief617232012-10-09 16:57:25 -0700396 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
397 if (mFirstHeader != null && !onIsHidingHeaders() && onIsMultiPane()) {
398 switchToHeaderLocal(mFirstHeader);
399 }
400 getListView().setSelectionFromTop(0, 0);
Amith Yamasani3965ae62010-11-15 14:45:19 -0800401 }
402 }
403
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700404 private void highlightHeader(int id) {
405 if (id != 0) {
406 Integer index = mHeaderIndexMap.get(id);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700407 if (index != null) {
408 getListView().setItemChecked(index, true);
Amith Yamasanief617232012-10-09 16:57:25 -0700409 if (isMultiPane()) {
410 getListView().smoothScrollToPosition(index);
411 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700412 }
413 }
414 }
415
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700416 @Override
417 public Intent getIntent() {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700418 Intent superIntent = super.getIntent();
419 String startingFragment = getStartingFragmentClass(superIntent);
Gilles Debunne3661b622011-06-27 11:19:16 -0700420 // This is called from super.onCreate, isMultiPane() is not yet reliable
421 // Do not use onIsHidingHeaders either, which relies itself on this method
422 if (startingFragment != null && !onIsMultiPane()) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700423 Intent modIntent = new Intent(superIntent);
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700424 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700425 Bundle args = superIntent.getExtras();
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700426 if (args != null) {
427 args = new Bundle(args);
428 } else {
429 args = new Bundle();
430 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700431 args.putParcelable("intent", superIntent);
432 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700433 return modIntent;
434 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700435 return superIntent;
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700436 }
437
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700438 /**
Amith Yamasani379d9b02010-09-27 12:03:59 -0700439 * Checks if the component name in the intent is different from the Settings class and
440 * returns the class name to load as a fragment.
441 */
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700442 protected String getStartingFragmentClass(Intent intent) {
443 if (mFragmentClass != null) return mFragmentClass;
444
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700445 String intentClass = intent.getComponent().getClassName();
Amith Yamasani379d9b02010-09-27 12:03:59 -0700446 if (intentClass.equals(getClass().getName())) return null;
447
Dianne Hackbornee293792010-11-01 12:32:33 -0700448 if ("com.android.settings.ManageApplications".equals(intentClass)
449 || "com.android.settings.RunningServices".equals(intentClass)
450 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700451 // Old names of manage apps.
Dianne Hackbornf4eb85b2010-10-29 16:53:04 -0700452 intentClass = com.android.settings.applications.ManageApplications.class.getName();
453 }
454
Amith Yamasani379d9b02010-09-27 12:03:59 -0700455 return intentClass;
456 }
457
458 /**
459 * Override initial header when an activity-alias is causing Settings to be launched
460 * for a specific fragment encoded in the android:name parameter.
461 */
462 @Override
463 public Header onGetInitialHeader() {
464 String fragmentClass = getStartingFragmentClass(super.getIntent());
465 if (fragmentClass != null) {
466 Header header = new Header();
467 header.fragment = fragmentClass;
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800468 header.title = getTitle();
Amith Yamasanie0e4fc22010-10-05 11:49:51 -0700469 header.fragmentArguments = getIntent().getExtras();
Amith Yamasanic9fdfa82010-12-14 14:38:16 -0800470 mCurrentHeader = header;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700471 return header;
472 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700473
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700474 return mFirstHeader;
Amith Yamasani379d9b02010-09-27 12:03:59 -0700475 }
476
Dianne Hackbornb7258182011-03-15 16:23:55 -0700477 @Override
Dianne Hackborn48147dc2011-03-18 12:29:41 -0700478 public Intent onBuildStartFragmentIntent(String fragmentName, Bundle args,
479 int titleRes, int shortTitleRes) {
480 Intent intent = super.onBuildStartFragmentIntent(fragmentName, args,
481 titleRes, shortTitleRes);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700482
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800483 // Some fragments want split ActionBar; these should stay in sync with
484 // uiOptions for fragments also defined as activities in manifest.
485 if (WifiSettings.class.getName().equals(fragmentName) ||
486 WifiP2pSettings.class.getName().equals(fragmentName) ||
487 BluetoothSettings.class.getName().equals(fragmentName) ||
488 DreamSettings.class.getName().equals(fragmentName) ||
Lifu Tangd5fbbc42013-08-01 17:23:10 -0700489 LocationSettings.class.getName().equals(fragmentName) ||
Svetoslav2d531652013-09-03 21:32:40 -0700490 ToggleAccessibilityServicePreferenceFragment.class.getName().equals(fragmentName) ||
491 PrintSettingsFragment.class.getName().equals(fragmentName) ||
492 PrintServiceSettingsFragment.class.getName().equals(fragmentName)) {
Jeff Sharkeycc0d26b2012-11-27 16:42:27 -0800493 intent.putExtra(EXTRA_UI_OPTIONS, ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
Jeff Sharkey9fab0da2011-07-09 17:52:31 -0700494 }
495
Dianne Hackbornb7258182011-03-15 16:23:55 -0700496 intent.setClass(this, SubSettings.class);
497 return intent;
498 }
Amith Yamasaniea7b28c2012-06-20 13:51:40 -0700499
Amith Yamasani379d9b02010-09-27 12:03:59 -0700500 /**
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700501 * Populate the activity with the top-level headers.
502 */
Amith Yamasanid7993472010-08-18 13:59:28 -0700503 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -0700504 public void onBuildHeaders(List<Header> headers) {
Amith Yamasani2972c012013-10-02 11:48:46 -0700505 if (!onIsHidingHeaders()) {
506 loadHeadersFromResource(R.xml.settings_headers, headers);
507 updateHeaderList(headers);
508 }
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700509 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700510
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700511 private void updateHeaderList(List<Header> target) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700512 final boolean showDev = mDevelopmentPreferences.getBoolean(
513 DevelopmentSettings.PREF_SHOW,
514 android.os.Build.TYPE.equals("eng"));
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700515 int i = 0;
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400516
517 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700518 mHeaderIndexMap.clear();
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700519 while (i < target.size()) {
520 Header header = target.get(i);
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700521 // Ids are integers, so downcasting
522 int id = (int) header.id;
Chris Wren2bc32ae2012-09-24 14:23:46 -0400523 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700524 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
Gilles Debunne2454f492011-06-21 16:16:06 -0700525 } else if (id == R.id.wifi_settings) {
526 // Remove WiFi Settings if WiFi service is not available.
527 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700528 target.remove(i);
Gilles Debunne2454f492011-06-21 16:16:06 -0700529 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700530 } else if (id == R.id.bluetooth_settings) {
531 // Remove Bluetooth Settings if Bluetooth service is not available.
Gilles Debunne2454f492011-06-21 16:16:06 -0700532 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700533 target.remove(i);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700534 }
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700535 } else if (id == R.id.data_usage_settings) {
536 // Remove data usage when kernel module not enabled
537 final INetworkManagementService netManager = INetworkManagementService.Stub
538 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
539 try {
540 if (!netManager.isBandwidthControlEnabled()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700541 target.remove(i);
Jeff Sharkey34e964d2012-04-21 15:41:48 -0700542 }
543 } catch (RemoteException e) {
544 // ignored
545 }
Jaewan Kima3fe77b2013-06-04 21:17:40 +0900546 } else if (id == R.id.battery_settings) {
547 // Remove battery settings when battery is not available. (e.g. TV)
548
549 if (!mBatteryPresent) {
550 target.remove(i);
551 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700552 } else if (id == R.id.account_settings) {
553 int headerIndex = i + 1;
554 i = insertAccountsHeaders(target, headerIndex);
Christopher Tatee238a472013-08-27 14:21:46 -0700555 } else if (id == R.id.home_settings) {
Christopher Tate8243c9a2013-09-19 14:59:17 -0700556 if (!updateHomeSettingHeaders(header)) {
557 target.remove(i);
558 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700559 } else if (id == R.id.user_settings) {
Amith Yamasanidc6bfa62012-09-06 18:04:06 -0700560 if (!UserHandle.MU_ENABLED
Jeff Sharkey44202462012-09-19 13:13:45 -0700561 || !UserManager.supportsMultipleUsers()
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700562 || Utils.isMonkeyRunning()) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700563 target.remove(i);
564 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700565 } else if (id == R.id.nfc_payment_settings) {
Martijn Coenen81ab8172013-09-30 12:07:02 -0700566 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
Martijn Coenen26515da2013-08-01 18:13:33 -0700567 target.remove(i);
Martijn Coenen81ab8172013-09-30 12:07:02 -0700568 } else {
569 // Only show if NFC is on and we have the HCE feature
570 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
571 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
572 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
573 target.remove(i);
574 }
Martijn Coenen26515da2013-08-01 18:13:33 -0700575 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700576 } else if (id == R.id.development_settings) {
577 if (!showDev) {
578 target.remove(i);
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700579 }
Maggie Benthall0c5a4012013-03-14 17:41:27 -0400580 } else if (id == R.id.account_add) {
581 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
582 target.remove(i);
583 }
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700584 }
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700585
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900586 if (i < target.size() && target.get(i) == header
Amith Yamasanib99f8642012-10-10 21:41:12 -0700587 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
Amith Yamasanib810a0d2012-03-25 10:12:26 -0700588 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
Dianne Hackborncaefa9b2012-10-10 15:05:42 -0700589 target.remove(i);
Amith Yamasanid7993472010-08-18 13:59:28 -0700590 }
Gilles Debunnee78c1872011-06-20 15:00:07 -0700591
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700592 // Increment if the current one wasn't removed by the Utils code.
Jaewan Kimbb0e6212013-02-04 15:36:17 +0900593 if (i < target.size() && target.get(i) == header) {
Amith Yamasani3965ae62010-11-15 14:45:19 -0800594 // Hold on to the first header, when we need to reset to the top-level
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700595 if (mFirstHeader == null &&
596 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
597 mFirstHeader = header;
598 }
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700599 mHeaderIndexMap.put(id, i);
Amith Yamasani02cf71a2010-09-21 15:48:52 -0700600 i++;
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700601 }
Amith Yamasanid7993472010-08-18 13:59:28 -0700602 }
603 }
604
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700605 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
606 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
607 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
608 for (String accountType : accountTypes) {
609 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
Brian Muramatsuc28af522012-06-28 14:25:14 -0700610 if (label == null) {
611 continue;
612 }
613
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700614 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
615 boolean skipToAccount = accounts.length == 1
616 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700617 Header accHeader = new Header();
618 accHeader.title = label;
619 if (accHeader.extras == null) {
620 accHeader.extras = new Bundle();
621 }
Amith Yamasani3882c2e2012-06-07 17:03:20 -0700622 if (skipToAccount) {
623 accHeader.breadCrumbTitleRes = R.string.account_sync_settings_title;
624 accHeader.breadCrumbShortTitleRes = R.string.account_sync_settings_title;
625 accHeader.fragment = AccountSyncSettings.class.getName();
626 accHeader.fragmentArguments = new Bundle();
627 // Need this for the icon
628 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
629 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
630 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
631 accounts[0]);
632 } else {
633 accHeader.breadCrumbTitle = label;
634 accHeader.breadCrumbShortTitle = label;
635 accHeader.fragment = ManageAccountsSettings.class.getName();
636 accHeader.fragmentArguments = new Bundle();
637 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
638 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
639 accountType);
640 if (!isMultiPane()) {
641 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
642 label.toString());
643 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700644 }
645 accountHeaders.add(accHeader);
Amith Yamasani2972c012013-10-02 11:48:46 -0700646 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700647 }
648
649 // Sort by label
650 Collections.sort(accountHeaders, new Comparator<Header>() {
651 @Override
652 public int compare(Header h1, Header h2) {
653 return h1.title.toString().compareTo(h2.title.toString());
654 }
655 });
656
657 for (Header header : accountHeaders) {
658 target.add(headerIndex++, header);
659 }
Amith Yamasani86708a82012-06-06 20:23:08 -0700660 if (!mListeningToAccountUpdates) {
661 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
662 mListeningToAccountUpdates = true;
663 }
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700664 return headerIndex;
665 }
666
Christopher Tate8243c9a2013-09-19 14:59:17 -0700667 private boolean updateHomeSettingHeaders(Header header) {
Christopher Tatedc7be102013-10-08 11:30:19 -0700668 // Once we decide to show Home settings, keep showing it forever
669 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
670 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
671 return true;
672 }
673
Christopher Tatee238a472013-08-27 14:21:46 -0700674 try {
Jeff Sharkeyd572a032013-09-23 17:41:16 -0700675 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
676 getPackageManager().getHomeActivities(homeApps);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700677 if (homeApps.size() < 2) {
678 // When there's only one available home app, omit this settings
Christopher Tatee0d934f2013-10-01 12:32:27 -0700679 // category entirely at the top level UI. If the user just
680 // uninstalled the penultimate home app candidiate, we also
681 // now tell them about why they aren't seeing 'Home' in the list.
682 if (sShowNoHomeNotice) {
683 sShowNoHomeNotice = false;
684 NoHomeDialogFragment.show(this);
685 }
Christopher Tate8243c9a2013-09-19 14:59:17 -0700686 return false;
Christopher Tatee0d934f2013-10-01 12:32:27 -0700687 } else {
688 // Okay, we're allowing the Home settings category. Tell it, when
689 // invoked via this front door, that we'll need to be told about the
690 // case when the user uninstalls all but one home app.
691 if (header.fragmentArguments == null) {
692 header.fragmentArguments = new Bundle();
693 }
694 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
Christopher Tate8243c9a2013-09-19 14:59:17 -0700695 }
Christopher Tatee238a472013-08-27 14:21:46 -0700696 } catch (Exception e) {
697 // Can't look up the home activity; bail on configuring the icon
698 Log.w(LOG_TAG, "Problem looking up home activity!", e);
699 }
Christopher Tatedc7be102013-10-08 11:30:19 -0700700
701 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
Christopher Tate8243c9a2013-09-19 14:59:17 -0700702 return true;
Christopher Tatee238a472013-08-27 14:21:46 -0700703 }
704
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700705 private void getMetaData() {
706 try {
707 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
708 PackageManager.GET_META_DATA);
709 if (ai == null || ai.metaData == null) return;
710 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
711 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
712 } catch (NameNotFoundException nnfe) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700713 // No recovery
Amith Yamasani5203bdf2010-11-04 09:59:44 -0700714 }
715 }
716
Amith Yamasani9e3a4702011-01-11 09:09:26 -0800717 @Override
718 public boolean hasNextButton() {
719 return super.hasNextButton();
720 }
721
722 @Override
723 public Button getNextButton() {
724 return super.getNextButton();
725 }
726
Christopher Tatee0d934f2013-10-01 12:32:27 -0700727 public static class NoHomeDialogFragment extends DialogFragment {
728 public static void show(Activity parent) {
729 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
730 dialog.show(parent.getFragmentManager(), null);
731 }
732
733 @Override
734 public Dialog onCreateDialog(Bundle savedInstanceState) {
735 return new AlertDialog.Builder(getActivity())
736 .setMessage(R.string.only_one_home_message)
737 .setPositiveButton(android.R.string.ok, null)
738 .create();
739 }
740 }
741
Gilles Debunnee78c1872011-06-20 15:00:07 -0700742 private static class HeaderAdapter extends ArrayAdapter<Header> {
743 static final int HEADER_TYPE_CATEGORY = 0;
744 static final int HEADER_TYPE_NORMAL = 1;
745 static final int HEADER_TYPE_SWITCH = 2;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400746 static final int HEADER_TYPE_BUTTON = 3;
747 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700748
749 private final WifiEnabler mWifiEnabler;
750 private final BluetoothEnabler mBluetoothEnabler;
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700751 private AuthenticatorHelper mAuthHelper;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400752 private DevicePolicyManager mDevicePolicyManager;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700753
754 private static class HeaderViewHolder {
755 ImageView icon;
756 TextView title;
757 TextView summary;
758 Switch switch_;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400759 ImageButton button_;
760 View divider_;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700761 }
762
763 private LayoutInflater mInflater;
764
765 static int getHeaderType(Header header) {
766 if (header.fragment == null && header.intent == null) {
767 return HEADER_TYPE_CATEGORY;
768 } else if (header.id == R.id.wifi_settings || header.id == R.id.bluetooth_settings) {
769 return HEADER_TYPE_SWITCH;
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400770 } else if (header.id == R.id.security_settings) {
771 return HEADER_TYPE_BUTTON;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700772 } else {
773 return HEADER_TYPE_NORMAL;
774 }
775 }
776
777 @Override
778 public int getItemViewType(int position) {
779 Header header = getItem(position);
780 return getHeaderType(header);
781 }
782
783 @Override
784 public boolean areAllItemsEnabled() {
785 return false; // because of categories
786 }
787
788 @Override
789 public boolean isEnabled(int position) {
790 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
791 }
792
793 @Override
794 public int getViewTypeCount() {
795 return HEADER_TYPE_COUNT;
796 }
797
798 @Override
799 public boolean hasStableIds() {
800 return true;
801 }
802
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700803 public HeaderAdapter(Context context, List<Header> objects,
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400804 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
Gilles Debunnee78c1872011-06-20 15:00:07 -0700805 super(context, 0, objects);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700806
807 mAuthHelper = authenticatorHelper;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700808 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700809
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700810 // Temp Switches provided as placeholder until the adapter replaces these with actual
Gilles Debunnee78c1872011-06-20 15:00:07 -0700811 // Switches inflated from their layouts. Must be done before adapter is set in super
812 mWifiEnabler = new WifiEnabler(context, new Switch(context));
813 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400814 mDevicePolicyManager = dpm;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700815 }
816
817 @Override
818 public View getView(int position, View convertView, ViewGroup parent) {
819 HeaderViewHolder holder;
820 Header header = getItem(position);
821 int headerType = getHeaderType(header);
822 View view = null;
823
824 if (convertView == null) {
825 holder = new HeaderViewHolder();
826 switch (headerType) {
827 case HEADER_TYPE_CATEGORY:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700828 view = new TextView(getContext(), null,
829 android.R.attr.listSeparatorTextViewStyle);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700830 holder.title = (TextView) view;
831 break;
832
833 case HEADER_TYPE_SWITCH:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700834 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
835 false);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700836 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700837 holder.title = (TextView)
838 view.findViewById(com.android.internal.R.id.title);
839 holder.summary = (TextView)
840 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700841 holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
842 break;
843
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400844 case HEADER_TYPE_BUTTON:
845 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
846 false);
847 holder.icon = (ImageView) view.findViewById(R.id.icon);
848 holder.title = (TextView)
849 view.findViewById(com.android.internal.R.id.title);
850 holder.summary = (TextView)
851 view.findViewById(com.android.internal.R.id.summary);
852 holder.button_ = (ImageButton) view.findViewById(R.id.buttonWidget);
853 holder.divider_ = view.findViewById(R.id.divider);
854 break;
855
Gilles Debunnee78c1872011-06-20 15:00:07 -0700856 case HEADER_TYPE_NORMAL:
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700857 view = mInflater.inflate(
Amith Yamasanic8a93172012-06-08 13:35:47 -0700858 R.layout.preference_header_item, parent,
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700859 false);
Amith Yamasanic8a93172012-06-08 13:35:47 -0700860 holder.icon = (ImageView) view.findViewById(R.id.icon);
Gilles Debunneb396c9b2011-06-22 16:07:29 -0700861 holder.title = (TextView)
862 view.findViewById(com.android.internal.R.id.title);
863 holder.summary = (TextView)
864 view.findViewById(com.android.internal.R.id.summary);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700865 break;
866 }
867 view.setTag(holder);
868 } else {
869 view = convertView;
870 holder = (HeaderViewHolder) view.getTag();
871 }
872
873 // All view fields must be updated every time, because the view may be recycled
874 switch (headerType) {
875 case HEADER_TYPE_CATEGORY:
876 holder.title.setText(header.getTitle(getContext().getResources()));
877 break;
878
879 case HEADER_TYPE_SWITCH:
880 // Would need a different treatment if the main menu had more switches
881 if (header.id == R.id.wifi_settings) {
882 mWifiEnabler.setSwitch(holder.switch_);
883 } else {
884 mBluetoothEnabler.setSwitch(holder.switch_);
885 }
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400886 updateCommonHeaderView(header, holder);
887 break;
Gilles Debunnee78c1872011-06-20 15:00:07 -0700888
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400889 case HEADER_TYPE_BUTTON:
890 if (header.id == R.id.security_settings) {
891 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
892 if (hasCert) {
893 holder.button_.setVisibility(View.VISIBLE);
894 holder.divider_.setVisibility(View.VISIBLE);
895 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
896 if (isManaged) {
Maggie Benthall273155c2013-09-18 16:53:23 -0400897 holder.button_.setImageResource(R.drawable.ic_settings_about);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400898 } else {
899 holder.button_.setImageResource(
900 android.R.drawable.stat_notify_error);
901 }
902 holder.button_.setOnClickListener(new OnClickListener() {
903 @Override
904 public void onClick(View v) {
905 Intent intent = new Intent(
906 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
907 getContext().startActivity(intent);
908 }
909 });
910 } else {
911 holder.button_.setVisibility(View.GONE);
912 holder.divider_.setVisibility(View.GONE);
913 }
914 }
915 updateCommonHeaderView(header, holder);
916 break;
917
Gilles Debunnee78c1872011-06-20 15:00:07 -0700918 case HEADER_TYPE_NORMAL:
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400919 updateCommonHeaderView(header, holder);
Gilles Debunnee78c1872011-06-20 15:00:07 -0700920 break;
921 }
922
923 return view;
924 }
925
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400926 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
927 if (header.extras != null
928 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
929 String accType = header.extras.getString(
930 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
931 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
932 setHeaderIcon(holder, icon);
Maggie Benthall9d6c40e2013-09-05 15:33:58 -0400933 } else {
934 holder.icon.setImageResource(header.iconRes);
935 }
936 holder.title.setText(header.getTitle(getContext().getResources()));
937 CharSequence summary = header.getSummary(getContext().getResources());
938 if (!TextUtils.isEmpty(summary)) {
939 holder.summary.setVisibility(View.VISIBLE);
940 holder.summary.setText(summary);
941 } else {
942 holder.summary.setVisibility(View.GONE);
943 }
944 }
945
Christopher Tatee238a472013-08-27 14:21:46 -0700946 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
947 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
948 lp.width = getContext().getResources().getDimensionPixelSize(
949 R.dimen.header_icon_width);
950 lp.height = lp.width;
951 holder.icon.setLayoutParams(lp);
952 holder.icon.setImageDrawable(icon);
953 }
954
Gilles Debunnee78c1872011-06-20 15:00:07 -0700955 public void resume() {
956 mWifiEnabler.resume();
957 mBluetoothEnabler.resume();
958 }
Brian Muramatsuc28af522012-06-28 14:25:14 -0700959
Gilles Debunnee78c1872011-06-20 15:00:07 -0700960 public void pause() {
961 mWifiEnabler.pause();
962 mBluetoothEnabler.pause();
963 }
964 }
965
966 @Override
Amith Yamasanid1ab8282012-05-18 09:50:08 -0700967 public void onHeaderClick(Header header, int position) {
968 boolean revert = false;
969 if (header.id == R.id.account_add) {
970 revert = true;
971 }
972
973 super.onHeaderClick(header, position);
974
975 if (revert && mLastHeader != null) {
976 highlightHeader((int) mLastHeader.id);
977 } else {
978 mLastHeader = header;
979 }
980 }
981
982 @Override
Amith Yamasania4379d62011-07-22 10:34:58 -0700983 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
984 // Override the fragment title for Wallpaper settings
Amith Yamasanidfb65432011-11-29 16:38:14 -0800985 int titleRes = pref.getTitleRes();
Amith Yamasania4379d62011-07-22 10:34:58 -0700986 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
Amith Yamasanidfb65432011-11-29 16:38:14 -0800987 titleRes = R.string.wallpaper_settings_fragment_title;
Amith Yamasani8666b9e2012-09-27 14:50:13 -0700988 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
989 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
Amith Yamasanib0171712013-04-28 22:13:22 -0700990 if (UserManager.get(this).isLinkedUser()) {
991 titleRes = R.string.profile_info_settings_title;
992 } else {
993 titleRes = R.string.user_info_settings_title;
994 }
Amith Yamasania4379d62011-07-22 10:34:58 -0700995 }
Jean Chalard7dabe452012-05-10 18:08:07 +0900996 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
997 null, 0);
Amith Yamasania4379d62011-07-22 10:34:58 -0700998 return true;
999 }
1000
Maggie Benthall0c5a4012013-03-14 17:41:27 -04001001 @Override
Amith Yamasani3d384f42012-05-11 15:22:04 -07001002 public boolean shouldUpRecreateTask(Intent targetIntent) {
1003 return super.shouldUpRecreateTask(new Intent(this, Settings.class));
1004 }
1005
Amith Yamasania4379d62011-07-22 10:34:58 -07001006 @Override
Gilles Debunnee78c1872011-06-20 15:00:07 -07001007 public void setListAdapter(ListAdapter adapter) {
Amith Yamasaniea7b28c2012-06-20 13:51:40 -07001008 if (adapter == null) {
1009 super.setListAdapter(null);
1010 } else {
Maggie Benthall9d6c40e2013-09-05 15:33:58 -04001011 DevicePolicyManager dpm =
1012 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
1013 super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper, dpm));
Gilles Debunnee78c1872011-06-20 15:00:07 -07001014 }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001015 }
1016
Amith Yamasani56821db2012-06-05 13:20:45 -07001017 @Override
1018 public void onAccountsUpdated(Account[] accounts) {
Jeff Sharkey9ff79c12012-10-03 16:48:17 -07001019 // TODO: watch for package upgrades to invalidate cache; see 7206643
1020 mAuthenticatorHelper.updateAuthDescriptions(this);
Amith Yamasani56821db2012-06-05 13:20:45 -07001021 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1022 invalidateHeaders();
1023 }
1024
Christopher Tatee0d934f2013-10-01 12:32:27 -07001025 public static void requestHomeNotice() {
1026 sShowNoHomeNotice = true;
1027 }
1028
Amith Yamasani5203bdf2010-11-04 09:59:44 -07001029 /*
1030 * Settings subclasses for launching independently.
1031 */
Gilles Debunnee78c1872011-06-20 15:00:07 -07001032 public static class BluetoothSettingsActivity extends Settings { /* empty */ }
1033 public static class WirelessSettingsActivity extends Settings { /* empty */ }
1034 public static class TetherSettingsActivity extends Settings { /* empty */ }
1035 public static class VpnSettingsActivity extends Settings { /* empty */ }
1036 public static class DateTimeSettingsActivity extends Settings { /* empty */ }
1037 public static class StorageSettingsActivity extends Settings { /* empty */ }
1038 public static class WifiSettingsActivity extends Settings { /* empty */ }
repo syncb98463f2011-06-30 10:58:43 -07001039 public static class WifiP2pSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001040 public static class InputMethodAndLanguageSettingsActivity extends Settings { /* empty */ }
Jeff Browne46c5f32012-04-05 11:42:18 -07001041 public static class KeyboardLayoutPickerActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001042 public static class InputMethodAndSubtypeEnablerActivity extends Settings { /* empty */ }
satoke077d2b2011-07-25 09:38:11 +09001043 public static class SpellCheckersSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001044 public static class LocalePickerActivity extends Settings { /* empty */ }
1045 public static class UserDictionarySettingsActivity extends Settings { /* empty */ }
1046 public static class SoundSettingsActivity extends Settings { /* empty */ }
1047 public static class DisplaySettingsActivity extends Settings { /* empty */ }
1048 public static class DeviceInfoSettingsActivity extends Settings { /* empty */ }
1049 public static class ApplicationSettingsActivity extends Settings { /* empty */ }
1050 public static class ManageApplicationsActivity extends Settings { /* empty */ }
Amith Yamasania88bdd32013-11-25 11:10:00 -08001051 public static class AppOpsSummaryActivity extends Settings {
1052 @Override
1053 public boolean isValidFragment(String className) {
1054 if (AppOpsSummary.class.getName().equals(className)) {
1055 return true;
1056 }
1057 return super.isValidFragment(className);
1058 }
1059 }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001060 public static class StorageUseActivity extends Settings { /* empty */ }
1061 public static class DevelopmentSettingsActivity extends Settings { /* empty */ }
1062 public static class AccessibilitySettingsActivity extends Settings { /* empty */ }
Alan Viverette9b625f02013-09-11 13:03:14 -07001063 public static class CaptioningSettingsActivity extends Settings { /* empty */ }
Alan Viverettefc0ab212013-10-02 16:36:33 -07001064 public static class AccessibilityInversionSettingsActivity extends Settings { /* empty */ }
1065 public static class AccessibilityContrastSettingsActivity extends Settings { /* empty */ }
1066 public static class AccessibilityDaltonizerSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001067 public static class SecuritySettingsActivity extends Settings { /* empty */ }
Gilles Debunnea6a8a142011-06-09 11:56:17 -07001068 public static class LocationSettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001069 public static class PrivacySettingsActivity extends Settings { /* empty */ }
Gilles Debunnee78c1872011-06-20 15:00:07 -07001070 public static class RunningServicesActivity extends Settings { /* empty */ }
1071 public static class ManageAccountsSettingsActivity extends Settings { /* empty */ }
1072 public static class PowerUsageSummaryActivity extends Settings { /* empty */ }
1073 public static class AccountSyncSettingsActivity extends Settings { /* empty */ }
1074 public static class AccountSyncSettingsInAddAccountActivity extends Settings { /* empty */ }
1075 public static class CryptKeeperSettingsActivity extends Settings { /* empty */ }
1076 public static class DeviceAdminSettingsActivity extends Settings { /* empty */ }
1077 public static class DataUsageSummaryActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001078 public static class AdvancedWifiSettingsActivity extends Settings { /* empty */ }
Gilles Debunneab189bd2011-07-06 13:10:16 -07001079 public static class TextToSpeechSettingsActivity extends Settings { /* empty */ }
Jeff Hamilton3d670de2011-09-21 16:44:36 -05001080 public static class AndroidBeamSettingsActivity extends Settings { /* empty */ }
Jeff Brown9e143f52012-09-19 20:46:07 -07001081 public static class WifiDisplaySettingsActivity extends Settings { /* empty */ }
Daniel Sandler7dee75c2012-11-27 22:41:30 -05001082 public static class DreamSettingsActivity extends Settings { /* empty */ }
Daniel Sandler328e2d22013-01-17 13:14:02 -05001083 public static class NotificationStationActivity extends Settings { /* empty */ }
Amith Yamasani233592b2013-03-01 17:05:23 -08001084 public static class UserSettingsActivity extends Settings { /* empty */ }
Daniel Sandler79b9bfe2013-04-04 14:30:04 -04001085 public static class NotificationAccessSettingsActivity extends Settings { /* empty */ }
Amith Yamasania677ee22013-07-26 13:38:41 -07001086 public static class UsbSettingsActivity extends Settings { /* empty */ }
Geoffrey Borggaardfc6bc202013-08-09 11:44:42 -04001087 public static class TrustedCredentialsSettingsActivity extends Settings { /* empty */ }
Martijn Coenen53083ab2013-08-09 13:37:38 -07001088 public static class PaymentSettingsActivity extends Settings { /* empty */ }
Svetoslav Ganov74648ac2013-09-04 23:52:28 -07001089 public static class PrintSettingsActivity extends Settings { /* empty */ }
Svetoslavd9f156d2013-10-09 21:59:04 -07001090 public static class PrintJobSettingsActivity extends Settings { /* empty */ }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001091}