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