• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 com.android.tv.settings.R;
20 
21 import com.android.tv.settings.ActionBehavior;
22 import com.android.tv.settings.ActionKey;
23 import com.android.tv.settings.BaseSettingsActivity;
24 import android.accounts.Account;
25 import android.accounts.AccountManager;
26 import com.android.tv.settings.util.SettingsHelper;
27 import com.android.tv.settings.dialog.old.Action;
28 import com.android.tv.settings.dialog.old.ActionAdapter;
29 import com.android.tv.settings.device.apps.AppManagementActivity;
30 
31 import android.app.AppOpsManager;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.pm.ApplicationInfo;
35 import android.content.pm.PackageManager;
36 import android.location.LocationManager;
37 import android.os.Bundle;
38 import android.preference.Preference;
39 import android.provider.Settings;
40 import android.util.Log;
41 import java.util.ArrayList;
42 import java.util.HashMap;
43 import java.util.List;
44 
45 /**
46  * Controls location settings.
47  */
48 public class LocationActivity extends BaseSettingsActivity implements ActionAdapter.Listener {
49 
50     private static final String TAG = "LocationActivity";
51     private static final boolean DEBUG = false;
52 
53     /**
54      * Stores a BatterySipper object and records whether the sipper has been
55      * used.
56      */
57     private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
58     /**
59      * Package name of GmsCore
60      */
61     private static final String GMS_PACKAGE = "com.google.android.gms";
62     /**
63      * Class name of Google location settings
64      */
65     private static final String GOOGLE_LOCATION_SETTINGS_CLASS =
66             "com.google.android.location.settings.GoogleLocationSettingsActivity";
67     /**
68      * Account type for google accounts
69      */
70     private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
71 
72     /**
73      * The extra key whose value specifies the name of account to be operated
74      * on.
75      */
76     static final String EXTRA_KEY_ACCOUNT = "com.google.android.location.settings.extra.account";
77 
78     private SettingsHelper mHelper;
79     private String mAccountName = null;
80 
81     @Override
onCreate(Bundle savedInstanceState)82     protected void onCreate(Bundle savedInstanceState) {
83         mHelper = new SettingsHelper(getApplicationContext());
84 
85         super.onCreate(savedInstanceState);
86     }
87 
88     @Override
onPause()89     protected void onPause() {
90         super.onPause();
91     }
92 
93     @Override
onDestroy()94     protected void onDestroy() {
95         super.onDestroy();
96     }
97 
98     @Override
getInitialState()99     protected Object getInitialState() {
100         return ActionType.LOCATION_OVERVIEW;
101     }
102 
103     @Override
refreshActionList()104     protected void refreshActionList() {
105         mActions.clear();
106         switch ((ActionType) mState) {
107             case LOCATION_OVERVIEW:
108                 mActions.add(ActionType.LOCATION_STATUS.toAction(
109                         mResources, mHelper.getStatusStringFromBoolean(isLocationEnabled())));
110                 if (isLocationEnabled()) {
111                     mActions.add(ActionType.LOCATION_MODE.toAction(mResources,
112                             getString(R.string.location_mode_wifi_description), false));
113                     mActions.add(ActionType.LOCATION_RECENT_REQUESTS.toAction(mResources));
114                 }
115                 break;
116             case LOCATION_STATUS:
117                 Action locationStatusOn = ActionType.ON.toAction(mResources);
118                 locationStatusOn.setChecked(isLocationEnabled());
119                 Action locationStatusOff = ActionType.OFF.toAction(mResources);
120                 locationStatusOff.setChecked(!isLocationEnabled());
121                 mActions.add(locationStatusOn);
122                 mActions.add(locationStatusOff);
123                 break;
124             case LOCATION_RECENT_REQUESTS:
125                 mActions = getRecentRequestActions();
126                 if (mActions.size() == 0) {
127                     mActions.add(ActionType.LOCATION_NO_RECENT_REQUESTS.toAction(
128                             mResources, false));
129                 }
130                 break;
131             case LOCATION_SERVICES:
132                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE.toAction(mResources));
133                 break;
134             case LOCATION_SERVICES_GOOGLE:
135                 mActions = getAccountsActions();
136                 break;
137             case LOCATION_SERVICES_GOOGLE_SETTINGS:
138                 // TODO add on and off
139                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE_REPORTING.toAction(mResources));
140                 mActions.add(ActionType.LOCATION_SERVICES_GOOGLE_HISTORY.toAction(mResources));
141                 break;
142             case LOCATION_SERVICES_GOOGLE_REPORTING:
143             case LOCATION_SERVICES_GOOGLE_HISTORY:
144                 // TODO set checked here
145                 mActions.add(ActionType.ON.toAction(mResources));
146                 mActions.add(ActionType.OFF.toAction(mResources));
147                 break;
148             default:
149         }
150     }
151 
152     // TODO Remove this. Use our own UI
startHoloGoogleLocationServicesSettings()153     private void startHoloGoogleLocationServicesSettings() {
154         Intent i = new Intent();
155         i.setClassName(GMS_PACKAGE, GOOGLE_LOCATION_SETTINGS_CLASS);
156         startActivity(i);
157     }
158 
getAccountsActions()159     private ArrayList<Action> getAccountsActions(){
160         ArrayList<Action> result = new ArrayList<Action>();
161         Action.Builder builder = new Action.Builder();
162         Account[] googleAccounts = ((AccountManager) getSystemService(Context.ACCOUNT_SERVICE))
163                 .getAccountsByType(ACCOUNT_TYPE_GOOGLE);
164         for (Account account : googleAccounts) {
165             result.add(builder.key(account.name).title(account.name).build());
166         }
167         return result;
168     }
169 
170     /**
171      * Fills a list of applications which queried location recently within
172      * specified time. TODO: add icons
173      */
getRecentRequestActions()174     private ArrayList<Action> getRecentRequestActions() {
175         ArrayList<Action> result = new ArrayList<Action>();
176 
177         // Retrieve a location usage list from AppOps
178         AppOpsManager aoManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
179         List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
180                 new int[] {
181                         AppOpsManager.OP_MONITOR_LOCATION,
182                         AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
183                 });
184         long now = System.currentTimeMillis();
185         for (AppOpsManager.PackageOps ops : appOps) {
186             Action action = getActionFromOps(now, ops);
187             if (action != null) {
188                 result.add(action);
189             }
190         }
191 
192         return result;
193     }
194 
getActionFromOps(long now, AppOpsManager.PackageOps ops)195     private Action getActionFromOps(long now, AppOpsManager.PackageOps ops) {
196         String packageName = ops.getPackageName();
197         List<AppOpsManager.OpEntry> entries = ops.getOps();
198         boolean highBattery = false;
199         boolean normalBattery = false;
200 
201         // Earliest time for a location request to end and still be shown in
202         // list.
203         long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
204         for (AppOpsManager.OpEntry entry : entries) {
205             if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
206                 switch (entry.getOp()) {
207                     case AppOpsManager.OP_MONITOR_LOCATION:
208                         normalBattery = true;
209                         break;
210                     case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
211                         highBattery = true;
212                         break;
213                     default:
214                         break;
215                 }
216             }
217         }
218 
219         if (!highBattery && !normalBattery) {
220             if (DEBUG) {
221                 Log.v(TAG, packageName + " hadn't used location within the time interval.");
222             }
223             return null;
224         }
225 
226         Action.Builder builder = new Action.Builder();
227         // The package is fresh enough, continue.
228         try {
229             ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
230                     packageName, PackageManager.GET_META_DATA);
231             if (appInfo.uid == ops.getUid()) {
232                 builder.key(packageName)
233                         .title(getPackageManager().getApplicationLabel(appInfo).toString())
234                         .description(highBattery ? getString(R.string.location_high_battery_use)
235                                 : getString(R.string.location_low_battery_use));
236             } else if (DEBUG) {
237                 Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() +
238                         " belongs to another inactive account, ignored.");
239             }
240         } catch (PackageManager.NameNotFoundException e) {
241             Log.wtf(TAG, "Package not found: " + packageName);
242         }
243         return builder.build();
244     }
245 
isLocationEnabled()246     private boolean isLocationEnabled() {
247         return Settings.Secure.getInt(getContentResolver(),
248                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
249                 Settings.Secure.LOCATION_MODE_OFF;
250     }
251 
252     @Override
updateView()253     protected void updateView() {
254         refreshActionList();
255         switch ((ActionType) mState) {
256             case LOCATION_OVERVIEW:
257                 setView(R.string.system_location, R.string.settings_app_name,
258                         R.string.system_desc_location, R.drawable.ic_settings_location);
259                 break;
260             case LOCATION_SERVICES_GOOGLE_SETTINGS:
261                 setView(mAccountName, getPrevState() != null ?
262                         ((ActionType) getPrevState()).getTitle(mResources) : null,
263                         ((ActionType) mState).getDescription(mResources), 0);
264                 break;
265             case ON:
266             case OFF:
267             case LOCATION_SERVICES_GOOGLE_REPORTING:
268             case LOCATION_SERVICES_GOOGLE_HISTORY:
269                 break;
270             default:
271                 setView(((ActionType) mState).getTitle(mResources), getPrevState() != null ?
272                         ((ActionType) getPrevState()).getTitle(mResources) : null,
273                         ((ActionType) mState).getDescription(mResources), 0);
274                 break;
275         }
276     }
277 
278     @Override
onActionClicked(Action action)279     public void onActionClicked(Action action) {
280         // clicking on recent location access apps
281         switch ((ActionType) mState) {
282             case LOCATION_RECENT_REQUESTS:
283                 // TODO handle no recent apps
284                 String packageName = action.getKey();
285                 Intent i = AppManagementActivity.getLaunchIntent(packageName);
286                 startActivity(i);
287                 return;
288 
289             case LOCATION_SERVICES_GOOGLE:
290                 mAccountName = action.getTitle();
291                 setState(ActionType.LOCATION_SERVICES_GOOGLE_SETTINGS, true);
292                 return;
293             default:
294         }
295 
296         ActionKey<ActionType, ActionBehavior> actionKey = new ActionKey<ActionType, ActionBehavior>(
297                 ActionType.class, ActionBehavior.class, action.getKey());
298         final ActionType type = actionKey.getType();
299         switch (type) {
300             case ON:
301                 setProperty(true);
302                 goBack();
303                 break;
304             case OFF:
305                 setProperty(false);
306                 goBack();
307                 break;
308             case LOCATION_SERVICES_GOOGLE_REPORTING:
309                 startLocationReportingSettings();
310                 break;
311             case LOCATION_SERVICES_GOOGLE_HISTORY:
312                 startLocationHistorySettings();
313                 break;
314             case LOCATION_SERVICES_GOOGLE: // TODO remove this here, it should
315                                            // fall into the default once we
316                                            // figure out how to now use the
317                                            // settings in GmsCore
318                 startHoloGoogleLocationServicesSettings();
319                 break;
320             default:
321                 setState(type, true);
322                 break;
323         }
324     }
325 
326     @Override
setProperty(boolean enable)327     protected void setProperty(boolean enable) {
328         switch ((ActionType) mState) {
329             case LOCATION_STATUS:
330                 setLocationMode(enable);
331                 break;
332              default:
333         }
334     }
335 
336     // TODO Location history settings is currently in
337     // com.google.android.location.settings.LocationHistorySettingsActivity and
338     // is non exported
startLocationHistorySettings()339     private void startLocationHistorySettings() {
340     }
341 
342     // TODO Location reporting settings is currently in
343     // com.google.android.location.settings.LocationReportingSettingsActivity
344     // and is non exported
startLocationReportingSettings()345     private void startLocationReportingSettings() {
346     }
347 
setLocationMode(boolean enable)348     private void setLocationMode(boolean enable) {
349         if (enable) {
350             // TODO
351             // com.google.android.gms/com.google.android.location.network.ConfirmAlertActivity
352             // pops up when we turn this on.
353             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
354                     Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
355         } else {
356             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
357                     Settings.Secure.LOCATION_MODE_OFF);
358         }
359     }
360 }
361