| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.device.geolocation; | |
| 6 | |
| 7 import android.content.Context; | |
| 8 import android.location.Criteria; | |
| 9 import android.location.Location; | |
| 10 import android.location.LocationListener; | |
| 11 import android.location.LocationManager; | |
| 12 import android.os.Bundle; | |
| 13 | |
| 14 import org.chromium.base.Log; | |
| 15 import org.chromium.base.ThreadUtils; | |
| 16 import org.chromium.base.VisibleForTesting; | |
| 17 | |
| 18 import java.util.List; | |
| 19 | |
| 20 /** | |
| 21 * Factory to create a LocationProvider to allow us to inject | |
| 22 * a mock for tests. | |
| 23 */ | |
| 24 public class LocationProviderFactory { | |
| 25 private static LocationProviderFactory.LocationProvider sProviderImpl; | |
| 26 | |
| 27 /** | |
| 28 * LocationProviderFactory.get() returns an instance of this interface. | |
| 29 */ | |
| 30 public interface LocationProvider { | |
| 31 public void start(boolean enableHighAccuracy); | |
| 32 public void stop(); | |
| 33 public boolean isRunning(); | |
| 34 } | |
| 35 | |
| 36 private LocationProviderFactory() { | |
| 37 } | |
| 38 | |
| 39 @VisibleForTesting | |
| 40 public static void setLocationProviderImpl(LocationProviderFactory.LocationP
rovider provider) { | |
| 41 assert sProviderImpl == null; | |
| 42 sProviderImpl = provider; | |
| 43 } | |
| 44 | |
| 45 public static LocationProvider get(Context context) { | |
| 46 if (sProviderImpl == null) { | |
| 47 sProviderImpl = new LocationProviderImpl(context); | |
| 48 } | |
| 49 return sProviderImpl; | |
| 50 } | |
| 51 | |
| 52 /** | |
| 53 * This is the core of android location provider. It is a separate class for
clarity | |
| 54 * so that it can manage all processing completely in the UI thread. The con
tainer class | |
| 55 * ensures that the start/stop calls into this class are done in the UI thre
ad. | |
| 56 */ | |
| 57 private static class LocationProviderImpl | |
| 58 implements LocationListener, LocationProviderFactory.LocationProvide
r { | |
| 59 | |
| 60 // Log tag | |
| 61 private static final String TAG = "cr_LocationProvider"; | |
| 62 | |
| 63 private Context mContext; | |
| 64 private LocationManager mLocationManager; | |
| 65 private boolean mIsRunning; | |
| 66 | |
| 67 LocationProviderImpl(Context context) { | |
| 68 mContext = context; | |
| 69 } | |
| 70 | |
| 71 /** | |
| 72 * Start listening for location updates. | |
| 73 * @param enableHighAccuracy Whether or not to enable high accuracy loca
tion providers. | |
| 74 */ | |
| 75 @Override | |
| 76 public void start(boolean enableHighAccuracy) { | |
| 77 unregisterFromLocationUpdates(); | |
| 78 registerForLocationUpdates(enableHighAccuracy); | |
| 79 } | |
| 80 | |
| 81 /** | |
| 82 * Stop listening for location updates. | |
| 83 */ | |
| 84 @Override | |
| 85 public void stop() { | |
| 86 unregisterFromLocationUpdates(); | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * Returns true if we are currently listening for location updates, fals
e if not. | |
| 91 */ | |
| 92 @Override | |
| 93 public boolean isRunning() { | |
| 94 return mIsRunning; | |
| 95 } | |
| 96 | |
| 97 @Override | |
| 98 public void onLocationChanged(Location location) { | |
| 99 // Callbacks from the system location service are queued to this thr
ead, so it's | |
| 100 // possible that we receive callbacks after unregistering. At this p
oint, the | |
| 101 // native object will no longer exist. | |
| 102 if (mIsRunning) { | |
| 103 updateNewLocation(location); | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 private void updateNewLocation(Location location) { | |
| 108 LocationProviderAdapter.newLocationAvailable( | |
| 109 location.getLatitude(), location.getLongitude(), | |
| 110 location.getTime() / 1000.0, | |
| 111 location.hasAltitude(), location.getAltitude(), | |
| 112 location.hasAccuracy(), location.getAccuracy(), | |
| 113 location.hasBearing(), location.getBearing(), | |
| 114 location.hasSpeed(), location.getSpeed()); | |
| 115 } | |
| 116 | |
| 117 @Override | |
| 118 public void onStatusChanged(String provider, int status, Bundle extras)
{ | |
| 119 } | |
| 120 | |
| 121 @Override | |
| 122 public void onProviderEnabled(String provider) { | |
| 123 } | |
| 124 | |
| 125 @Override | |
| 126 public void onProviderDisabled(String provider) { | |
| 127 } | |
| 128 | |
| 129 private void ensureLocationManagerCreated() { | |
| 130 if (mLocationManager != null) return; | |
| 131 mLocationManager = (LocationManager) mContext.getSystemService( | |
| 132 Context.LOCATION_SERVICE); | |
| 133 if (mLocationManager == null) { | |
| 134 Log.e(TAG, "Could not get location manager."); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Registers this object with the location service. | |
| 140 */ | |
| 141 private void registerForLocationUpdates(boolean enableHighAccuracy) { | |
| 142 ensureLocationManagerCreated(); | |
| 143 if (usePassiveOneShotLocation()) return; | |
| 144 | |
| 145 assert !mIsRunning; | |
| 146 mIsRunning = true; | |
| 147 | |
| 148 // We're running on the main thread. The C++ side is responsible to | |
| 149 // bounce notifications to the Geolocation thread as they arrive in
the mainLooper. | |
| 150 try { | |
| 151 Criteria criteria = new Criteria(); | |
| 152 if (enableHighAccuracy) criteria.setAccuracy(Criteria.ACCURACY_F
INE); | |
| 153 mLocationManager.requestLocationUpdates(0, 0, criteria, this, | |
| 154 ThreadUtils.getUiThreadLooper()); | |
| 155 } catch (SecurityException e) { | |
| 156 Log.e(TAG, "Caught security exception while registering for loca
tion updates " | |
| 157 + "from the system. The application does not have suffic
ient geolocation " | |
| 158 + "permissions."); | |
| 159 unregisterFromLocationUpdates(); | |
| 160 // Propagate an error to JavaScript, this can happen in case of
WebView | |
| 161 // when the embedding app does not have sufficient permissions. | |
| 162 LocationProviderAdapter.newErrorAvailable("application does not
have sufficient " | |
| 163 + "geolocation permissions."); | |
| 164 } catch (IllegalArgumentException e) { | |
| 165 Log.e(TAG, "Caught IllegalArgumentException registering for loca
tion updates."); | |
| 166 unregisterFromLocationUpdates(); | |
| 167 assert false; | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * Unregisters this object from the location service. | |
| 173 */ | |
| 174 private void unregisterFromLocationUpdates() { | |
| 175 if (mIsRunning) { | |
| 176 mIsRunning = false; | |
| 177 mLocationManager.removeUpdates(this); | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 private boolean usePassiveOneShotLocation() { | |
| 182 if (!isOnlyPassiveLocationProviderEnabled()) return false; | |
| 183 | |
| 184 // Do not request a location update if the only available location p
rovider is | |
| 185 // the passive one. Make use of the last known location and call | |
| 186 // onLocationChanged directly. | |
| 187 final Location location = mLocationManager.getLastKnownLocation( | |
| 188 LocationManager.PASSIVE_PROVIDER); | |
| 189 if (location != null) { | |
| 190 ThreadUtils.runOnUiThread(new Runnable() { | |
| 191 @Override | |
| 192 public void run() { | |
| 193 updateNewLocation(location); | |
| 194 } | |
| 195 }); | |
| 196 } | |
| 197 return true; | |
| 198 } | |
| 199 | |
| 200 /* | |
| 201 * Checks if the passive location provider is the only provider availabl
e | |
| 202 * in the system. | |
| 203 */ | |
| 204 private boolean isOnlyPassiveLocationProviderEnabled() { | |
| 205 List<String> providers = mLocationManager.getProviders(true); | |
| 206 return providers != null && providers.size() == 1 | |
| 207 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER)
; | |
| 208 } | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 | |
| OLD | NEW |