Home | History | Annotate | Download | only in location
      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