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.Bundle;
25 import android.os.UserManager;
26 import android.provider.Settings;
27 import android.support.v17.preference.LeanbackPreferenceFragment;
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.settingslib.location.RecentLocationApps;
37 import com.android.tv.settings.R;
38 import com.android.tv.settings.device.apps.AppManagementFragment;
39 
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.Comparator;
43 import java.util.List;
44 
45 public class LocationFragment extends LeanbackPreferenceFragment implements
46         Preference.OnPreferenceChangeListener {
47 
48     private static final String TAG = "LocationFragment";
49 
50     private static final String LOCATION_MODE_WIFI = "wifi";
51     private static final String LOCATION_MODE_OFF = "off";
52 
53     private static final String KEY_LOCATION_MODE = "locationMode";
54 
55     private static final String MODE_CHANGING_ACTION =
56             "com.android.settings.location.MODE_CHANGING";
57     private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
58     private static final String NEW_MODE_KEY = "NEW_MODE";
59 
60     private ListPreference mLocationMode;
61 
62     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
63         @Override
64         public void onReceive(Context context, Intent intent) {
65             if (Log.isLoggable(TAG, Log.DEBUG)) {
66                 Log.d(TAG, "Received location mode change intent: " + intent);
67             }
68             refreshLocationMode();
69         }
70     };
71 
newInstance()72     public static LocationFragment newInstance() {
73         return new LocationFragment();
74     }
75 
76     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)77     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
78         final Context themedContext = getPreferenceManager().getContext();
79         final PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(
80                 themedContext);
81         screen.setTitle(R.string.system_location);
82 
83         mLocationMode = new ListPreference(themedContext);
84         screen.addPreference(mLocationMode);
85         mLocationMode.setKey(KEY_LOCATION_MODE);
86         mLocationMode.setPersistent(false);
87         mLocationMode.setTitle(R.string.location_status);
88         mLocationMode.setDialogTitle(R.string.location_status);
89         mLocationMode.setSummary("%s");
90         mLocationMode.setEntries(new CharSequence[] {
91                 getString(R.string.location_mode_wifi_description),
92                 getString(R.string.off)
93         });
94         mLocationMode.setEntryValues(new CharSequence[] {
95                 LOCATION_MODE_WIFI,
96                 LOCATION_MODE_OFF
97         });
98         mLocationMode.setOnPreferenceChangeListener(this);
99 
100         final UserManager um = UserManager.get(getContext());
101         mLocationMode.setEnabled(!um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION));
102 
103         final PreferenceCategory recentRequests = new PreferenceCategory(themedContext);
104         screen.addPreference(recentRequests);
105         recentRequests.setTitle(R.string.location_category_recent_location_requests);
106 
107         List<RecentLocationApps.Request> recentLocationRequests =
108                 new RecentLocationApps(themedContext).getAppList();
109         List<Preference> recentLocationPrefs = new ArrayList<>(recentLocationRequests.size());
110         for (final RecentLocationApps.Request request : recentLocationRequests) {
111             Preference pref = new Preference(themedContext);
112             pref.setIcon(request.icon);
113             pref.setTitle(request.label);
114             if (request.isHighBattery) {
115                 pref.setSummary(R.string.location_high_battery_use);
116             } else {
117                 pref.setSummary(R.string.location_low_battery_use);
118             }
119             pref.setFragment(AppManagementFragment.class.getName());
120             AppManagementFragment.prepareArgs(pref.getExtras(), request.packageName);
121             recentLocationPrefs.add(pref);
122         }
123 
124         if (recentLocationRequests.size() > 0) {
125             addPreferencesSorted(recentLocationPrefs, recentRequests);
126         } else {
127             // If there's no item to display, add a "No recent apps" item.
128             Preference banner = new Preference(themedContext);
129             banner.setTitle(R.string.location_no_recent_apps);
130             banner.setSelectable(false);
131             recentRequests.addPreference(banner);
132         }
133 
134         // TODO: are location services relevant on TV?
135 
136         setPreferenceScreen(screen);
137     }
138 
139     @Override
onStart()140     public void onStart() {
141         super.onStart();
142         IntentFilter filter = new IntentFilter();
143         filter.addAction(LocationManager.MODE_CHANGED_ACTION);
144         getActivity().registerReceiver(mReceiver, filter);
145         refreshLocationMode();
146     }
147 
148     @Override
onStop()149     public void onStop() {
150         super.onStop();
151         try {
152             getActivity().unregisterReceiver(mReceiver);
153         } catch (RuntimeException e) {
154             // Ignore exceptions caused by race condition
155         }
156     }
157 
addPreferencesSorted(List<Preference> prefs, PreferenceGroup container)158     private void addPreferencesSorted(List<Preference> prefs, PreferenceGroup container) {
159         // If there's some items to display, sort the items and add them to the container.
160         Collections.sort(prefs, new Comparator<Preference>() {
161             @Override
162             public int compare(Preference lhs, Preference rhs) {
163                 return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
164             }
165         });
166         for (Preference entry : prefs) {
167             container.addPreference(entry);
168         }
169     }
170 
171     @Override
onPreferenceChange(Preference preference, Object newValue)172     public boolean onPreferenceChange(Preference preference, Object newValue) {
173         if (TextUtils.equals(preference.getKey(), KEY_LOCATION_MODE)) {
174             int mode = Settings.Secure.LOCATION_MODE_OFF;
175             if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_WIFI)) {
176                 // TODO
177                 // com.google.android.gms/com.google.android.location.network.ConfirmAlertActivity
178                 // pops up when we turn this on.
179                 mode = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
180             } else if (TextUtils.equals((CharSequence) newValue, LOCATION_MODE_OFF)) {
181                 mode = Settings.Secure.LOCATION_MODE_OFF;
182             } else {
183                 Log.wtf(TAG, "Tried to set unknown location mode!");
184             }
185 
186             int currentMode = Settings.Secure.getInt(getActivity().getContentResolver(),
187                     Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
188             Intent intent = new Intent(MODE_CHANGING_ACTION);
189             intent.putExtra(CURRENT_MODE_KEY, currentMode);
190             intent.putExtra(NEW_MODE_KEY, mode);
191             getActivity().sendBroadcast(intent, android.Manifest.permission.WRITE_SECURE_SETTINGS);
192             Settings.Secure.putInt(getActivity().getContentResolver(),
193                     Settings.Secure.LOCATION_MODE, mode);
194 
195             refreshLocationMode();
196         }
197         return true;
198     }
199 
refreshLocationMode()200     private void refreshLocationMode() {
201         if (mLocationMode == null) {
202             return;
203         }
204         final int mode = Settings.Secure.getInt(getActivity().getContentResolver(),
205                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
206         if (mode == Settings.Secure.LOCATION_MODE_HIGH_ACCURACY) {
207             mLocationMode.setValue(LOCATION_MODE_WIFI);
208         } else if (mode == Settings.Secure.LOCATION_MODE_OFF) {
209             mLocationMode.setValue(LOCATION_MODE_OFF);
210         } else {
211             Log.d(TAG, "Unknown location mode: " + mode);
212         }
213     }
214 }
215