Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing NMEA on Android API level < 24 when compiled for target API level 29?

I just tried updating the target and compile API level of our app to 29 (Android 10) and noticed that I cannot compile any more because LocationManager.addNmeaListener only accepts OnNmeaMessageListener (introduced with API level 24) instead of the older and deprecated GpsStatus.NmeaListener (so, I get "incompatible types: NmeaListener cannot be converted to OnNmeaMessageListener").

I understand that addNmeaListener(NmeaListener) has been deprecated in previous versions and I am prepared to refactor my code to branch for different API levels, but since I could not find any deprecated methods with different names or any compatibility libraries for LocationManager, it seems like there is no way to access NMEA data on Android 6 devices if I target Android 10 (which will be mandatory at some point).

Any suggestions on how I can still get NMEA data on older devices?

A little additional background if someone has ideas on how to avoid NMEA data: Our app "phyphox" is designed to allow students to use the sensors in their phones for physics experiments. It is important for us to run on old devices (Android 4+), because using smartphone sensors in physics education is especially valuable for schools and students with little funding. At the same time, we want to have comparable experimental results for all devices (new/old, Android/iOS). Therefore, we need the NMEA data to calculate the altitude above the geoid instead of the altitude above the WGS84 ellipsoid (as provided by Location.getAltitude(), so the students get consistent and comparable results. Therefore, any suggestion for alternative ways to achieve that are welcome too.

You can have a look at the current code for API 28 here:

https://github.com/Staacks/phyphox-android/blob/master/app/src/main/java/de/rwth_aachen/phyphox/gpsInput.java

like image 787
Sebastian Staacks Avatar asked Sep 17 '19 14:09

Sebastian Staacks


2 Answers

It seems there was a problem in the Android SDK 29 release and the method providing retro-compatibility was left out.

There is an issue opened in the Android bugtracker

From the comments of the Android engineers in that issue, the recommended approach is to access it via reflection... I still have to test it, but I guess it would be on the line of the following:

nmeaListenerDeprecated = new GpsStatus.NmeaListener() {
    @Override
    public void onNmeaReceived(long timestamp, String message) {
        // TODO
    }
};
try {
    //noinspection JavaReflectionMemberAccess
    Method addNmeaListener =
        LocationManager.class.getMethod("addNmeaListener", GpsStatus.NmeaListener.class);
    addNmeaListener.invoke(locationManager, nmeaListenerDeprecated);
} catch (Exception exception) {
    // TODO
}

EDIT: there was a missing argument in the .invoke() call. It needs the target object reference to perform the invocation! (too long since learning Reflection... and not used it ever since!)

like image 177
necavit Avatar answered Nov 16 '22 12:11

necavit


since starting from now (november 2020) minimum allowed Target SDK is 29 when deploying to Google, someone can experience this error again (as happened to me). Then, today a faster solution is this:

compileSdkVersion 30 
like image 22
edestrero Avatar answered Nov 16 '22 12:11

edestrero