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