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 |