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