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