1 /* 2 * Copyright (C) 2015 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.tv.settings.system; 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.LocationManager; 24 import android.os.BatteryManager; 25 import android.os.Bundle; 26 import android.os.UserManager; 27 import android.provider.Settings; 28 import android.support.v7.preference.ListPreference; 29 import android.support.v7.preference.Preference; 30 import android.support.v7.preference.PreferenceCategory; 31 import android.support.v7.preference.PreferenceGroup; 32 import android.support.v7.preference.PreferenceScreen; 33 import android.text.TextUtils; 34 import android.util.Log; 35 36 import com.android.internal.logging.nano.MetricsProto; 37 import com.android.settingslib.location.RecentLocationApps; 38 import com.android.tv.settings.R; 39 import com.android.tv.settings.SettingsPreferenceFragment; 40 import com.android.tv.settings.device.apps.AppManagementFragment; 41 42 import java.util.ArrayList; 43 import java.util.Comparator; 44 import java.util.List; 45 46 /** 47 * The location settings screen in TV settings. 48 */ 49 public class LocationFragment extends SettingsPreferenceFragment implements 50 Preference.OnPreferenceChangeListener { 51 52 private static final String TAG = "LocationFragment"; 53 54 private static final String LOCATION_MODE_WIFI = "wifi"; 55 private static final String LOCATION_MODE_OFF = "off"; 56 57 private static final String KEY_LOCATION_MODE = "locationMode"; 58 59 private static final String MODE_CHANGING_ACTION = 60 "com.android.settings.location.MODE_CHANGING"; 61 private static final String CURRENT_MODE_KEY = "CURRENT_MODE"; 62 private static final String NEW_MODE_KEY = "NEW_MODE"; 63 64 private ListPreference mLocationMode; 65 66 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 67 @Override 68 public void onReceive(Context context, Intent intent) { 69 if (Log.isLoggable(TAG, Log.DEBUG)) { 70 Log.d(TAG, "Received location mode change intent: " + intent); 71 } 72 refreshLocationMode(); 73 } 74 }; 75 newInstance()76 public static LocationFragment newInstance() { 77 return new LocationFragment(); 78 } 79 80 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)81 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 82 final Context themedContext = getPreferenceManager().getContext(); 83 final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen( 84 themedContext); 85 screen.setTitle(R.string.system_location); 86 87 mLocationMode = new ListPreference(themedContext); 88 screen.addPreference(mLocationMode); 89 mLocationMode.setKey(KEY_LOCATION_MODE); 90 mLocationMode.setPersistent(false); 91 mLocationMode.setTitle(R.string.location_status); 92 mLocationMode.setDialogTitle(R.string.location_status); 93 mLocationMode.setSummary("%s"); 94 mLocationMode.setEntries(new CharSequence[] { 95 getString(R.string.location_mode_wifi_description), 96 getString(R.string.off) 97 }); 98 mLocationMode.setEntryValues(new CharSequence[] { 99 LOCATION_MODE_WIFI, 100 LOCATION_MODE_OFF 101 }); 102 mLocationMode.setOnPreferenceChangeListener(this); 103 104 final UserManager um = UserManager.get(getContext()); 105 mLocationMode.setEnabled(!um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)); 106 107 final PreferenceCategory recentRequests = new PreferenceCategory(themedContext); 108 screen.addPreference(recentRequests); 109 recentRequests.setTitle(R.string.location_category_recent_location_requests); 110 recentRequests.setLayoutResource(R.layout.preference_category_compact_layout); 111 112 List<RecentLocationApps.Request> recentLocationRequests = 113 new RecentLocationApps(themedContext).getAppList(); 114 List<Preference> recentLocationPrefs = new ArrayList<>(recentLocationRequests.size()); 115 for (final RecentLocationApps.Request request : recentLocationRequests) { 116 Preference pref = new Preference(themedContext); 117 pref.setIcon(request.icon); 118 pref.setTitle(request.label); 119 // Most Android TV devices don't have built-in batteries and we ONLY show "High/Low 120 // battery use" for devices with built-in batteries when they are not plugged-in. 121 final BatteryManager batteryManager = (BatteryManager) getContext() 122 .getSystemService(Context.BATTERY_SERVICE); 123 if (batteryManager != null && !batteryManager.isCharging()) { 124 if (request.isHighBattery) { 125 pref.setSummary(R.string.location_high_battery_use); 126 } else { 127 pref.setSummary(R.string.location_low_battery_use); 128 } 129 } 130 pref.setFragment(AppManagementFragment.class.getName()); 131 AppManagementFragment.prepareArgs(pref.getExtras(), request.packageName); 132 recentLocationPrefs.add(pref); 133 } 134 135 if (recentLocationRequests.size() > 0) { 136 addPreferencesSorted(recentLocationPrefs, recentRequests); 137 } else { 138 // If there's no item to display, add a "No recent apps" item. 139 Preference banner = new Preference(themedContext); 140 banner.setTitle(R.string.location_no_recent_apps); 141 banner.setSelectable(false); 142 recentRequests.addPreference(banner); 143 } 144 145 // TODO: are location services relevant on TV? 146 147 setPreferenceScreen(screen); 148 } 149 150 // When selecting the location preference, LeanbackPreferenceFragment 151 // creates an inner view with the selection options; that's when we want to 152 // register our receiver, bacause from now on user can change the location 153 // providers. 154 @Override onCreate(Bundle savedInstanceState)155 public void onCreate(Bundle savedInstanceState) { 156 super.onCreate(savedInstanceState); 157 getActivity().registerReceiver(mReceiver, 158 new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); 159 refreshLocationMode(); 160 } 161 162 @Override onDestroy()163 public void onDestroy() { 164 super.onDestroy(); 165 getActivity().unregisterReceiver(mReceiver); 166 } 167 addPreferencesSorted(List<Preference> prefs, PreferenceGroup container)168 private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) { 169 // If there's some items to display, sort the items and add them to the container. 170 prefs.sort(Comparator.comparing(lhs -> lhs.getTitle().toString())); 171 for (Preference entry : prefs) { 172 container.addPreference(entry); 173 } 174 } 175 176 @Override onPreferenceChange(Preference preference, Object newValue)177 public boolean onPreferenceChange(Preference preference, Object newValue) { 178 if (TextUtils.equals(preference.getKey(), KEY_LOCATION_MODE)) { 179 int mode = Settings.Secure.LOCATION_MODE_OFF; 180 if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_WIFI)) { 181 mode = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY; 182 } else if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_OFF)) { 183 mode = Settings.Secure.LOCATION_MODE_OFF; 184 } else { 185 Log.wtf(TAG, "Tried to set unknown location mode!"); 186 } 187 188 writeLocationMode(mode); 189 refreshLocationMode(); 190 } 191 return true; 192 } 193 writeLocationMode(int mode)194 private void writeLocationMode(int mode) { 195 int currentMode = Settings.Secure.getInt(getActivity().getContentResolver(), 196 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF); 197 Intent intent = new Intent(MODE_CHANGING_ACTION); 198 intent.putExtra(CURRENT_MODE_KEY, currentMode); 199 intent.putExtra(NEW_MODE_KEY, mode); 200 getActivity().sendBroadcast(intent, android.Manifest.permission.WRITE_SECURE_SETTINGS); 201 Settings.Secure.putInt(getActivity().getContentResolver(), 202 Settings.Secure.LOCATION_MODE, mode); 203 } 204 refreshLocationMode()205 private void refreshLocationMode() { 206 if (mLocationMode == null) { 207 return; 208 } 209 final int mode = Settings.Secure.getInt(getActivity().getContentResolver(), 210 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF); 211 if (mode == Settings.Secure.LOCATION_MODE_HIGH_ACCURACY 212 || mode == Settings.Secure.LOCATION_MODE_BATTERY_SAVING) { 213 mLocationMode.setValue(LOCATION_MODE_WIFI); 214 } else if (mode == Settings.Secure.LOCATION_MODE_OFF) { 215 mLocationMode.setValue(LOCATION_MODE_OFF); 216 } else if (mode == Settings.Secure.LOCATION_MODE_SENSORS_ONLY) { 217 writeLocationMode(Settings.Secure.LOCATION_MODE_OFF); 218 mLocationMode.setValue(LOCATION_MODE_OFF); 219 } else { 220 Log.d(TAG, "Unknown location mode: " + mode); 221 } 222 } 223 224 @Override getMetricsCategory()225 public int getMetricsCategory() { 226 return MetricsProto.MetricsEvent.LOCATION; 227 } 228 } 229