• 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 android.app.AppOpsManager;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.content.res.Resources;
25 import android.provider.Settings;
26 import android.util.Log;
27 
28 import com.android.tv.settings.R;
29 import com.android.tv.settings.device.apps.AppManagementActivity;
30 import com.android.tv.settings.dialog.Layout;
31 import com.android.tv.settings.dialog.SettingsLayoutActivity;
32 
33 import java.util.List;
34 
35 /**
36  * Controls location settings.
37  */
38 public class LocationActivity extends SettingsLayoutActivity {
39 
40     private static final String TAG = "LocationActivity";
41     private static final boolean DEBUG = false;
42 
43     private static final int ACTION_LOCATION_ON = 1;
44     private static final int ACTION_LOCATION_OFF = 2;
45 
46     private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
47 
48     private final Layout.LayoutGetter mRecentRequestsLayoutGetter =
49             new RecentRequestsLayoutGetter();
50 
51     @Override
createLayout()52     public Layout createLayout() {
53         final Resources res = getResources();
54         return new Layout().breadcrumb(getString(R.string.header_category_personal))
55                 .add(new Layout.Header.Builder(res)
56                         .title(R.string.system_location)
57                         .icon(R.drawable.ic_settings_location)
58                         .detailedDescription(R.string.system_desc_location)
59                         .build()
60                         .add(new Layout.Header.Builder(res)
61                                 .title(R.string.location_status)
62                                 .build()
63                                 .setSelectionGroup(new Layout.SelectionGroup.Builder(2)
64                                         .add(getString(R.string.location_mode_wifi_description),
65                                                 null, ACTION_LOCATION_ON)
66                                         .add(getString(R.string.off), null, ACTION_LOCATION_OFF)
67                                         .select(isLocationEnabled() ?
68                                                 ACTION_LOCATION_ON : ACTION_LOCATION_OFF)
69                                         .build()))
70                         .add(mRecentRequestsLayoutGetter));
71     }
72 
73     private class RecentRequestsLayoutGetter extends Layout.LayoutGetter {
74 
75         @Override
get()76         public Layout get() {
77             if (isLocationEnabled()) {
78                 return new Layout()
79                         .add(getRecentRequestHeader());
80             } else {
81                 return new Layout();
82             }
83         }
84     }
85 
86     @Override
onActionClicked(Layout.Action action)87     public void onActionClicked(Layout.Action action) {
88         switch (action.getId()) {
89             case ACTION_LOCATION_ON:
90                 setLocationMode(true);
91                 break;
92             case ACTION_LOCATION_OFF:
93                 setLocationMode(false);
94                 break;
95             default:
96                 final Intent intent = action.getIntent();
97                 if (intent != null) {
98                     startActivity(intent);
99                 }
100         }
101     }
102 
103     /**
104      * Fills a list of applications which queried location recently within
105      * specified time. TODO: add icons
106      */
getRecentRequestHeader()107     private Layout.Header getRecentRequestHeader() {
108         final Layout.Header header = new Layout.Header.Builder(getResources())
109                 .title(R.string.location_category_recent_location_requests)
110                 .build();
111 
112         // Retrieve a location usage list from AppOps
113         AppOpsManager aoManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
114         List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
115                 new int[] {
116                         AppOpsManager.OP_MONITOR_LOCATION,
117                         AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
118                 });
119         long now = System.currentTimeMillis();
120         for (AppOpsManager.PackageOps ops : appOps) {
121             Layout.Action action = getActionFromOps(now, ops);
122             if (action != null) {
123                 header.add(action);
124             }
125         }
126 
127         return header;
128     }
129 
getActionFromOps(long now, AppOpsManager.PackageOps ops)130     private Layout.Action getActionFromOps(long now, AppOpsManager.PackageOps ops) {
131         String packageName = ops.getPackageName();
132         List<AppOpsManager.OpEntry> entries = ops.getOps();
133         boolean highBattery = false;
134         boolean normalBattery = false;
135 
136         // Earliest time for a location request to end and still be shown in
137         // list.
138         long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
139         for (AppOpsManager.OpEntry entry : entries) {
140             if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
141                 switch (entry.getOp()) {
142                     case AppOpsManager.OP_MONITOR_LOCATION:
143                         normalBattery = true;
144                         break;
145                     case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
146                         highBattery = true;
147                         break;
148                     default:
149                         break;
150                 }
151             }
152         }
153 
154         if (!highBattery && !normalBattery) {
155             if (DEBUG) {
156                 Log.v(TAG, packageName + " hadn't used location within the time interval.");
157             }
158             return null;
159         }
160 
161         Layout.Action.Builder builder = new Layout.Action.Builder(getResources(),
162                 AppManagementActivity.getLaunchIntent(packageName));
163         // The package is fresh enough, continue.
164         try {
165             ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
166                     packageName, PackageManager.GET_META_DATA);
167             if (appInfo.uid == ops.getUid()) {
168                 builder.title(getPackageManager().getApplicationLabel(appInfo).toString())
169                         .description(highBattery ? getString(R.string.location_high_battery_use)
170                                 : getString(R.string.location_low_battery_use));
171             } else if (DEBUG) {
172                 Log.v(TAG, "package " + packageName + " with Uid " + ops.getUid() +
173                         " belongs to another inactive account, ignored.");
174             }
175         } catch (PackageManager.NameNotFoundException e) {
176             Log.wtf(TAG, "Package not found: " + packageName);
177         }
178         return builder.build();
179     }
180 
isLocationEnabled()181     private boolean isLocationEnabled() {
182         return Settings.Secure.getInt(getContentResolver(),
183                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
184                 Settings.Secure.LOCATION_MODE_OFF;
185     }
186 
setLocationMode(boolean enable)187     private void setLocationMode(boolean enable) {
188         if (enable) {
189             // TODO
190             // com.google.android.gms/com.google.android.location.network.ConfirmAlertActivity
191             // pops up when we turn this on.
192             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
193                     Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
194         } else {
195             Settings.Secure.putInt(getContentResolver(), Settings.Secure.LOCATION_MODE,
196                     Settings.Secure.LOCATION_MODE_OFF);
197         }
198     }
199 }
200