LocationController.java revision c750c1fb83fbdec895e236dda7207db4da14ec49
1/* 2 * Copyright (C) 2008 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 17package com.android.systemui.statusbar.policy; 18 19import android.app.AppOpsManager; 20import android.app.StatusBarManager; 21import android.content.BroadcastReceiver; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.database.ContentObserver; 27import android.location.LocationManager; 28import android.os.Handler; 29import android.os.UserManager; 30import android.provider.Settings; 31 32import com.android.systemui.R; 33 34import java.util.ArrayList; 35import java.util.List; 36 37/** 38 * A controller to manage changes of location related states and update the views accordingly. 39 */ 40public class LocationController extends BroadcastReceiver { 41 // The name of the placeholder corresponding to the location request status icon. 42 // This string corresponds to config_statusBarIcons in core/res/res/values/config.xml. 43 private static final String LOCATION_STATUS_ICON_PLACEHOLDER = "location"; 44 private static final int LOCATION_STATUS_ICON_ID 45 = R.drawable.stat_sys_device_access_location_found; 46 47 private static final int[] mHighPowerRequestAppOpArray 48 = new int[] {AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION}; 49 50 private Context mContext; 51 52 private AppOpsManager mAppOpsManager; 53 private StatusBarManager mStatusBarManager; 54 55 private boolean mAreActiveLocationRequests; 56 private boolean mIsAirplaneMode; 57 58 private ArrayList<LocationSettingsChangeCallback> mSettingsChangeCallbacks = 59 new ArrayList<LocationSettingsChangeCallback>(); 60 61 /** 62 * A callback for change in location settings (the user has enabled/disabled location). 63 */ 64 public interface LocationSettingsChangeCallback { 65 /** 66 * Called whenever location settings change. 67 * 68 * @param locationEnabled A value of true indicates that at least one type of location 69 * is enabled in settings. 70 */ 71 public void onLocationSettingsChanged(boolean locationEnabled); 72 } 73 74 public LocationController(Context context) { 75 mContext = context; 76 77 IntentFilter filter = new IntentFilter(); 78 filter.addAction(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION); 79 // Listen for a change in the airplane mode setting so we can defensively turn off the 80 // high power location icon when radios are disabled. 81 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); 82 context.registerReceiver(this, filter); 83 84 mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 85 mStatusBarManager 86 = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); 87 88 // Register to listen for changes to the location settings 89 context.getContentResolver().registerContentObserver( 90 Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true, 91 new ContentObserver(new Handler()) { 92 @Override 93 public void onChange(boolean selfChange) { 94 boolean isEnabled = isLocationEnabled(); 95 for (LocationSettingsChangeCallback cb : mSettingsChangeCallbacks) { 96 cb.onLocationSettingsChanged(isEnabled); 97 } 98 } 99 }); 100 101 // Examine the current location state and initialize the status view. 102 updateActiveLocationRequests(); 103 updateAirplaneMode(); 104 refreshViews(); 105 } 106 107 /** 108 * Add a callback to listen for changes in location settings. 109 */ 110 public void addSettingsChangedCallback(LocationSettingsChangeCallback cb) { 111 mSettingsChangeCallbacks.add(cb); 112 } 113 114 /** 115 * Enable or disable location in settings. 116 * 117 * <p>This will attempt to enable/disable every type of location setting 118 * (e.g. high and balanced power). 119 * 120 * <p>If enabling, a user consent dialog will pop up prompting the user to accept. 121 * If the user doesn't accept, network location won't be enabled. 122 */ 123 public void setLocationEnabled(boolean enabled) { 124 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 125 if (um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) { 126 return; 127 } 128 final ContentResolver cr = mContext.getContentResolver(); 129 Settings.Secure.setLocationProviderEnabled( 130 cr, LocationManager.GPS_PROVIDER, enabled); 131 // When enabling the NETWORK_PROVIDER, a user consent dialog will pop up, and the 132 // setting won't actually be enabled until the user accepts the agreement. 133 Settings.Secure.setLocationProviderEnabled( 134 cr, LocationManager.NETWORK_PROVIDER, enabled); 135 } 136 137 /** 138 * Returns true if either gps or network location are enabled in settings. 139 */ 140 public boolean isLocationEnabled() { 141 ContentResolver contentResolver = mContext.getContentResolver(); 142 boolean isGpsEnabled = Settings.Secure.isLocationProviderEnabled( 143 contentResolver, LocationManager.GPS_PROVIDER); 144 boolean isNetworkEnabled = Settings.Secure.isLocationProviderEnabled( 145 contentResolver, LocationManager.NETWORK_PROVIDER); 146 return isGpsEnabled || isNetworkEnabled; 147 } 148 149 /** 150 * Returns true if there currently exist active high power location requests. 151 */ 152 private boolean areActiveHighPowerLocationRequests() { 153 List<AppOpsManager.PackageOps> packages 154 = mAppOpsManager.getPackagesForOps(mHighPowerRequestAppOpArray); 155 // AppOpsManager can return null when there is no requested data. 156 if (packages != null) { 157 final int numPackages = packages.size(); 158 for (int packageInd = 0; packageInd < numPackages; packageInd++) { 159 AppOpsManager.PackageOps packageOp = packages.get(packageInd); 160 List<AppOpsManager.OpEntry> opEntries = packageOp.getOps(); 161 if (opEntries != null) { 162 final int numOps = opEntries.size(); 163 for (int opInd = 0; opInd < numOps; opInd++) { 164 AppOpsManager.OpEntry opEntry = opEntries.get(opInd); 165 // AppOpsManager should only return OP_MONITOR_HIGH_POWER_LOCATION because 166 // of the mHighPowerRequestAppOpArray filter, but checking defensively. 167 if (opEntry.getOp() == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) { 168 if (opEntry.isRunning()) { 169 return true; 170 } 171 } 172 } 173 } 174 } 175 } 176 177 return false; 178 } 179 180 // Updates the status view based on the current state of location requests and airplane mode. 181 private void refreshViews() { 182 // The airplane mode check is defensive - there shouldn't be any active high power 183 // location requests when airplane mode is on. 184 if (!mIsAirplaneMode && mAreActiveLocationRequests) { 185 mStatusBarManager.setIcon(LOCATION_STATUS_ICON_PLACEHOLDER, LOCATION_STATUS_ICON_ID, 0, 186 mContext.getString(R.string.accessibility_location_active)); 187 } else { 188 mStatusBarManager.removeIcon(LOCATION_STATUS_ICON_PLACEHOLDER); 189 } 190 } 191 192 // Reads the active location requests and updates the status view if necessary. 193 private void updateActiveLocationRequests() { 194 boolean hadActiveLocationRequests = mAreActiveLocationRequests; 195 mAreActiveLocationRequests = areActiveHighPowerLocationRequests(); 196 if (mAreActiveLocationRequests != hadActiveLocationRequests) { 197 refreshViews(); 198 } 199 } 200 201 // Reads the airplane mode setting and updates the status view if necessary. 202 private void updateAirplaneMode() { 203 boolean wasAirplaneMode = mIsAirplaneMode; 204 // TODO This probably warrants a utility method in Settings.java. 205 mIsAirplaneMode = (Settings.Global.getInt( 206 mContext.getContentResolver(), 207 Settings.Global.AIRPLANE_MODE_ON, 0) == 1); 208 if (mIsAirplaneMode != wasAirplaneMode) { 209 refreshViews(); 210 } 211 } 212 213 @Override 214 public void onReceive(Context context, Intent intent) { 215 final String action = intent.getAction(); 216 if (LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)) { 217 updateActiveLocationRequests(); 218 } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) { 219 updateAirplaneMode(); 220 } 221 } 222} 223