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