jeudi 29 janvier 2015

GeoFence Not Triggered w/Mock Location

I am trying to test GeoFences for a location aware application. I have gotten as many as 11 consecutive successes, but that is typically followed by dozens of failures. Rebooting the device does not help. Uninstalling/reinstalling does not help. Disabling location, re-enabling location does not help. Changing the devices Location Accuracy settings does not help.


I've tested on v2.3.4, v4.0.3, v4.4.2, v4.4.4 and 2 v5.0.1 physical devices. One of the Lollipop devices is a device with cellular service. The remaining devices are SIMless and used for testing only. Side note: the devices without service struggle with setting their location via MockLocation, but if they manage to set their location they almost never register a fence entry/exit.


Location is being updated via Mock Location well enough, but the GeoFence(s) are not being triggered with any reliability.


I've Googled it. I've looked at the documentation on developer.android.com site. I've searched StackOverflow. In fact, many of the suggestions made by others have been implemented in the code below.


So, how can I reliably trigger my GeoFence using MockLocation?



public class GeoFenceTester extends ActivityInstrumentationTestCase2<GeoFenceTesterActivity> {

public static final String TAG = GeoFenceTester.class.getSimpleName();

private LocationManager locationManager;
private Activity activityUnderTest;

protected void setUp() throws Exception {
super.setUp();
activityUnderTest = getActivity();
/*
MOCK Locations are required for these tests. If the user has not enabled MOCK Locations
on the device or emulator these tests cannot work.
*/
if (Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) == 0) {
throw new RuntimeException("Mock locations are currently disabled in Settings - These tests require mock locations");
}

Log.i(TAG, "Setup MOCK Location Providers");
locationManager = (LocationManager) activityUnderTest.getSystemService(Context.LOCATION_SERVICE);

Log.i(TAG, "GPS Provider");
locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false, false, false, false, false, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);

Log.i(TAG, "Network Provider");
locationManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false, true, false, false, false, false, Criteria.POWER_MEDIUM, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, true);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Log.wtf(TAG, String.format("Location Accuracy: %1$d", Settings.Secure.getInt(activityUnderTest.getContentResolver(), Settings.Secure.LOCATION_MODE)));
}

/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().register(this);
}

protected void tearDown() {
/* Our EventBus for onEvent() callbacks */
EventBus.getDefault().unregister(this);

locationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
locationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
locationManager = null;

activityUnderTest = null;

super.tearDown();
}

public void testGeoFence() {

/*
Using one or the other or both of these makes no difference.
*/
Location mockGpsLocation = new Location(LocationManager.GPS_PROVIDER);
mockGpsLocation.setLatitude(32.652411);
mockGpsLocation.setLongitude(-79.938063);
mockGpsLocation.setAccuracy(1.0f);
mockGpsLocation.setTime(System.currentTimeMillis());

Location mockNetworkLocation = new Location(LocationManager.NETWORK_PROVIDER);
mockNetworkLocation.setLatitude(32.652411);
mockNetworkLocation.setLongitude(-79.938063);
mockNetworkLocation.setAccuracy(1.0f);
mockNetworkLocation.setTime(System.currentTimeMillis());

/*
setElapsedRealtimeNanos() was added in API 17
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mockGpsLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
mockNetworkLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}

try {
Method locationJellyBeanFixMethod = Location.class.getMethod("makeComplete");
if (locationJellyBeanFixMethod != null) {
locationJellyBeanFixMethod.invoke(mockGpsLocation);
locationJellyBeanFixMethod.invoke(mockNetworkLocation);
}
} catch (Exception e) {
// There's no action to take here. This is a fix for Jelly Bean and no reason to report a failure.
}

for (int i = 0; i < 30; i++){
Log.i(TAG, String.format("Iterating over our location ... (%1$d)", i));
locationManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockGpsLocation);
locationManager.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, mockNetworkLocation);
try {
Thread.sleep(1000);
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
}

public void onEvent(final GeoFenceUpdateEvent event) {
// This doesn't get called about 9/10 times.
// I have a GeoFence with a 5000m radius around 32.652411, -79.938063
}

public void onEvent(final LocationUpdateEvent event) {
// This gets called without incident and event.getLatitude() & event.getLongitude() are correct.
}

}

Aucun commentaire:

Enregistrer un commentaire