blob: 96b9589afe2a20507406322d3c155c3a9e214e83 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.tv.settings.system;
import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted;
import android.app.tvsettings.TvSettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Process;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Keep;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settingslib.location.RecentLocationApps;
import com.android.tv.settings.R;
import com.android.tv.settings.SettingsPreferenceFragment;
import com.android.tv.settings.device.apps.AppManagementFragment;
import com.android.tv.settings.overlay.FlavorUtils;
import com.android.tv.twopanelsettings.SummaryListPreference;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* The location settings screen in TV settings.
*/
@Keep
public class LocationFragment extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener {
private static final String TAG = "LocationFragment";
private static final String LOCATION_MODE_WIFI = "wifi";
private static final String LOCATION_MODE_OFF = "off";
private static final String KEY_LOCATION_MODE = "locationMode";
private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan";
private static final String MODE_CHANGING_ACTION =
"com.android.settings.location.MODE_CHANGING";
private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
private static final String NEW_MODE_KEY = "NEW_MODE";
private SummaryListPreference mLocationMode;
private SwitchPreference mAlwaysScan;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Received location mode change intent: " + intent);
}
refreshLocationMode();
}
};
public static LocationFragment newInstance() {
return new LocationFragment();
}
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
final Context themedContext = getPreferenceManager().getContext();
final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(
themedContext);
screen.setTitle(R.string.system_location);
mLocationMode = new SummaryListPreference(themedContext);
screen.addPreference(mLocationMode);
mLocationMode.setKey(KEY_LOCATION_MODE);
mLocationMode.setPersistent(false);
mLocationMode.setTitle(R.string.location_status);
mLocationMode.setDialogTitle(R.string.location_status);
mLocationMode.setSummary("%s");
mLocationMode.setEntries(new CharSequence[] {
getString(R.string.location_mode_wifi_description),
getString(R.string.off)
});
mLocationMode.setEntryValues(new CharSequence[] {
LOCATION_MODE_WIFI,
LOCATION_MODE_OFF
});
mLocationMode.setSummaries(new CharSequence[] {
getString(R.string.system_location_summary),
null
});
mLocationMode.setOnPreferenceChangeListener(this);
mLocationMode.setOnPreferenceClickListener(
preference -> {
logEntrySelected(TvSettingsEnums.PRIVACY_LOCATION_STATUS);
return false;
});
final UserManager um = UserManager.get(getContext());
mLocationMode.setEnabled(!um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION));
if (FlavorUtils.isTwoPanel(getContext())) {
mAlwaysScan = new SwitchPreference(themedContext);
mAlwaysScan.setKey(KEY_WIFI_ALWAYS_SCAN);
mAlwaysScan.setTitle(R.string.wifi_setting_always_scan);
mAlwaysScan.setSummary(R.string.wifi_setting_always_scan_context);
screen.addPreference(mAlwaysScan);
mAlwaysScan.setOnPreferenceChangeListener(this);
}
final PreferenceCategory recentRequests = new PreferenceCategory(themedContext);
screen.addPreference(recentRequests);
recentRequests.setTitle(R.string.location_category_recent_location_requests);
List<RecentLocationApps.Request> recentLocationRequests =
new RecentLocationApps(themedContext).getAppList(true);
List<Preference> recentLocationPrefs = new ArrayList<>(recentLocationRequests.size());
for (final RecentLocationApps.Request request : recentLocationRequests) {
Preference pref = new Preference(themedContext);
pref.setIcon(request.icon);
pref.setTitle(request.label);
// Most Android TV devices don't have built-in batteries and we ONLY show "High/Low
// battery use" for devices with built-in batteries when they are not plugged-in.
final BatteryManager batteryManager = (BatteryManager) getContext()
.getSystemService(Context.BATTERY_SERVICE);
if (batteryManager != null && !batteryManager.isCharging()) {
if (request.isHighBattery) {
pref.setSummary(R.string.location_high_battery_use);
} else {
pref.setSummary(R.string.location_low_battery_use);
}
}
pref.setOnPreferenceClickListener(
preference -> {
logEntrySelected(TvSettingsEnums.PRIVACY_LOCATION_REQUESTED_APP);
return false;
});
pref.setFragment(AppManagementFragment.class.getName());
AppManagementFragment.prepareArgs(pref.getExtras(), request.packageName);
recentLocationPrefs.add(pref);
}
if (recentLocationRequests.size() > 0) {
addPreferencesSorted(recentLocationPrefs, recentRequests);
} else {
// If there's no item to display, add a "No recent apps" item.
Preference banner = new Preference(themedContext);
banner.setTitle(R.string.location_no_recent_apps);
banner.setSelectable(false);
recentRequests.addPreference(banner);
}
// TODO: are location services relevant on TV?
setPreferenceScreen(screen);
}
// When selecting the location preference, LeanbackPreferenceFragment
// creates an inner view with the selection options; that's when we want to
// register our receiver, bacause from now on user can change the location
// providers.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().registerReceiver(mReceiver,
new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
refreshLocationMode();
}
@Override
public void onResume() {
super.onResume();
updateConnectivity();
}
@Override
public void onDestroy() {
super.onDestroy();
getActivity().unregisterReceiver(mReceiver);
}
private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
// If there's some items to display, sort the items and add them to the container.
prefs.sort(Comparator.comparing(lhs -> lhs.getTitle().toString()));
for (Preference entry : prefs) {
container.addPreference(entry);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (TextUtils.equals(preference.getKey(), KEY_LOCATION_MODE)) {
int mode = Settings.Secure.LOCATION_MODE_OFF;
if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_WIFI)) {
mode = Settings.Secure.LOCATION_MODE_ON;
logEntrySelected(TvSettingsEnums.PRIVACY_LOCATION_STATUS_USE_WIFI);
} else if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_OFF)) {
mode = Settings.Secure.LOCATION_MODE_OFF;
logEntrySelected(TvSettingsEnums.PRIVACY_LOCATION_STATUS_OFF);
} else {
Log.wtf(TAG, "Tried to set unknown location mode!");
}
writeLocationMode(mode);
refreshLocationMode();
}
return true;
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), KEY_WIFI_ALWAYS_SCAN)) {
Settings.Global.putInt(getActivity().getContentResolver(),
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
mAlwaysScan.isChecked() ? 1 : 0);
logToggleInteracted(
TvSettingsEnums.PRIVACY_LOCATION_ALWAYS_SCANNING_NETWORKS,
mAlwaysScan.isChecked());
updateConnectivity();
}
return super.onPreferenceTreeClick(preference);
}
private void writeLocationMode(int mode) {
int currentMode = Settings.Secure.getInt(getActivity().getContentResolver(),
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
Intent intent = new Intent(MODE_CHANGING_ACTION);
intent.putExtra(CURRENT_MODE_KEY, currentMode);
intent.putExtra(NEW_MODE_KEY, mode);
getActivity().sendBroadcast(intent, android.Manifest.permission.WRITE_SECURE_SETTINGS);
getActivity().getSystemService(LocationManager.class).setLocationEnabledForUser(
mode != Settings.Secure.LOCATION_MODE_OFF,
Process.myUserHandle());
}
private void refreshLocationMode() {
if (mLocationMode == null) {
return;
}
if (getActivity().getSystemService(LocationManager.class).isLocationEnabled()) {
mLocationMode.setValue(LOCATION_MODE_WIFI);
} else {
mLocationMode.setValue(LOCATION_MODE_OFF);
}
}
private void updateConnectivity() {
if (FlavorUtils.isTwoPanel(getContext())) {
int scanAlwaysAvailable = Settings.Global.getInt(getActivity().getContentResolver(),
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
mAlwaysScan.setChecked(scanAlwaysAvailable == 1);
}
}
@Override
protected int getPageId() {
return TvSettingsEnums.PRIVACY_LOCATION;
}
}