1 /* 2 * Copyright (C) 2011 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 17 package com.android.settings.location; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.location.SettingInjectorService; 24 import android.os.Bundle; 25 import android.preference.Preference; 26 import android.preference.PreferenceCategory; 27 import android.preference.PreferenceGroup; 28 import android.preference.PreferenceScreen; 29 import android.util.Log; 30 import android.widget.Switch; 31 32 import com.android.settings.R; 33 import com.android.settings.SettingsActivity; 34 import com.android.settings.widget.SwitchBar; 35 36 import java.util.Collections; 37 import java.util.Comparator; 38 import java.util.List; 39 40 /** 41 * Location access settings. 42 */ 43 public class LocationSettings extends LocationSettingsBase 44 implements SwitchBar.OnSwitchChangeListener { 45 46 private static final String TAG = "LocationSettings"; 47 48 /** Key for preference screen "Mode" */ 49 private static final String KEY_LOCATION_MODE = "location_mode"; 50 /** Key for preference category "Recent location requests" */ 51 private static final String KEY_RECENT_LOCATION_REQUESTS = "recent_location_requests"; 52 /** Key for preference category "Location services" */ 53 private static final String KEY_LOCATION_SERVICES = "location_services"; 54 55 private SwitchBar mSwitchBar; 56 private Switch mSwitch; 57 private boolean mValidListener = false; 58 private Preference mLocationMode; 59 private PreferenceCategory mCategoryRecentLocationRequests; 60 /** Receives UPDATE_INTENT */ 61 private BroadcastReceiver mReceiver; 62 private SettingsInjector injector; 63 64 @Override 65 public void onActivityCreated(Bundle savedInstanceState) { 66 super.onActivityCreated(savedInstanceState); 67 68 final SettingsActivity activity = (SettingsActivity) getActivity(); 69 70 mSwitchBar = activity.getSwitchBar(); 71 mSwitch = mSwitchBar.getSwitch(); 72 mSwitchBar.show(); 73 } 74 75 @Override 76 public void onDestroyView() { 77 super.onDestroyView(); 78 mSwitchBar.hide(); 79 } 80 81 @Override 82 public void onResume() { 83 super.onResume(); 84 createPreferenceHierarchy(); 85 if (!mValidListener) { 86 mSwitchBar.addOnSwitchChangeListener(this); 87 mValidListener = true; 88 } 89 } 90 91 @Override 92 public void onPause() { 93 try { 94 getActivity().unregisterReceiver(mReceiver); 95 } catch (RuntimeException e) { 96 // Ignore exceptions caused by race condition 97 if (Log.isLoggable(TAG, Log.VERBOSE)) { 98 Log.v(TAG, "Swallowing " + e); 99 } 100 } 101 if (mValidListener) { 102 mSwitchBar.removeOnSwitchChangeListener(this); 103 mValidListener = false; 104 } 105 super.onPause(); 106 } 107 108 private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) { 109 // If there's some items to display, sort the items and add them to the container. 110 Collections.sort(prefs, new Comparator<Preference>() { 111 @Override 112 public int compare(Preference lhs, Preference rhs) { 113 return lhs.getTitle().toString().compareTo(rhs.getTitle().toString()); 114 } 115 }); 116 for (Preference entry : prefs) { 117 container.addPreference(entry); 118 } 119 } 120 121 private PreferenceScreen createPreferenceHierarchy() { 122 final SettingsActivity activity = (SettingsActivity) getActivity(); 123 PreferenceScreen root = getPreferenceScreen(); 124 if (root != null) { 125 root.removeAll(); 126 } 127 addPreferencesFromResource(R.xml.location_settings); 128 root = getPreferenceScreen(); 129 130 mLocationMode = root.findPreference(KEY_LOCATION_MODE); 131 mLocationMode.setOnPreferenceClickListener( 132 new Preference.OnPreferenceClickListener() { 133 @Override 134 public boolean onPreferenceClick(Preference preference) { 135 activity.startPreferencePanel( 136 LocationMode.class.getName(), null, 137 R.string.location_mode_screen_title, null, LocationSettings.this, 138 0); 139 return true; 140 } 141 }); 142 143 mCategoryRecentLocationRequests = 144 (PreferenceCategory) root.findPreference(KEY_RECENT_LOCATION_REQUESTS); 145 RecentLocationApps recentApps = new RecentLocationApps(activity); 146 List<Preference> recentLocationRequests = recentApps.getAppList(); 147 if (recentLocationRequests.size() > 0) { 148 addPreferencesSorted(recentLocationRequests, mCategoryRecentLocationRequests); 149 } else { 150 // If there's no item to display, add a "No recent apps" item. 151 Preference banner = new Preference(activity); 152 banner.setLayoutResource(R.layout.location_list_no_item); 153 banner.setTitle(R.string.location_no_recent_apps); 154 banner.setSelectable(false); 155 mCategoryRecentLocationRequests.addPreference(banner); 156 } 157 158 addLocationServices(activity, root); 159 160 refreshLocationMode(); 161 return root; 162 } 163 164 /** 165 * Add the settings injected by external apps into the "App Settings" category. Hides the 166 * category if there are no injected settings. 167 * 168 * Reloads the settings whenever receives 169 * {@link SettingInjectorService#ACTION_INJECTED_SETTING_CHANGED}. 170 */ 171 private void addLocationServices(Context context, PreferenceScreen root) { 172 PreferenceCategory categoryLocationServices = 173 (PreferenceCategory) root.findPreference(KEY_LOCATION_SERVICES); 174 injector = new SettingsInjector(context); 175 List<Preference> locationServices = injector.getInjectedSettings(); 176 177 mReceiver = new BroadcastReceiver() { 178 @Override 179 public void onReceive(Context context, Intent intent) { 180 if (Log.isLoggable(TAG, Log.DEBUG)) { 181 Log.d(TAG, "Received settings change intent: " + intent); 182 } 183 injector.reloadStatusMessages(); 184 } 185 }; 186 187 IntentFilter filter = new IntentFilter(); 188 filter.addAction(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED); 189 context.registerReceiver(mReceiver, filter); 190 191 if (locationServices.size() > 0) { 192 addPreferencesSorted(locationServices, categoryLocationServices); 193 } else { 194 // If there's no item to display, remove the whole category. 195 root.removePreference(categoryLocationServices); 196 } 197 } 198 199 @Override 200 public int getHelpResource() { 201 return R.string.help_url_location_access; 202 } 203 204 @Override 205 public void onModeChanged(int mode, boolean restricted) { 206 switch (mode) { 207 case android.provider.Settings.Secure.LOCATION_MODE_OFF: 208 mLocationMode.setSummary(R.string.location_mode_location_off_title); 209 break; 210 case android.provider.Settings.Secure.LOCATION_MODE_SENSORS_ONLY: 211 mLocationMode.setSummary(R.string.location_mode_sensors_only_title); 212 break; 213 case android.provider.Settings.Secure.LOCATION_MODE_BATTERY_SAVING: 214 mLocationMode.setSummary(R.string.location_mode_battery_saving_title); 215 break; 216 case android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY: 217 mLocationMode.setSummary(R.string.location_mode_high_accuracy_title); 218 break; 219 default: 220 break; 221 } 222 223 // Restricted user can't change the location mode, so disable the master switch. But in some 224 // corner cases, the location might still be enabled. In such case the master switch should 225 // be disabled but checked. 226 boolean enabled = (mode != android.provider.Settings.Secure.LOCATION_MODE_OFF); 227 // Disable the whole switch bar instead of the switch itself. If we disabled the switch 228 // only, it would be re-enabled again if the switch bar is not disabled. 229 mSwitchBar.setEnabled(!restricted); 230 mLocationMode.setEnabled(enabled && !restricted); 231 mCategoryRecentLocationRequests.setEnabled(enabled); 232 233 if (enabled != mSwitch.isChecked()) { 234 // set listener to null so that that code below doesn't trigger onCheckedChanged() 235 if (mValidListener) { 236 mSwitchBar.removeOnSwitchChangeListener(this); 237 } 238 mSwitch.setChecked(enabled); 239 if (mValidListener) { 240 mSwitchBar.addOnSwitchChangeListener(this); 241 } 242 } 243 // As a safety measure, also reloads on location mode change to ensure the settings are 244 // up-to-date even if an affected app doesn't send the setting changed broadcast. 245 injector.reloadStatusMessages(); 246 } 247 248 /** 249 * Listens to the state change of the location master switch. 250 */ 251 @Override 252 public void onSwitchChanged(Switch switchView, boolean isChecked) { 253 if (isChecked) { 254 setLocationMode(android.provider.Settings.Secure.LOCATION_MODE_HIGH_ACCURACY); 255 } else { 256 setLocationMode(android.provider.Settings.Secure.LOCATION_MODE_OFF); 257 } 258 } 259 } 260