blob: e49d8277eadf898a02531656866cbe8e96d9deed [file] [log] [blame]
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
21import android.accounts.OnAccountsUpdateListener;
22import android.app.ActionBar;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
27import android.app.AlertDialog;
28import android.app.Dialog;
29import android.app.DialogFragment;
30import android.app.admin.DevicePolicyManager;
31import android.content.BroadcastReceiver;
32import android.content.Context;
33import android.content.Intent;
34import android.content.IntentFilter;
35import android.content.SharedPreferences;
36import android.content.pm.ActivityInfo;
37import android.content.pm.PackageManager;
38import android.content.pm.PackageManager.NameNotFoundException;
39import android.content.pm.ResolveInfo;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.res.TypedArray;
43import android.content.res.XmlResourceParser;
44import android.graphics.drawable.Drawable;
45import android.nfc.NfcAdapter;
46import android.os.Bundle;
47import android.os.Handler;
48import android.os.INetworkManagementService;
49import android.os.Message;
50import android.os.Parcel;
51import android.os.Parcelable;
52import android.os.RemoteException;
53import android.os.ServiceManager;
54import android.os.UserHandle;
55import android.os.UserManager;
56import android.preference.Preference;
57import android.preference.PreferenceFragment;
58import android.preference.PreferenceManager;
59import android.preference.PreferenceScreen;
60import android.support.v4.app.ActionBarDrawerToggle;
61import android.support.v4.widget.DrawerLayout;
62import android.text.TextUtils;
63import android.util.AttributeSet;
64import android.util.Log;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080065import android.util.TypedValue;
66import android.util.Xml;
67import android.view.LayoutInflater;
68import android.view.MenuItem;
69import android.view.View;
70import android.view.View.OnClickListener;
71import android.view.ViewGroup;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +000072import android.widget.AbsListView;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080073import android.widget.AdapterView;
74import android.widget.ArrayAdapter;
75import android.widget.Button;
76import android.widget.ImageButton;
77import android.widget.ImageView;
78import android.widget.ListView;
79import android.widget.Switch;
80import android.widget.TextView;
81
82import com.android.internal.util.ArrayUtils;
83import com.android.internal.util.XmlUtils;
84import com.android.settings.accessibility.AccessibilitySettings;
85import com.android.settings.accessibility.CaptionPropertiesFragment;
86import com.android.settings.accounts.AccountSyncSettings;
87import com.android.settings.accounts.AuthenticatorHelper;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080088import com.android.settings.accounts.ManageAccountsSettings;
89import com.android.settings.applications.ManageApplications;
90import com.android.settings.applications.ProcessStatsUi;
91import com.android.settings.bluetooth.BluetoothEnabler;
92import com.android.settings.bluetooth.BluetoothSettings;
93import com.android.settings.dashboard.DashboardSummary;
94import com.android.settings.deviceinfo.Memory;
95import com.android.settings.deviceinfo.UsbSettings;
96import com.android.settings.fuelgauge.PowerUsageSummary;
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -080097import com.android.settings.indexer.Index;
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -070098import com.android.settings.indexer.IndexableRef;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -080099import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
100import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
101import com.android.settings.inputmethod.SpellCheckersSettings;
102import com.android.settings.inputmethod.UserDictionaryList;
103import com.android.settings.location.LocationSettings;
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700104import com.android.settings.net.DataUsageMeteredSettings;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800105import com.android.settings.nfc.AndroidBeam;
106import com.android.settings.nfc.PaymentSettings;
107import com.android.settings.print.PrintJobSettingsFragment;
108import com.android.settings.print.PrintSettingsFragment;
109import com.android.settings.tts.TextToSpeechSettings;
110import com.android.settings.users.UserSettings;
111import com.android.settings.vpn2.VpnSettings;
112import com.android.settings.wfd.WifiDisplaySettings;
113import com.android.settings.wifi.AdvancedWifiSettings;
114import com.android.settings.wifi.WifiEnabler;
115import com.android.settings.wifi.WifiSettings;
116import com.android.settings.wifi.p2p.WifiP2pSettings;
117import org.xmlpull.v1.XmlPullParser;
118import org.xmlpull.v1.XmlPullParserException;
119
120import java.io.IOException;
121import java.util.ArrayList;
122import java.util.Collections;
123import java.util.Comparator;
124import java.util.HashMap;
125import java.util.List;
126
127public class SettingsActivity extends Activity
128 implements PreferenceManager.OnPreferenceTreeClickListener,
129 PreferenceFragment.OnPreferenceStartFragmentCallback,
130 ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener {
131
132 private static final String LOG_TAG = "Settings";
133
134 // Constants for state save/restore
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800135 private static final String SAVE_KEY_HEADERS_TAG = ":settings:headers";
136 private static final String SAVE_KEY_CURRENT_HEADER_TAG = ":settings:cur_header";
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800137
138 /**
139 * When starting this activity, the invoking Intent can contain this extra
140 * string to specify which fragment should be initially displayed.
141 * <p/>Starting from Key Lime Pie, when this argument is passed in, the activity
142 * will call isValidFragment() to confirm that the fragment class name is valid for this
143 * activity.
144 */
145 public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
146
147 /**
148 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
149 * this extra can also be specified to supply a Bundle of arguments to pass
150 * to that fragment when it is instantiated during the initial creation
151 * of the activity.
152 */
153 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
154
155 /**
156 * When starting this activity, the invoking Intent can contain this extra
157 * boolean that the header list should not be displayed. This is most often
158 * used in conjunction with {@link #EXTRA_SHOW_FRAGMENT} to launch
159 * the activity to display a specific fragment that the user has navigated
160 * to.
161 */
162 public static final String EXTRA_NO_HEADERS = ":settings:no_headers";
163
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800164 public static final String BACK_STACK_PREFS = ":settings:prefs";
165
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800166 // extras that allow any preference activity to be launched as part of a wizard
167
168 // show Back and Next buttons? takes boolean parameter
169 // Back will then return RESULT_CANCELED and Next RESULT_OK
170 protected static final String EXTRA_PREFS_SHOW_BUTTON_BAR = "extra_prefs_show_button_bar";
171
172 // add a Skip button?
173 private static final String EXTRA_PREFS_SHOW_SKIP = "extra_prefs_show_skip";
174
175 // specify custom text for the Back or Next buttons, or cause a button to not appear
176 // at all by setting it to null
177 protected static final String EXTRA_PREFS_SET_NEXT_TEXT = "extra_prefs_set_next_text";
178 protected static final String EXTRA_PREFS_SET_BACK_TEXT = "extra_prefs_set_back_text";
179
180 /**
181 * When starting this activity and using {@link #EXTRA_SHOW_FRAGMENT},
182 * this extra can also be specify to supply the title to be shown for
183 * that fragment.
184 */
185 protected static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
186
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800187 private static final String META_DATA_KEY_HEADER_ID =
188 "com.android.settings.TOP_LEVEL_HEADER_ID";
189
190 private static final String META_DATA_KEY_FRAGMENT_CLASS =
191 "com.android.settings.FRAGMENT_CLASS";
192
193 private static final String EXTRA_UI_OPTIONS = "settings:ui_options";
194
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800195 private static boolean sShowNoHomeNotice = false;
196
197 private String mFragmentClass;
198 private int mTopLevelHeaderId;
199 private Header mFirstHeader;
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800200 private Header mSelectedHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800201 private Header mCurrentHeader;
202
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800203 private CharSequence mInitialTitle;
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800204 private Header mInitialHeader;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800205
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800206 // Show only these settings for restricted users
207 private int[] SETTINGS_FOR_RESTRICTED = {
208 R.id.wireless_section,
209 R.id.wifi_settings,
210 R.id.bluetooth_settings,
211 R.id.data_usage_settings,
212 R.id.wireless_settings,
213 R.id.device_section,
214 R.id.sound_settings,
215 R.id.display_settings,
216 R.id.storage_settings,
217 R.id.application_settings,
218 R.id.battery_settings,
219 R.id.personal_section,
220 R.id.location_settings,
221 R.id.security_settings,
222 R.id.language_settings,
223 R.id.user_settings,
224 R.id.account_settings,
225 R.id.account_add,
226 R.id.system_section,
227 R.id.date_time_settings,
228 R.id.about_settings,
229 R.id.accessibility_settings,
230 R.id.print_settings,
231 R.id.nfc_payment_settings,
Fabrice Di Meglio2858b792014-02-18 19:35:08 -0800232 R.id.home_settings,
233 R.id.dashboard
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800234 };
235
236 private static final String[] ENTRY_FRAGMENTS = {
237 WirelessSettings.class.getName(),
238 WifiSettings.class.getName(),
239 AdvancedWifiSettings.class.getName(),
240 BluetoothSettings.class.getName(),
241 TetherSettings.class.getName(),
242 WifiP2pSettings.class.getName(),
243 VpnSettings.class.getName(),
244 DateTimeSettings.class.getName(),
245 LocalePicker.class.getName(),
246 InputMethodAndLanguageSettings.class.getName(),
247 SpellCheckersSettings.class.getName(),
248 UserDictionaryList.class.getName(),
249 UserDictionarySettings.class.getName(),
250 SoundSettings.class.getName(),
251 DisplaySettings.class.getName(),
252 DeviceInfoSettings.class.getName(),
253 ManageApplications.class.getName(),
254 ProcessStatsUi.class.getName(),
255 NotificationStation.class.getName(),
256 LocationSettings.class.getName(),
257 SecuritySettings.class.getName(),
258 PrivacySettings.class.getName(),
259 DeviceAdminSettings.class.getName(),
260 AccessibilitySettings.class.getName(),
261 CaptionPropertiesFragment.class.getName(),
262 com.android.settings.accessibility.ToggleInversionPreferenceFragment.class.getName(),
263 com.android.settings.accessibility.ToggleContrastPreferenceFragment.class.getName(),
264 com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
265 TextToSpeechSettings.class.getName(),
266 Memory.class.getName(),
267 DevelopmentSettings.class.getName(),
268 UsbSettings.class.getName(),
269 AndroidBeam.class.getName(),
270 WifiDisplaySettings.class.getName(),
271 PowerUsageSummary.class.getName(),
272 AccountSyncSettings.class.getName(),
273 CryptKeeperSettings.class.getName(),
274 DataUsageSummary.class.getName(),
275 DreamSettings.class.getName(),
276 UserSettings.class.getName(),
277 NotificationAccessSettings.class.getName(),
278 ManageAccountsSettings.class.getName(),
279 PrintSettingsFragment.class.getName(),
280 PrintJobSettingsFragment.class.getName(),
281 TrustedCredentialsSettings.class.getName(),
282 PaymentSettings.class.getName(),
283 KeyboardLayoutPickerFragment.class.getName(),
John Spurlock72438062014-02-27 18:01:20 -0500284 DashboardSummary.class.getName(),
285 ZenModeSettings.class.getName()
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800286 };
287
288 private SharedPreferences mDevelopmentPreferences;
289 private SharedPreferences.OnSharedPreferenceChangeListener mDevelopmentPreferencesListener;
290
291 // TODO: Update Call Settings based on airplane mode state.
292
293 protected HashMap<Integer, Integer> mHeaderIndexMap = new HashMap<Integer, Integer>();
294
295 private AuthenticatorHelper mAuthenticatorHelper;
296 private boolean mListeningToAccountUpdates;
297
298 private Button mNextButton;
299
300 private boolean mBatteryPresent = true;
301 private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
302
303 @Override
304 public void onReceive(Context context, Intent intent) {
305 String action = intent.getAction();
306 if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
307 boolean batteryPresent = Utils.isBatteryPresent(intent);
308
309 if (mBatteryPresent != batteryPresent) {
310 mBatteryPresent = batteryPresent;
311 invalidateHeaders();
312 }
313 }
314 }
315 };
316
317 private final ArrayList<Header> mHeaders = new ArrayList<Header>();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800318 private HeaderAdapter mHeaderAdapter;
319
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800320 private DrawerLayout mDrawerLayout;
321 private ListView mDrawer;
322 private ActionBarDrawerToggle mDrawerToggle;
323 private ActionBar mActionBar;
324
325 private static final int MSG_BUILD_HEADERS = 1;
326 private Handler mHandler = new Handler() {
327 @Override
328 public void handleMessage(Message msg) {
329 switch (msg.what) {
330 case MSG_BUILD_HEADERS: {
331 mHeaders.clear();
332 onBuildHeaders(mHeaders);
333 mHeaderAdapter.notifyDataSetChanged();
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800334 if (mCurrentHeader != null) {
335 Header mappedHeader = findBestMatchingHeader(mCurrentHeader, mHeaders);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800336 if (mappedHeader != null) {
337 setSelectedHeader(mappedHeader);
338 }
339 }
340 } break;
341 }
342 }
343 };
344
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700345 private static int NO_DATA_RES_ID = 0;
346
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800347 /**
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700348 * Indexable data description.
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800349 *
350 * Known restriction: we are only searching (for now) the first level of Settings.
351 */
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700352 private static IndexableRef[] INDEXABLE_REFS = new IndexableRef[] {
353 new IndexableRef(1, NO_DATA_RES_ID,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700354 WifiSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800355 R.drawable.ic_settings_wireless),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700356 new IndexableRef(2, R.xml.bluetooth_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700357 BluetoothSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800358 R.drawable.ic_settings_bluetooth2),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700359 new IndexableRef(3, R.xml.data_usage_metered_prefs,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700360 DataUsageMeteredSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800361 R.drawable.ic_settings_data_usage),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700362 new IndexableRef(4, R.xml.wireless_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700363 WirelessSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800364 R.drawable.empty_icon),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700365 new IndexableRef(5, R.xml.home_selection,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700366 HomeSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800367 R.drawable.ic_settings_home),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700368 new IndexableRef(6, R.xml.sound_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700369 SoundSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800370 R.drawable.ic_settings_sound),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700371 new IndexableRef(7, R.xml.display_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700372 DisplaySettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800373 R.drawable.ic_settings_display),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700374 new IndexableRef(7, NO_DATA_RES_ID,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700375 WallpaperTypeSettings.class.getName(),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700376 R.drawable.ic_settings_display),
377 new IndexableRef(8, R.xml.device_info_memory,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700378 Memory.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800379 R.drawable.ic_settings_storage),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700380 new IndexableRef(9, R.xml.power_usage_summary,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700381 PowerUsageSummary.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800382 R.drawable.ic_settings_battery),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700383 new IndexableRef(10, R.xml.user_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700384 UserSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800385 R.drawable.ic_settings_multiuser),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700386 new IndexableRef(11, R.xml.location_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700387 LocationSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800388 R.drawable.ic_settings_location),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700389 new IndexableRef(12, R.xml.security_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700390 SecuritySettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800391 R.drawable.ic_settings_security),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700392 new IndexableRef(13, R.xml.language_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700393 InputMethodAndLanguageSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800394 R.drawable.ic_settings_language),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700395 new IndexableRef(14, R.xml.privacy_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700396 PrivacySettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800397 R.drawable.ic_settings_backup),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700398 new IndexableRef(15, R.xml.date_time_prefs,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700399 DateTimeSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800400 R.drawable.ic_settings_date_time),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700401 new IndexableRef(16, R.xml.accessibility_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700402 AccessibilitySettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800403 R.drawable.ic_settings_accessibility),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700404 new IndexableRef(17, R.xml.print_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700405 PrintSettingsFragment.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800406 com.android.internal.R.drawable.ic_print),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700407 new IndexableRef(18, R.xml.development_prefs,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700408 DevelopmentSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800409 R.drawable.ic_settings_development),
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700410 new IndexableRef(19, R.xml.device_info_settings,
Fabrice Di Meglio7cacc122014-03-12 13:19:34 -0700411 DeviceInfoSettings.class.getName(),
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800412 R.drawable.ic_settings_about),
413 };
414
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800415 @Override
416 public boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref) {
417 // Override the fragment title for Wallpaper settings
418 int titleRes = pref.getTitleRes();
419 if (pref.getFragment().equals(WallpaperTypeSettings.class.getName())) {
420 titleRes = R.string.wallpaper_settings_fragment_title;
421 } else if (pref.getFragment().equals(OwnerInfoSettings.class.getName())
422 && UserHandle.myUserId() != UserHandle.USER_OWNER) {
423 if (UserManager.get(this).isLinkedUser()) {
424 titleRes = R.string.profile_info_settings_title;
425 } else {
426 titleRes = R.string.user_info_settings_title;
427 }
428 }
429 startPreferencePanel(pref.getFragment(), pref.getExtras(), titleRes, pref.getTitle(),
430 null, 0);
431 return true;
432 }
433
434 @Override
435 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
436 return false;
437 }
438
439 private class DrawerListener implements DrawerLayout.DrawerListener {
440 @Override
441 public void onDrawerOpened(View drawerView) {
442 mDrawerToggle.onDrawerOpened(drawerView);
443 }
444
445 @Override
446 public void onDrawerClosed(View drawerView) {
447 mDrawerToggle.onDrawerClosed(drawerView);
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800448 // Cannot process clicks when the App is finishing
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800449 if (isFinishing() || mSelectedHeader == null) {
450 return;
451 }
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800452 switchToHeader(mSelectedHeader, false, false);
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800453 mSelectedHeader = null;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800454 }
455
456 @Override
457 public void onDrawerSlide(View drawerView, float slideOffset) {
458 mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
459 }
460
461 @Override
462 public void onDrawerStateChanged(int newState) {
463 mDrawerToggle.onDrawerStateChanged(newState);
464 }
465 }
466
467 private class DrawerItemClickListener implements ListView.OnItemClickListener {
468 @Override
469 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
470 mDrawerLayout.closeDrawer(mDrawer);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000471 onListItemClick((ListView)parent, view, position, id);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800472 }
473 }
474
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800475 private Header findBestMatchingHeader(Header current, ArrayList<Header> from) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800476 ArrayList<Header> matches = new ArrayList<Header>();
477 for (int j=0; j<from.size(); j++) {
478 Header oh = from.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800479 if (current == oh || (current.id != HEADER_ID_UNDEFINED && current.id == oh.id)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800480 // Must be this one.
481 matches.clear();
482 matches.add(oh);
483 break;
484 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800485 if (current.fragment != null) {
486 if (current.fragment.equals(oh.fragment)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800487 matches.add(oh);
488 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800489 } else if (current.intent != null) {
490 if (current.intent.equals(oh.intent)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800491 matches.add(oh);
492 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800493 } else if (current.title != null) {
494 if (current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800495 matches.add(oh);
496 }
497 }
498 }
499 final int NM = matches.size();
500 if (NM == 1) {
501 return matches.get(0);
502 } else if (NM > 1) {
503 for (int j=0; j<NM; j++) {
504 Header oh = matches.get(j);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800505 if (current.fragmentArguments != null &&
506 current.fragmentArguments.equals(oh.fragmentArguments)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800507 return oh;
508 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800509 if (current.extras != null && current.extras.equals(oh.extras)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800510 return oh;
511 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800512 if (current.title != null && current.title.equals(oh.title)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800513 return oh;
514 }
515 }
516 }
517 return null;
518 }
519
520 private void invalidateHeaders() {
521 if (!mHandler.hasMessages(MSG_BUILD_HEADERS)) {
522 mHandler.sendEmptyMessage(MSG_BUILD_HEADERS);
523 }
524 }
525
526 @Override
527 protected void onPostCreate(Bundle savedInstanceState) {
528 super.onPostCreate(savedInstanceState);
529
530 // Sync the toggle state after onRestoreInstanceState has occurred.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800531 mDrawerToggle.syncState();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800532 }
533
534 @Override
535 public void onConfigurationChanged(Configuration newConfig) {
536 super.onConfigurationChanged(newConfig);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800537 mDrawerToggle.onConfigurationChanged(newConfig);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800538 Index.getInstance(this).update();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800539 }
540
541 @Override
542 public boolean onOptionsItemSelected(MenuItem item) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800543 if (mDrawerToggle.onOptionsItemSelected(item)) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800544 return true;
545 }
546 return super.onOptionsItemSelected(item);
547 }
548
549 @Override
550 protected void onCreate(Bundle savedInstanceState) {
551 if (getIntent().hasExtra(EXTRA_UI_OPTIONS)) {
552 getWindow().setUiOptions(getIntent().getIntExtra(EXTRA_UI_OPTIONS, 0));
553 }
554
Fabrice Di Megliob8dfbf12014-03-10 19:24:54 -0700555 Index.getInstance(this).addIndexableData(INDEXABLE_REFS);
Fabrice Di Meglio6f0739a2014-02-03 18:12:25 -0800556 Index.getInstance(this).update();
557
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800558 mAuthenticatorHelper = new AuthenticatorHelper();
559 mAuthenticatorHelper.updateAuthDescriptions(this);
560 mAuthenticatorHelper.onAccountsUpdated(this, null);
561
562 DevicePolicyManager dpm =
563 (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
Fabrice Di Megliodca28062014-02-21 17:42:56 -0800564
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800565 mHeaderAdapter= new HeaderAdapter(this, mHeaders, mAuthenticatorHelper, dpm);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800566
567 mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
568 Context.MODE_PRIVATE);
569
570 getMetaData();
571
572 super.onCreate(savedInstanceState);
573
574 setContentView(R.layout.settings_main);
575
576 getFragmentManager().addOnBackStackChangedListener(this);
577
578 mActionBar = getActionBar();
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800579 mActionBar.setDisplayHomeAsUpEnabled(true);
580 mActionBar.setHomeButtonEnabled(true);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800581
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800582 mDrawer = (ListView) findViewById(R.id.headers_drawer);
583 mDrawer.setAdapter(mHeaderAdapter);
584 mDrawer.setOnItemClickListener(new DrawerItemClickListener());
585 mDrawer.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800586
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800587 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
588 mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
589 R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800590
591 if (savedInstanceState != null) {
592 // We are restarting from a previous saved state; used that to
593 // initialize, instead of starting fresh.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800594 mInitialTitle = getTitle();
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800595
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800596 ArrayList<Header> headers =
597 savedInstanceState.getParcelableArrayList(SAVE_KEY_HEADERS_TAG);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800598 if (headers != null) {
599 mHeaders.addAll(headers);
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800600 int curHeader = savedInstanceState.getInt(SAVE_KEY_CURRENT_HEADER_TAG,
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800601 (int) HEADER_ID_UNDEFINED);
602 if (curHeader >= 0 && curHeader < mHeaders.size()) {
603 setSelectedHeader(mHeaders.get(curHeader));
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800604 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800605 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700606 setTitleFromBackStack();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800607 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800608 } else {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800609 String initialFragment = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
610 Bundle initialArguments = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
611
612 // We need to build the Headers in all cases
613 onBuildHeaders(mHeaders);
614
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800615 if (initialFragment != null) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000616 // If we are just showing a fragment, we want to run in
617 // new fragment mode, but don't need to compute and show
618 // the headers.
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800619 final int initialTitleResId = getIntent().getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE, 0);
620 mInitialTitle = (initialTitleResId > 0) ? getText(initialTitleResId) : getTitle();
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800621 setTitle(mInitialTitle);
622 switchToHeaderInner(initialFragment, initialArguments, true, false, mInitialTitle);
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800623 setSelectedHeaderById(mTopLevelHeaderId);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800624 mInitialHeader = mCurrentHeader;
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000625 } else {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000626 // If there are headers, then at this point we need to show
627 // them and, depending on the screen, we may also show in-line
628 // the currently selected preference fragment.
629 if (mHeaders.size() > 0) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800630 mInitialHeader = onGetInitialHeader();
631 mInitialTitle = getHeaderTitle(mInitialHeader);
632 switchToHeader(mInitialHeader, false, true);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000633 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800634 }
635 }
636
637 // see if we should show Back/Next buttons
638 Intent intent = getIntent();
639 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
640
641 View buttonBar = findViewById(com.android.internal.R.id.button_bar);
642 if (buttonBar != null) {
643 buttonBar.setVisibility(View.VISIBLE);
644
645 Button backButton = (Button)findViewById(com.android.internal.R.id.back_button);
646 backButton.setOnClickListener(new OnClickListener() {
647 public void onClick(View v) {
648 setResult(RESULT_CANCELED);
649 finish();
650 }
651 });
652 Button skipButton = (Button)findViewById(com.android.internal.R.id.skip_button);
653 skipButton.setOnClickListener(new OnClickListener() {
654 public void onClick(View v) {
655 setResult(RESULT_OK);
656 finish();
657 }
658 });
659 mNextButton = (Button)findViewById(com.android.internal.R.id.next_button);
660 mNextButton.setOnClickListener(new OnClickListener() {
661 public void onClick(View v) {
662 setResult(RESULT_OK);
663 finish();
664 }
665 });
666
667 // set our various button parameters
668 if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
669 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
670 if (TextUtils.isEmpty(buttonText)) {
671 mNextButton.setVisibility(View.GONE);
672 }
673 else {
674 mNextButton.setText(buttonText);
675 }
676 }
677 if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
678 String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
679 if (TextUtils.isEmpty(buttonText)) {
680 backButton.setVisibility(View.GONE);
681 }
682 else {
683 backButton.setText(buttonText);
684 }
685 }
686 if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
687 skipButton.setVisibility(View.VISIBLE);
688 }
689 }
690 }
691
692 if (!onIsHidingHeaders()) {
693 highlightHeader(mTopLevelHeaderId);
694 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800695 }
696
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800697 public Header onGetInitialHeader() {
698 String fragmentClass = getStartingFragmentClass(super.getIntent());
699 if (fragmentClass != null) {
700 Header header = new Header();
701 header.fragment = fragmentClass;
702 header.title = getTitle();
703 header.fragmentArguments = getIntent().getExtras();
704 return header;
705 }
706
707 return mFirstHeader;
708 }
709
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800710 @Override
Fabrice Di Meglioc95be4f2014-03-07 12:57:38 -0800711 public void onBackPressed() {
712 if (mDrawerLayout.isDrawerOpen(mDrawer)) {
713 mDrawerLayout.closeDrawer(mDrawer);
714 return;
715 }
716 super.onBackPressed();
717 }
718
719 @Override
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800720 public void onBackStackChanged() {
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700721 if (setTitleFromBackStack() == 0) {
722 setSelectedHeaderById(mInitialHeader.id);
723 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800724 }
725
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700726 private int setTitleFromBackStack() {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800727 final int count = getFragmentManager().getBackStackEntryCount();
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700728
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800729 if (count == 0) {
730 setTitle(mInitialTitle);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700731 return 0;
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800732 }
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700733
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800734 FragmentManager.BackStackEntry bse = getFragmentManager().getBackStackEntryAt(count - 1);
735 setTitleFromBackStackEntry(bse);
Fabrice Di Megliob643cbf2014-03-10 12:18:39 -0700736
737 return count;
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800738 }
739
740 private void setTitleFromBackStackEntry(FragmentManager.BackStackEntry bse) {
741 final CharSequence title;
742 final int titleRes = bse.getBreadCrumbTitleRes();
743 if (titleRes > 0) {
744 title = getText(titleRes);
745 } else {
746 title = bse.getBreadCrumbTitle();
747 }
748 if (title != null) {
749 setTitle(title);
750 }
Fabrice Di Meglio5529d292014-02-11 19:52:28 -0800751 }
752
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800753 @Override
754 protected void onSaveInstanceState(Bundle outState) {
755 super.onSaveInstanceState(outState);
756
757 if (mHeaders.size() > 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800758 outState.putParcelableArrayList(SAVE_KEY_HEADERS_TAG, mHeaders);
759 if (mCurrentHeader != null) {
760 int index = mHeaders.indexOf(mCurrentHeader);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800761 if (index >= 0) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800762 outState.putInt(SAVE_KEY_CURRENT_HEADER_TAG, index);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800763 }
764 }
765 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800766 }
767
768 @Override
769 public void onResume() {
770 super.onResume();
771
772 mDevelopmentPreferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
773 @Override
774 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
775 invalidateHeaders();
776 }
777 };
778 mDevelopmentPreferences.registerOnSharedPreferenceChangeListener(
779 mDevelopmentPreferencesListener);
780
Matthew Xiea504c4d2014-02-14 16:32:32 -0800781 mHeaderAdapter.resume(this);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800782 invalidateHeaders();
783
784 registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800785
786 mDrawerLayout.setDrawerListener(new DrawerListener());
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800787 }
788
789 @Override
790 public void onPause() {
791 super.onPause();
792
Fabrice Di Meglio7ce7c402014-02-10 17:11:10 -0800793 mDrawerLayout.setDrawerListener(null);
794
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800795 unregisterReceiver(mBatteryInfoReceiver);
796
797 mHeaderAdapter.pause();
798
799 mDevelopmentPreferences.unregisterOnSharedPreferenceChangeListener(
800 mDevelopmentPreferencesListener);
801
802 mDevelopmentPreferencesListener = null;
803 }
804
805 @Override
806 public void onDestroy() {
807 super.onDestroy();
808 if (mListeningToAccountUpdates) {
809 AccountManager.get(this).removeOnAccountsUpdatedListener(this);
810 }
811 }
812
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800813 protected boolean isValidFragment(String fragmentName) {
814 // Almost all fragments are wrapped in this,
815 // except for a few that have their own activities.
816 for (int i = 0; i < ENTRY_FRAGMENTS.length; i++) {
817 if (ENTRY_FRAGMENTS[i].equals(fragmentName)) return true;
818 }
819 return false;
820 }
821
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800822 private CharSequence getHeaderTitle(Header header) {
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800823 if (header == null || header.fragment == null) return getTitle();
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800824 final CharSequence title;
825 if (header.fragment.equals(DashboardSummary.class.getName())) {
826 title = getResources().getString(R.string.settings_label);
827 } else {
828 title = header.getTitle(getResources());
829 }
830 return title;
831 }
832
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800833 private void setSelectedHeaderById(long headerId) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800834 final int count = mHeaders.size();
835 for (int n = 0; n < count; n++) {
836 Header h = mHeaders.get(n);
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800837 if (h.id == headerId) {
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800838 setSelectedHeader(h);
839 return;
840 }
841 }
842 }
843
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800844 /**
845 * As the Headers can be rebuilt, their references can change, so use this method with caution!
846 */
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800847 private void setSelectedHeader(Header header) {
Fabrice Di Meglioa7ad6192014-02-12 15:38:26 -0800848 if (header == null) {
849 mCurrentHeader = null;
850 return;
851 }
852 // Update selected Header into Drawer only if it is not "Add Account"
853 if (header.id == R.id.account_add) {
854 mDrawer.clearChoices();
855 return;
856 }
Fabrice Di Meglio65027202014-02-11 15:19:46 -0800857 mCurrentHeader = header;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800858 int index = mHeaders.indexOf(header);
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800859 if (index >= 0) {
860 mDrawer.setItemChecked(index, true);
861 } else {
862 mDrawer.clearChoices();
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800863 }
864 }
865
Fabrice Di Meglio75eabc22014-03-07 18:17:03 -0800866 private void highlightHeader(int id) {
867 if (id != 0) {
868 Integer index = mHeaderIndexMap.get(id);
869 if (index != null) {
870 mDrawer.setItemChecked(index, true);
871 if (mDrawer.getVisibility() == View.VISIBLE) {
872 mDrawer.smoothScrollToPosition(index);
873 }
874 }
875 }
876 }
877
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800878 /**
879 * When in two-pane mode, switch to the fragment pane to show the given
880 * preference fragment.
881 *
882 * @param header The new header to display.
883 * @param validate true means that the fragment's Header needs to be validated.
884 * @param initial true means that it is the initial Header.
885 */
886 private void switchToHeader(Header header, boolean validate, boolean initial) {
887 if (header == null) {
888 return;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800889 }
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800890 // For switching to another Header it should be a different one
891 if (mCurrentHeader == null || header.id != mCurrentHeader.id) {
892 if (header.fragment != null) {
Fabrice Di Meglio832e5462014-03-06 19:12:14 -0800893 boolean addToBackStack;
894 if (initial) {
895 addToBackStack = false;
896 } else {
897 if (header.id != mInitialHeader.id) {
898 addToBackStack = true;
899 } else {
900 addToBackStack = (mTopLevelHeaderId > 0);
901 }
902 }
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800903 switchToHeaderInner(header.fragment, header.fragmentArguments, validate,
904 addToBackStack, getHeaderTitle(header));
905 setSelectedHeader(header);
906 } else if (header.intent != null) {
907 setSelectedHeader(header);
908 startActivity(header.intent);
909 } else {
910 throw new IllegalStateException(
911 "Can't switch to header that has no Fragment nor Intent");
912 }
913 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800914 }
915
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000916 /**
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800917 * Switch the fragment pane to show the given preference fragment.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000918 *
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800919 * (used for initial fragment)
920 *
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000921 * @param fragmentName The name of the fragment to display.
922 * @param args Optional arguments to supply to the fragment.
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800923 * @param validate true means that the fragment's Header needs to be validated.
924 * @param title The title of the fragment to display.
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000925 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800926 private void switchToHeader(String fragmentName, Bundle args, boolean validate,
927 CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000928 setSelectedHeader(null);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800929 switchToHeaderInner(fragmentName, args, validate, false, title);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000930 }
931
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -0800932 /**
933 * Switch to a specific Header with taking care of validation, Title and BackStack
934 */
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800935 private void switchToHeaderInner(String fragmentName, Bundle args, boolean validate,
936 boolean addToBackStack, CharSequence title) {
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +0000937 getFragmentManager().popBackStack(BACK_STACK_PREFS,
938 FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800939 if (validate && !isValidFragment(fragmentName)) {
940 throw new IllegalArgumentException("Invalid fragment for this activity: "
941 + fragmentName);
942 }
943 Fragment f = Fragment.instantiate(this, fragmentName, args);
944 FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fabrice Di Meglio4cc95a52014-02-07 18:53:14 -0800945 transaction.replace(R.id.prefs, f);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -0800946 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
947 if (addToBackStack) {
948 transaction.addToBackStack(BACK_STACK_PREFS);
949 }
950 if (title != null) {
951 transaction.setBreadCrumbTitle(title);
952 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800953 transaction.commitAllowingStateLoss();
954 }
955
956 @Override
957 public void onNewIntent(Intent intent) {
958 super.onNewIntent(intent);
959
960 // If it is not launched from history, then reset to top-level
961 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
962 if (mDrawer != null) {
963 mDrawer.setSelectionFromTop(0, 0);
964 }
965 }
966 }
967
968 /**
969 * Called to determine whether the header list should be hidden.
970 * The default implementation returns the
971 * value given in {@link #EXTRA_NO_HEADERS} or false if it is not supplied.
972 * This is set to false, for example, when the activity is being re-launched
973 * to show a particular preference activity.
974 */
975 public boolean onIsHidingHeaders() {
976 return getIntent().getBooleanExtra(EXTRA_NO_HEADERS, false);
977 }
978
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -0800979 @Override
980 public Intent getIntent() {
981 Intent superIntent = super.getIntent();
982 String startingFragment = getStartingFragmentClass(superIntent);
983 // This is called from super.onCreate, isMultiPane() is not yet reliable
984 // Do not use onIsHidingHeaders either, which relies itself on this method
985 if (startingFragment != null) {
986 Intent modIntent = new Intent(superIntent);
987 modIntent.putExtra(EXTRA_SHOW_FRAGMENT, startingFragment);
988 Bundle args = superIntent.getExtras();
989 if (args != null) {
990 args = new Bundle(args);
991 } else {
992 args = new Bundle();
993 }
994 args.putParcelable("intent", superIntent);
995 modIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, superIntent.getExtras());
996 return modIntent;
997 }
998 return superIntent;
999 }
1000
1001 /**
1002 * Checks if the component name in the intent is different from the Settings class and
1003 * returns the class name to load as a fragment.
1004 */
1005 private String getStartingFragmentClass(Intent intent) {
1006 if (mFragmentClass != null) return mFragmentClass;
1007
1008 String intentClass = intent.getComponent().getClassName();
1009 if (intentClass.equals(getClass().getName())) return null;
1010
1011 if ("com.android.settings.ManageApplications".equals(intentClass)
1012 || "com.android.settings.RunningServices".equals(intentClass)
1013 || "com.android.settings.applications.StorageUse".equals(intentClass)) {
1014 // Old names of manage apps.
1015 intentClass = com.android.settings.applications.ManageApplications.class.getName();
1016 }
1017
1018 return intentClass;
1019 }
1020
1021 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001022 * Start a new fragment containing a preference panel. If the preferences
1023 * are being displayed in multi-pane mode, the given fragment class will
1024 * be instantiated and placed in the appropriate pane. If running in
1025 * single-pane mode, a new activity will be launched in which to show the
1026 * fragment.
1027 *
1028 * @param fragmentClass Full name of the class implementing the fragment.
1029 * @param args Any desired arguments to supply to the fragment.
1030 * @param titleRes Optional resource identifier of the title of this
1031 * fragment.
1032 * @param titleText Optional text of the title of this fragment.
1033 * @param resultTo Optional fragment that result data should be sent to.
1034 * If non-null, resultTo.onActivityResult() will be called when this
1035 * preference panel is done. The launched panel must use
1036 * {@link #finishPreferencePanel(Fragment, int, Intent)} when done.
1037 * @param resultRequestCode If resultTo is non-null, this is the caller's
1038 * request code to be received with the resut.
1039 */
1040 public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
1041 CharSequence titleText, Fragment resultTo,
1042 int resultRequestCode) {
1043 startWithFragment(fragmentClass, args, resultTo, resultRequestCode, titleRes, titleText);
1044 }
1045
1046 /**
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001047 * Called by a preference panel fragment to finish itself.
1048 *
1049 * @param caller The fragment that is asking to be finished.
1050 * @param resultCode Optional result code to send back to the original
1051 * launching fragment.
1052 * @param resultData Optional result data to send back to the original
1053 * launching fragment.
1054 */
1055 public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
1056 setResult(resultCode, resultData);
1057 }
1058
1059 /**
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001060 * Start a new fragment.
1061 *
1062 * @param fragment The fragment to start
1063 * @param push If true, the current fragment will be pushed onto the back stack. If false,
1064 * the current fragment will be replaced.
1065 */
1066 public void startPreferenceFragment(Fragment fragment, boolean push) {
1067 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1068 transaction.replace(R.id.prefs, fragment);
1069 if (push) {
1070 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1071 transaction.addToBackStack(BACK_STACK_PREFS);
1072 } else {
1073 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
1074 }
1075 transaction.commitAllowingStateLoss();
1076 }
1077
1078 /**
1079 * Start a new fragment.
1080 *
1081 * @param fragmentName The name of the fragment to display.
1082 * @param args Optional arguments to supply to the fragment.
1083 * @param resultTo Option fragment that should receive the result of
1084 * the activity launch.
1085 * @param resultRequestCode If resultTo is non-null, this is the request code in which to
1086 * report the result.
1087 * @param titleRes Resource ID of string to display for the title of. If the Resource ID is a
1088 * valid one then it will be used to get the title. Otherwise the titleText
1089 * argument will be used as the title.
1090 * @param titleText string to display for the title of.
1091 */
1092 private void startWithFragment(String fragmentName, Bundle args, Fragment resultTo,
1093 int resultRequestCode, int titleRes, CharSequence titleText) {
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001094 final CharSequence cs;
1095 if (titleRes != 0) {
1096 cs = getText(titleRes);
1097 } else {
1098 cs = titleText;
1099 }
1100
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001101 Fragment f = Fragment.instantiate(this, fragmentName, args);
1102 if (resultTo != null) {
1103 f.setTargetFragment(resultTo, resultRequestCode);
1104 }
1105 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1106 transaction.replace(R.id.prefs, f);
1107 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
1108 transaction.addToBackStack(BACK_STACK_PREFS);
Fabrice Di Meglio8eb3f0f2014-02-27 15:51:46 -08001109 transaction.setBreadCrumbTitle(cs);
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001110 transaction.commitAllowingStateLoss();
Fabrice Di Meglio10afdb82014-02-11 19:50:56 +00001111 }
1112
1113 /**
Fabrice Di Meglio6d534a12014-03-03 11:34:18 -08001114 * Called when the activity needs its list of headers build.
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001115 *
1116 * @param headers The list in which to place the headers.
1117 */
1118 private void onBuildHeaders(List<Header> headers) {
1119 loadHeadersFromResource(R.xml.settings_headers, headers);
1120 updateHeaderList(headers);
1121 }
1122
1123 /**
1124 * Parse the given XML file as a header description, adding each
1125 * parsed Header into the target list.
1126 *
1127 * @param resid The XML resource to load and parse.
1128 * @param target The list in which the parsed headers should be placed.
1129 */
1130 private void loadHeadersFromResource(int resid, List<Header> target) {
1131 XmlResourceParser parser = null;
1132 try {
1133 parser = getResources().getXml(resid);
1134 AttributeSet attrs = Xml.asAttributeSet(parser);
1135
1136 int type;
1137 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1138 && type != XmlPullParser.START_TAG) {
1139 // Parse next until start tag is found
1140 }
1141
1142 String nodeName = parser.getName();
1143 if (!"preference-headers".equals(nodeName)) {
1144 throw new RuntimeException(
1145 "XML document must start with <preference-headers> tag; found"
1146 + nodeName + " at " + parser.getPositionDescription());
1147 }
1148
1149 Bundle curBundle = null;
1150
1151 final int outerDepth = parser.getDepth();
1152 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1153 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1154 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1155 continue;
1156 }
1157
1158 nodeName = parser.getName();
1159 if ("header".equals(nodeName)) {
1160 Header header = new Header();
1161
1162 TypedArray sa = obtainStyledAttributes(
1163 attrs, com.android.internal.R.styleable.PreferenceHeader);
1164 header.id = sa.getResourceId(
1165 com.android.internal.R.styleable.PreferenceHeader_id,
1166 (int)HEADER_ID_UNDEFINED);
1167 TypedValue tv = sa.peekValue(
1168 com.android.internal.R.styleable.PreferenceHeader_title);
1169 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1170 if (tv.resourceId != 0) {
1171 header.titleRes = tv.resourceId;
1172 } else {
1173 header.title = tv.string;
1174 }
1175 }
1176 tv = sa.peekValue(
1177 com.android.internal.R.styleable.PreferenceHeader_summary);
1178 if (tv != null && tv.type == TypedValue.TYPE_STRING) {
1179 if (tv.resourceId != 0) {
1180 header.summaryRes = tv.resourceId;
1181 } else {
1182 header.summary = tv.string;
1183 }
1184 }
1185 header.iconRes = sa.getResourceId(
1186 com.android.internal.R.styleable.PreferenceHeader_icon, 0);
1187 header.fragment = sa.getString(
1188 com.android.internal.R.styleable.PreferenceHeader_fragment);
1189 sa.recycle();
1190
1191 if (curBundle == null) {
1192 curBundle = new Bundle();
1193 }
1194
1195 final int innerDepth = parser.getDepth();
1196 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1197 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
1198 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1199 continue;
1200 }
1201
1202 String innerNodeName = parser.getName();
1203 if (innerNodeName.equals("extra")) {
1204 getResources().parseBundleExtra("extra", attrs, curBundle);
1205 XmlUtils.skipCurrentTag(parser);
1206
1207 } else if (innerNodeName.equals("intent")) {
1208 header.intent = Intent.parseIntent(getResources(), parser, attrs);
1209
1210 } else {
1211 XmlUtils.skipCurrentTag(parser);
1212 }
1213 }
1214
1215 if (curBundle.size() > 0) {
1216 header.fragmentArguments = curBundle;
1217 curBundle = null;
1218 }
1219
1220 target.add(header);
1221 } else {
1222 XmlUtils.skipCurrentTag(parser);
1223 }
1224 }
1225
1226 } catch (XmlPullParserException e) {
1227 throw new RuntimeException("Error parsing headers", e);
1228 } catch (IOException e) {
1229 throw new RuntimeException("Error parsing headers", e);
1230 } finally {
1231 if (parser != null) parser.close();
1232 }
1233 }
1234
1235 private void updateHeaderList(List<Header> target) {
1236 final boolean showDev = mDevelopmentPreferences.getBoolean(
1237 DevelopmentSettings.PREF_SHOW,
1238 android.os.Build.TYPE.equals("eng"));
1239 int i = 0;
1240
1241 final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
1242 mHeaderIndexMap.clear();
1243 while (i < target.size()) {
1244 Header header = target.get(i);
1245 // Ids are integers, so downcasting
1246 int id = (int) header.id;
1247 if (id == R.id.operator_settings || id == R.id.manufacturer_settings) {
1248 Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header);
1249 } else if (id == R.id.wifi_settings) {
1250 // Remove WiFi Settings if WiFi service is not available.
1251 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
1252 target.remove(i);
1253 }
1254 } else if (id == R.id.bluetooth_settings) {
1255 // Remove Bluetooth Settings if Bluetooth service is not available.
1256 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
1257 target.remove(i);
1258 }
1259 } else if (id == R.id.data_usage_settings) {
1260 // Remove data usage when kernel module not enabled
1261 final INetworkManagementService netManager = INetworkManagementService.Stub
1262 .asInterface(ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
1263 try {
1264 if (!netManager.isBandwidthControlEnabled()) {
1265 target.remove(i);
1266 }
1267 } catch (RemoteException e) {
1268 // ignored
1269 }
1270 } else if (id == R.id.battery_settings) {
1271 // Remove battery settings when battery is not available. (e.g. TV)
1272
1273 if (!mBatteryPresent) {
1274 target.remove(i);
1275 }
1276 } else if (id == R.id.account_settings) {
1277 int headerIndex = i + 1;
1278 i = insertAccountsHeaders(target, headerIndex);
1279 } else if (id == R.id.home_settings) {
1280 if (!updateHomeSettingHeaders(header)) {
1281 target.remove(i);
1282 }
1283 } else if (id == R.id.user_settings) {
1284 if (!UserHandle.MU_ENABLED
1285 || !UserManager.supportsMultipleUsers()
1286 || Utils.isMonkeyRunning()) {
1287 target.remove(i);
1288 }
1289 } else if (id == R.id.nfc_payment_settings) {
1290 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
1291 target.remove(i);
1292 } else {
1293 // Only show if NFC is on and we have the HCE feature
1294 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
1295 if (!adapter.isEnabled() || !getPackageManager().hasSystemFeature(
1296 PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)) {
1297 target.remove(i);
1298 }
1299 }
1300 } else if (id == R.id.development_settings) {
1301 if (!showDev) {
1302 target.remove(i);
1303 }
1304 } else if (id == R.id.account_add) {
1305 if (um.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
1306 target.remove(i);
1307 }
1308 }
1309
1310 if (i < target.size() && target.get(i) == header
1311 && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0
1312 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) {
1313 target.remove(i);
1314 }
1315
1316 // Increment if the current one wasn't removed by the Utils code.
1317 if (i < target.size() && target.get(i) == header) {
1318 // Hold on to the first header, when we need to reset to the top-level
1319 if (mFirstHeader == null &&
1320 HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) {
1321 mFirstHeader = header;
1322 }
1323 mHeaderIndexMap.put(id, i);
1324 i++;
1325 }
1326 }
1327 }
1328
1329 private int insertAccountsHeaders(List<Header> target, int headerIndex) {
1330 String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
1331 List<Header> accountHeaders = new ArrayList<Header>(accountTypes.length);
1332 for (String accountType : accountTypes) {
1333 CharSequence label = mAuthenticatorHelper.getLabelForType(this, accountType);
1334 if (label == null) {
1335 continue;
1336 }
1337
1338 Account[] accounts = AccountManager.get(this).getAccountsByType(accountType);
1339 boolean skipToAccount = accounts.length == 1
1340 && !mAuthenticatorHelper.hasAccountPreferences(accountType);
1341 Header accHeader = new Header();
1342 accHeader.title = label;
1343 if (accHeader.extras == null) {
1344 accHeader.extras = new Bundle();
1345 }
1346 if (skipToAccount) {
1347 accHeader.fragment = AccountSyncSettings.class.getName();
1348 accHeader.fragmentArguments = new Bundle();
1349 // Need this for the icon
1350 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1351 accHeader.extras.putParcelable(AccountSyncSettings.ACCOUNT_KEY, accounts[0]);
1352 accHeader.fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
1353 accounts[0]);
1354 } else {
1355 accHeader.fragment = ManageAccountsSettings.class.getName();
1356 accHeader.fragmentArguments = new Bundle();
1357 accHeader.extras.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
1358 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE,
1359 accountType);
1360 accHeader.fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
1361 label.toString());
1362 }
1363 accountHeaders.add(accHeader);
1364 mAuthenticatorHelper.preloadDrawableForType(this, accountType);
1365 }
1366
1367 // Sort by label
1368 Collections.sort(accountHeaders, new Comparator<Header>() {
1369 @Override
1370 public int compare(Header h1, Header h2) {
1371 return h1.title.toString().compareTo(h2.title.toString());
1372 }
1373 });
1374
1375 for (Header header : accountHeaders) {
1376 target.add(headerIndex++, header);
1377 }
1378 if (!mListeningToAccountUpdates) {
1379 AccountManager.get(this).addOnAccountsUpdatedListener(this, null, true);
1380 mListeningToAccountUpdates = true;
1381 }
1382 return headerIndex;
1383 }
1384
1385 private boolean updateHomeSettingHeaders(Header header) {
1386 // Once we decide to show Home settings, keep showing it forever
1387 SharedPreferences sp = getSharedPreferences(HomeSettings.HOME_PREFS, Context.MODE_PRIVATE);
1388 if (sp.getBoolean(HomeSettings.HOME_PREFS_DO_SHOW, false)) {
1389 return true;
1390 }
1391
1392 try {
1393 final ArrayList<ResolveInfo> homeApps = new ArrayList<ResolveInfo>();
1394 getPackageManager().getHomeActivities(homeApps);
1395 if (homeApps.size() < 2) {
1396 // When there's only one available home app, omit this settings
1397 // category entirely at the top level UI. If the user just
1398 // uninstalled the penultimate home app candidiate, we also
1399 // now tell them about why they aren't seeing 'Home' in the list.
1400 if (sShowNoHomeNotice) {
1401 sShowNoHomeNotice = false;
1402 NoHomeDialogFragment.show(this);
1403 }
1404 return false;
1405 } else {
1406 // Okay, we're allowing the Home settings category. Tell it, when
1407 // invoked via this front door, that we'll need to be told about the
1408 // case when the user uninstalls all but one home app.
1409 if (header.fragmentArguments == null) {
1410 header.fragmentArguments = new Bundle();
1411 }
1412 header.fragmentArguments.putBoolean(HomeSettings.HOME_SHOW_NOTICE, true);
1413 }
1414 } catch (Exception e) {
1415 // Can't look up the home activity; bail on configuring the icon
1416 Log.w(LOG_TAG, "Problem looking up home activity!", e);
1417 }
1418
1419 sp.edit().putBoolean(HomeSettings.HOME_PREFS_DO_SHOW, true).apply();
1420 return true;
1421 }
1422
1423 private void getMetaData() {
1424 try {
1425 ActivityInfo ai = getPackageManager().getActivityInfo(getComponentName(),
1426 PackageManager.GET_META_DATA);
1427 if (ai == null || ai.metaData == null) return;
1428 mTopLevelHeaderId = ai.metaData.getInt(META_DATA_KEY_HEADER_ID);
1429 mFragmentClass = ai.metaData.getString(META_DATA_KEY_FRAGMENT_CLASS);
1430 } catch (NameNotFoundException nnfe) {
1431 // No recovery
1432 Log.d(LOG_TAG, "Cannot get Metadata for: " + getComponentName().toString());
1433 }
1434 }
1435
1436 // give subclasses access to the Next button
1437 public boolean hasNextButton() {
1438 return mNextButton != null;
1439 }
1440
1441 public Button getNextButton() {
1442 return mNextButton;
1443 }
1444
1445 public static class NoHomeDialogFragment extends DialogFragment {
1446 public static void show(Activity parent) {
1447 final NoHomeDialogFragment dialog = new NoHomeDialogFragment();
1448 dialog.show(parent.getFragmentManager(), null);
1449 }
1450
1451 @Override
1452 public Dialog onCreateDialog(Bundle savedInstanceState) {
1453 return new AlertDialog.Builder(getActivity())
1454 .setMessage(R.string.only_one_home_message)
1455 .setPositiveButton(android.R.string.ok, null)
1456 .create();
1457 }
1458 }
1459
1460 private static class HeaderAdapter extends ArrayAdapter<Header> {
1461 static final int HEADER_TYPE_CATEGORY = 0;
1462 static final int HEADER_TYPE_NORMAL = 1;
1463 static final int HEADER_TYPE_SWITCH = 2;
1464 static final int HEADER_TYPE_BUTTON = 3;
1465 private static final int HEADER_TYPE_COUNT = HEADER_TYPE_BUTTON + 1;
1466
1467 private final WifiEnabler mWifiEnabler;
1468 private final BluetoothEnabler mBluetoothEnabler;
1469 private AuthenticatorHelper mAuthHelper;
1470 private DevicePolicyManager mDevicePolicyManager;
1471
1472 private static class HeaderViewHolder {
1473 ImageView mIcon;
1474 TextView mTitle;
1475 TextView mSummary;
1476 Switch mSwitch;
1477 ImageButton mButton;
1478 View mDivider;
1479 }
1480
1481 private LayoutInflater mInflater;
1482
1483 static int getHeaderType(Header header) {
1484 if (header.fragment == null && header.intent == null) {
1485 return HEADER_TYPE_CATEGORY;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001486 } else if (header.id == R.id.security_settings) {
1487 return HEADER_TYPE_BUTTON;
1488 } else {
1489 return HEADER_TYPE_NORMAL;
1490 }
1491 }
1492
1493 @Override
1494 public int getItemViewType(int position) {
1495 Header header = getItem(position);
1496 return getHeaderType(header);
1497 }
1498
1499 @Override
1500 public boolean areAllItemsEnabled() {
1501 return false; // because of categories
1502 }
1503
1504 @Override
1505 public boolean isEnabled(int position) {
1506 return getItemViewType(position) != HEADER_TYPE_CATEGORY;
1507 }
1508
1509 @Override
1510 public int getViewTypeCount() {
1511 return HEADER_TYPE_COUNT;
1512 }
1513
1514 @Override
1515 public boolean hasStableIds() {
1516 return true;
1517 }
1518
1519 public HeaderAdapter(Context context, List<Header> objects,
1520 AuthenticatorHelper authenticatorHelper, DevicePolicyManager dpm) {
1521 super(context, 0, objects);
1522
1523 mAuthHelper = authenticatorHelper;
1524 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1525
1526 // Temp Switches provided as placeholder until the adapter replaces these with actual
1527 // Switches inflated from their layouts. Must be done before adapter is set in super
1528 mWifiEnabler = new WifiEnabler(context, new Switch(context));
1529 mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
1530 mDevicePolicyManager = dpm;
1531 }
1532
1533 @Override
1534 public View getView(int position, View convertView, ViewGroup parent) {
1535 HeaderViewHolder holder;
1536 Header header = getItem(position);
1537 int headerType = getHeaderType(header);
1538 View view = null;
1539
1540 if (convertView == null) {
1541 holder = new HeaderViewHolder();
1542 switch (headerType) {
1543 case HEADER_TYPE_CATEGORY:
1544 view = new TextView(getContext(), null,
1545 android.R.attr.listSeparatorTextViewStyle);
1546 holder.mTitle = (TextView) view;
1547 break;
1548
1549 case HEADER_TYPE_SWITCH:
1550 view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
1551 false);
1552 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1553 holder.mTitle = (TextView)
1554 view.findViewById(com.android.internal.R.id.title);
1555 holder.mSummary = (TextView)
1556 view.findViewById(com.android.internal.R.id.summary);
1557 holder.mSwitch = (Switch) view.findViewById(R.id.switchWidget);
1558 break;
1559
1560 case HEADER_TYPE_BUTTON:
1561 view = mInflater.inflate(R.layout.preference_header_button_item, parent,
1562 false);
1563 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1564 holder.mTitle = (TextView)
1565 view.findViewById(com.android.internal.R.id.title);
1566 holder.mSummary = (TextView)
1567 view.findViewById(com.android.internal.R.id.summary);
1568 holder.mButton = (ImageButton) view.findViewById(R.id.buttonWidget);
1569 holder.mDivider = view.findViewById(R.id.divider);
1570 break;
1571
1572 case HEADER_TYPE_NORMAL:
1573 view = mInflater.inflate(
1574 R.layout.preference_header_item, parent,
1575 false);
1576 holder.mIcon = (ImageView) view.findViewById(R.id.icon);
1577 holder.mTitle = (TextView)
1578 view.findViewById(com.android.internal.R.id.title);
1579 holder.mSummary = (TextView)
1580 view.findViewById(com.android.internal.R.id.summary);
1581 break;
1582 }
1583 view.setTag(holder);
1584 } else {
1585 view = convertView;
1586 holder = (HeaderViewHolder) view.getTag();
1587 }
1588
1589 // All view fields must be updated every time, because the view may be recycled
1590 switch (headerType) {
1591 case HEADER_TYPE_CATEGORY:
1592 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1593 break;
1594
1595 case HEADER_TYPE_SWITCH:
1596 // Would need a different treatment if the main menu had more switches
1597 if (header.id == R.id.wifi_settings) {
1598 mWifiEnabler.setSwitch(holder.mSwitch);
1599 } else {
1600 mBluetoothEnabler.setSwitch(holder.mSwitch);
1601 }
1602 updateCommonHeaderView(header, holder);
1603 break;
1604
1605 case HEADER_TYPE_BUTTON:
1606 if (header.id == R.id.security_settings) {
1607 boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
1608 if (hasCert) {
1609 holder.mButton.setVisibility(View.VISIBLE);
1610 holder.mDivider.setVisibility(View.VISIBLE);
1611 boolean isManaged = mDevicePolicyManager.getDeviceOwner() != null;
1612 if (isManaged) {
1613 holder.mButton.setImageResource(R.drawable.ic_settings_about);
1614 } else {
1615 holder.mButton.setImageResource(
1616 android.R.drawable.stat_notify_error);
1617 }
1618 holder.mButton.setOnClickListener(new OnClickListener() {
1619 @Override
1620 public void onClick(View v) {
1621 Intent intent = new Intent(
1622 android.provider.Settings.ACTION_MONITORING_CERT_INFO);
1623 getContext().startActivity(intent);
1624 }
1625 });
1626 } else {
1627 holder.mButton.setVisibility(View.GONE);
1628 holder.mDivider.setVisibility(View.GONE);
1629 }
1630 }
1631 updateCommonHeaderView(header, holder);
1632 break;
1633
1634 case HEADER_TYPE_NORMAL:
1635 updateCommonHeaderView(header, holder);
1636 break;
1637 }
1638
1639 return view;
1640 }
1641
1642 private void updateCommonHeaderView(Header header, HeaderViewHolder holder) {
1643 if (header.extras != null
1644 && header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
1645 String accType = header.extras.getString(
1646 ManageAccountsSettings.KEY_ACCOUNT_TYPE);
1647 Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
1648 setHeaderIcon(holder, icon);
1649 } else {
Fabrice Di Meglio8dcea482014-03-13 12:49:01 -07001650 if (header.iconRes > 0) {
1651 holder.mIcon.setImageResource(header.iconRes);
1652 } else {
1653 holder.mIcon.setImageDrawable(null);
1654 }
1655 }
1656 if (holder.mIcon != null) {
1657 if (header.iconRes > 0) {
1658 holder.mIcon.setBackgroundResource(R.color.background_drawer_icon);
1659 } else {
1660 holder.mIcon.setBackground(null);
1661 }
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001662 }
1663 holder.mTitle.setText(header.getTitle(getContext().getResources()));
1664 CharSequence summary = header.getSummary(getContext().getResources());
1665 if (!TextUtils.isEmpty(summary)) {
1666 holder.mSummary.setVisibility(View.VISIBLE);
1667 holder.mSummary.setText(summary);
1668 } else {
1669 holder.mSummary.setVisibility(View.GONE);
1670 }
1671 }
1672
1673 private void setHeaderIcon(HeaderViewHolder holder, Drawable icon) {
1674 ViewGroup.LayoutParams lp = holder.mIcon.getLayoutParams();
1675 lp.width = getContext().getResources().getDimensionPixelSize(
1676 R.dimen.header_icon_width);
1677 lp.height = lp.width;
1678 holder.mIcon.setLayoutParams(lp);
1679 holder.mIcon.setImageDrawable(icon);
1680 }
1681
Matthew Xiea504c4d2014-02-14 16:32:32 -08001682 public void resume(Context context) {
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001683 mWifiEnabler.resume();
Matthew Xiea504c4d2014-02-14 16:32:32 -08001684 mBluetoothEnabler.resume(context);
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001685 }
1686
1687 public void pause() {
1688 mWifiEnabler.pause();
1689 mBluetoothEnabler.pause();
1690 }
1691 }
1692
1693 private void onListItemClick(ListView l, View v, int position, long id) {
1694 if (!isResumed()) {
1695 return;
1696 }
1697 Object item = mHeaderAdapter.getItem(position);
1698 if (item instanceof Header) {
Fabrice Di Meglio65027202014-02-11 15:19:46 -08001699 mSelectedHeader = (Header) item;
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001700 }
1701 }
1702
Fabrice Di Meglio263bcc82014-01-17 19:17:58 -08001703 @Override
1704 public boolean shouldUpRecreateTask(Intent targetIntent) {
1705 return super.shouldUpRecreateTask(new Intent(this, SettingsActivity.class));
1706 }
1707
1708 @Override
1709 public void onAccountsUpdated(Account[] accounts) {
1710 // TODO: watch for package upgrades to invalidate cache; see 7206643
1711 mAuthenticatorHelper.updateAuthDescriptions(this);
1712 mAuthenticatorHelper.onAccountsUpdated(this, accounts);
1713 invalidateHeaders();
1714 }
1715
1716 public static void requestHomeNotice() {
1717 sShowNoHomeNotice = true;
1718 }
1719
1720 /**
1721 * Default value for {@link Header#id Header.id} indicating that no
1722 * identifier value is set. All other values (including those below -1)
1723 * are valid.
1724 */
1725 private static final long HEADER_ID_UNDEFINED = -1;
1726
1727 /**
1728 * Description of a single Header item that the user can select.
1729 */
1730 static final class Header implements Parcelable {
1731 /**
1732 * Identifier for this header, to correlate with a new list when
1733 * it is updated. The default value is
1734 * {@link SettingsActivity#HEADER_ID_UNDEFINED}, meaning no id.
1735 * @attr ref android.R.styleable#PreferenceHeader_id
1736 */
1737 public long id = HEADER_ID_UNDEFINED;
1738
1739 /**
1740 * Resource ID of title of the header that is shown to the user.
1741 * @attr ref android.R.styleable#PreferenceHeader_title
1742 */
1743 public int titleRes;
1744
1745 /**
1746 * Title of the header that is shown to the user.
1747 * @attr ref android.R.styleable#PreferenceHeader_title
1748 */
1749 public CharSequence title;
1750
1751 /**
1752 * Resource ID of optional summary describing what this header controls.
1753 * @attr ref android.R.styleable#PreferenceHeader_summary
1754 */
1755 public int summaryRes;
1756
1757 /**
1758 * Optional summary describing what this header controls.
1759 * @attr ref android.R.styleable#PreferenceHeader_summary
1760 */
1761 public CharSequence summary;
1762
1763 /**
1764 * Optional icon resource to show for this header.
1765 * @attr ref android.R.styleable#PreferenceHeader_icon
1766 */
1767 public int iconRes;
1768
1769 /**
1770 * Full class name of the fragment to display when this header is
1771 * selected.
1772 * @attr ref android.R.styleable#PreferenceHeader_fragment
1773 */
1774 public String fragment;
1775
1776 /**
1777 * Optional arguments to supply to the fragment when it is
1778 * instantiated.
1779 */
1780 public Bundle fragmentArguments;
1781
1782 /**
1783 * Intent to launch when the preference is selected.
1784 */
1785 public Intent intent;
1786
1787 /**
1788 * Optional additional data for use by subclasses of the activity
1789 */
1790 public Bundle extras;
1791
1792 public Header() {
1793 // Empty
1794 }
1795
1796 /**
1797 * Return the currently set title. If {@link #titleRes} is set,
1798 * this resource is loaded from <var>res</var> and returned. Otherwise
1799 * {@link #title} is returned.
1800 */
1801 public CharSequence getTitle(Resources res) {
1802 if (titleRes != 0) {
1803 return res.getText(titleRes);
1804 }
1805 return title;
1806 }
1807
1808 /**
1809 * Return the currently set summary. If {@link #summaryRes} is set,
1810 * this resource is loaded from <var>res</var> and returned. Otherwise
1811 * {@link #summary} is returned.
1812 */
1813 public CharSequence getSummary(Resources res) {
1814 if (summaryRes != 0) {
1815 return res.getText(summaryRes);
1816 }
1817 return summary;
1818 }
1819
1820 @Override
1821 public int describeContents() {
1822 return 0;
1823 }
1824
1825 @Override
1826 public void writeToParcel(Parcel dest, int flags) {
1827 dest.writeLong(id);
1828 dest.writeInt(titleRes);
1829 TextUtils.writeToParcel(title, dest, flags);
1830 dest.writeInt(summaryRes);
1831 TextUtils.writeToParcel(summary, dest, flags);
1832 dest.writeInt(iconRes);
1833 dest.writeString(fragment);
1834 dest.writeBundle(fragmentArguments);
1835 if (intent != null) {
1836 dest.writeInt(1);
1837 intent.writeToParcel(dest, flags);
1838 } else {
1839 dest.writeInt(0);
1840 }
1841 dest.writeBundle(extras);
1842 }
1843
1844 public void readFromParcel(Parcel in) {
1845 id = in.readLong();
1846 titleRes = in.readInt();
1847 title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1848 summaryRes = in.readInt();
1849 summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
1850 iconRes = in.readInt();
1851 fragment = in.readString();
1852 fragmentArguments = in.readBundle();
1853 if (in.readInt() != 0) {
1854 intent = Intent.CREATOR.createFromParcel(in);
1855 }
1856 extras = in.readBundle();
1857 }
1858
1859 Header(Parcel in) {
1860 readFromParcel(in);
1861 }
1862
1863 public static final Creator<Header> CREATOR = new Creator<Header>() {
1864 public Header createFromParcel(Parcel source) {
1865 return new Header(source);
1866 }
1867 public Header[] newArray(int size) {
1868 return new Header[size];
1869 }
1870 };
1871 }
1872}