Reputation: 21765
I'm trying to mock my location by creating a Location instance and then using the LocationManager.SetTestProviderLocation(string providerName, Locatin location) method.
For example:
Location location = new Location(providerName)
{
Latitude = 51.20958,
Longitude = 2.92372,
Accuracy = 100.0f,
Time = DateTime.Now.Ticks,
Altitude = 1.0,
Speed = 0.0f,
Bearing = 0.0f,
Provider = providerName,
};
locationManager.SetTestProviderLocation(providerName, location);
This always results in an exception:
"Incomplete location object, missing timestamp or accuracy? Location[gps 51.209580,2.923720 acc=1 et=?!? alt=1.0 vel=0.0 bear=0.0]"
I found some information on the subject here.
The author mentions that the LocationManger checks an isComplete (method)
to determine whether or not the Location is complete.
I expected the mono version of the class to have an IsComplete property
and MakeComplete method, but they do not exist (or at least I could not find them)
Taking a closer look at the Android SDK source code:
public void makeComplete()
{
if (mProvider == null) mProvider = "?";
if (!mHasAccuracy) {
mHasAccuracy = true;
mAccuracy = 100.0f;
}
if (mTime == 0) mTime = System.currentTimeMillis();
if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
}
I'm guessing that the "et=?!?" value in the exception refers to the mElapsedRealtimeNanos field
and since there is no way that I know off to access that field I cannot get mocking to work.
Am I missing something, is this a missing feature of Mono for Android or
is there perhaps another way to make it work.
Note: I'm aware there are ways of setting a fake location using DDMS, but I'm looking for a way to automate the location mocking
UPDATE
Some time after posting this question I realized I wasn't targeting API level 16.
As a result Mono for Android did not expose the ElapsedRealtimeNanos property.
After switching to API level 16 the property became available. It did not however,
provide a MakeComplete method. (Perhaps this is a bug / missing feature).
So I made a quick solution to verify the behavior:
Location location = new Location(providerName)
{
Longitude = locations[position, 0],
Latitude = locations[position, 1],
Accuracy = 100.0f,
Time = DateTime.Now.Ticks,
Altitude = 1.0,
Speed = 0.0f,
Bearing = 0.0f,
Provider = providerName,
ElapsedRealtimeNanos = (long)(TimeSpan.FromTicks(DateTime.Now.Ticks)
.TotalMilliseconds * 1000000)
};
While this solves the problem for Jelly Bean devices (4.1 or later) the code
will break on older devices. Which is where the solution suggested by @Cheesebaron comes into play.
Upvotes: 4
Views: 1740
Reputation: 24470
You will probably need to access makeComplete
through JNI with code like:
public class MyLocation : Location
{
protected MyLocation(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public MyLocation(Location l) : base(l)
{
}
public MyLocation(string provider) : base(provider)
{
}
public void MakeComplete()
{
if ((int)Build.VERSION.SdkInt >= 16)
{
var locationClass = JNIEnv.FindClass("android/location/Location");
var makeComplete = JNIEnv.GetMethodID(locationClass, "makeComplete", "()V");
JNIEnv.CallVoidMethod(Handle, makeComplete);
}
}
}
Then simply use instances of MyLocation
where you call MakeComplete
. However it will only run on API 16.
Upvotes: 3