diff --git a/src/androidTest/java/de/blau/android/TestUtils.java b/src/androidTest/java/de/blau/android/TestUtils.java index 76d263e8ed..c351aa442a 100644 --- a/src/androidTest/java/de/blau/android/TestUtils.java +++ b/src/androidTest/java/de/blau/android/TestUtils.java @@ -1383,7 +1383,7 @@ public static void scrollTo(@NonNull String text, boolean fail) { } } } - + /** * Scroll to a specific text * diff --git a/src/androidTest/java/de/blau/android/gpx/GpxBarometerTest.java b/src/androidTest/java/de/blau/android/gpx/GpxBarometerTest.java new file mode 100644 index 0000000000..b61e725b97 --- /dev/null +++ b/src/androidTest/java/de/blau/android/gpx/GpxBarometerTest.java @@ -0,0 +1,192 @@ +package de.blau.android.gpx; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.app.Instrumentation; +import android.app.Instrumentation.ActivityMonitor; +import android.content.Context; +import android.content.Intent; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationManager; +import androidx.test.core.app.ActivityScenario; +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; +import de.blau.android.App; +import de.blau.android.Logic; +import de.blau.android.Main; +import de.blau.android.Map; +import de.blau.android.MockTileServer; +import de.blau.android.R; +import de.blau.android.SignalHandler; +import de.blau.android.TestUtils; +import de.blau.android.layer.LayerType; +import de.blau.android.prefs.AdvancedPrefDatabase; +import de.blau.android.prefs.Preferences; +import de.blau.android.resources.TileLayerDatabase; +import de.blau.android.services.TrackerService; +import okhttp3.mockwebserver.MockWebServer; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class GpxBarometerTest { + + public static final int TIMEOUT = 180; + + Main main = null; + UiDevice device = null; + Instrumentation instrumentation = null; + MockWebServer tileServer = null; + Preferences prefs = null; + + /** + * Manual start of activity so that we can set up the monitor for main + */ + @Rule + public ActivityScenarioRule
activityScenarioRule = new ActivityScenarioRule<>(Main.class); + + /** + * Pre-test setup + */ + @Before + public void setup() { + instrumentation = InstrumentationRegistry.getInstrumentation(); + device = UiDevice.getInstance(instrumentation); + // this sets the mock location permission + instrumentation.getUiAutomation().executeShellCommand("appops set de.blau.android android:mock_location allow"); + + prefs = App.getPreferences(instrumentation.getTargetContext()); + prefs.setGpsDistance(0); + prefs.enableBarometricHeight(true); // this needs to be set before TrackerService is created + + ActivityMonitor monitor = instrumentation.addMonitor(Main.class.getName(), null, false); + ActivityScenario.launch(Main.class); + + main = (Main) instrumentation.waitForMonitorWithTimeout(monitor, 30000); + instrumentation.removeMonitor(monitor); + + tileServer = MockTileServer.setupTileServer(main, "ersatz_background.mbt", true); + + Logic logic = App.getLogic(); + Map map = main.getMap(); + + App.getDelegator().reset(true); + try (AdvancedPrefDatabase db = new AdvancedPrefDatabase(main)) { + db.deleteLayer(LayerType.GPX, null); + } + + TestUtils.grantPermissons(device); + TestUtils.dismissStartUpDialogs(device, main); + TestUtils.stopEasyEdit(main); + TestUtils.zoomToNullIsland(logic, map); + } + + /** + * Post-test teardown + */ + @After + public void teardown() { + instrumentation.waitForIdleSync(); + prefs.enableBarometricHeight(false); + try { + tileServer.close(); + } catch (IOException | NullPointerException e) { + // ignore + } + if (main != null) { + try (AdvancedPrefDatabase db = new AdvancedPrefDatabase(main)) { + db.deleteLayer(LayerType.GPX, null); + } + TestUtils.stopEasyEdit(main); + main.deleteDatabase(TileLayerDatabase.DATABASE_NAME); + main.finish(); + } else { + System.out.println("main is null"); + } + } + + /** + * Turn on using barometric height, calibrate default pressure at sea level to -100m + */ + // @SdkSuppress(minSdkVersion = 26) + @Test + public void recordWithBarometricElevation() { + Intent intent = new Intent(main, TrackerService.class); + intent.putExtra(TrackerService.CALIBRATE_KEY, true); + intent.putExtra(TrackerService.CALIBRATE_HEIGHT_KEY, -100); + main.bindService(intent, main, Context.BIND_AUTO_CREATE); + + main.startService(intent); + // + TestUtils.setupMockLocation(main, Criteria.ACCURACY_FINE); + // wait for the trackerservice to start + // unluckily there doesn't seem to be any elegant way to do this + int retries = 0; + synchronized (device) { + while (main.getTracker() == null && retries < 60) { + try { + device.wait(1000); + } catch (InterruptedException e) { + // Ignore + } + retries++; + if (retries >= 60) { + fail("Tracker service didn't start"); + } + } + } + + TestUtils.zoomToLevel(device, main, 19); + TestUtils.clickButton(device, device.getCurrentPackageName() + ":id/follow", false); + + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + InputStream is = loader.getResourceAsStream("short.gpx"); + Track track = new Track(main, false); + track.importFromGPX(is); + + // set a different current location so that the first point always gets recorded + int trackSize = track.getTrackPoints().size(); + TrackPoint startPoint = track.getTrackPoints().get(trackSize / 2); + Location loc = new Location(LocationManager.GPS_PROVIDER); + loc.setLatitude(startPoint.getLatitude()); + loc.setLongitude(startPoint.getLongitude()); + main.getTracker().updateLocation(loc); + TestUtils.sleep(); + main.invalidateOptionsMenu(); + + GpxTest.clickGpsButton(device); + assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_gps_start), false, false)); + GpxTest.clickAwayTip(device, main); + + final CountDownLatch signal = new CountDownLatch(1); + main.getTracker().getTrack().reset(); // clear out anything saved + TestUtils.injectLocation(main, track.getTrackPoints(), Criteria.ACCURACY_FINE, 1000, new SignalHandler(signal)); + try { + signal.await(TIMEOUT, TimeUnit.SECONDS); + } catch (InterruptedException e) { + fail(e.getMessage()); + } + GpxTest.clickGpsButton(device); + assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_gps_pause), true, false)); + + TrackPoint recorded = main.getTracker().getTrack().getTrackPoints().get(1); + assertTrue(recorded.hasAltitude()); + assertEquals(-105f, recorded.getAltitude(), 1f); + } +} diff --git a/src/androidTest/java/de/blau/android/gpx/GpxTest.java b/src/androidTest/java/de/blau/android/gpx/GpxTest.java index ba8d3fb6bd..51ac63d146 100644 --- a/src/androidTest/java/de/blau/android/gpx/GpxTest.java +++ b/src/androidTest/java/de/blau/android/gpx/GpxTest.java @@ -15,7 +15,6 @@ import java.util.concurrent.TimeUnit; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -68,9 +67,6 @@ public class GpxTest { MockWebServer tileServer = null; Preferences prefs = null; - /** - * Manual start of activity so that we can set up the monitor for main - */ @Rule public ActivityTestRule
mActivityRule = new ActivityTestRule<>(Main.class); @@ -87,11 +83,12 @@ public void setup() { main = mActivityRule.getActivity(); tileServer = MockTileServer.setupTileServer(main, "ersatz_background.mbt", true); - prefs = new Preferences(main); + prefs = App.getPreferences(main); Logic logic = App.getLogic(); logic.setPrefs(prefs); Map map = main.getMap(); map.setPrefs(main, prefs); + prefs.enableBarometricHeight(false); App.getDelegator().reset(true); try (AdvancedPrefDatabase db = new AdvancedPrefDatabase(main)) { @@ -136,20 +133,7 @@ public void recordSaveAndImportGpx() { TestUtils.setupMockLocation(main, Criteria.ACCURACY_FINE); // wait for the trackerservice to start // unluckily there doesn't seem to be any elegant way to do this - int retries = 0; - synchronized (device) { - while (main.getTracker() == null && retries < 60) { - try { - device.wait(1000); - } catch (InterruptedException e) { - // Ignore - } - retries++; - if (retries >= 60) { - fail("Tracker service didn't start"); - } - } - } + checkTracker(); // set min distance to 1m prefs.setGpsDistance(0); @@ -181,7 +165,7 @@ public void recordSaveAndImportGpx() { try { signal.await(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { - Assert.fail(e.getMessage()); + fail(e.getMessage()); } clickGpsButton(device); assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_gps_pause), true, false)); @@ -234,14 +218,9 @@ public void recordSaveAndImportGpx() { } /** - * Start recording, pause resume, clear + * Wait until the tracker is available */ - // @SdkSuppress(minSdkVersion = 26) - @Test - public void recordPauseAndResume() { - TestUtils.setupMockLocation(main, Criteria.ACCURACY_FINE); - // wait for the trackerservice to start - // unluckily there doesn't seem to be any elegant way to do this + private void checkTracker() { int retries = 0; synchronized (device) { while (main.getTracker() == null && retries < 60) { @@ -256,6 +235,19 @@ public void recordPauseAndResume() { } } } + } + + /** + * Start recording, pause resume, clear + */ + // @SdkSuppress(minSdkVersion = 26) + @Test + public void recordPauseAndResume() { + TestUtils.setupMockLocation(main, Criteria.ACCURACY_FINE); + // wait for the trackerservice to start + // unluckily there doesn't seem to be any elegant way to do this + int retries = 0; + checkTracker(); // set min distance to 1m prefs.setGpsDistance(0); @@ -287,7 +279,7 @@ public void recordPauseAndResume() { try { signal.await(TIMEOUT, TimeUnit.SECONDS); } catch (InterruptedException e) { - Assert.fail(e.getMessage()); + fail(e.getMessage()); } clickGpsButton(device); assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_gps_pause), true, false)); @@ -358,11 +350,14 @@ public void importWayPoints() { @Test public void followNetworkLocation() { TestUtils.setupMockLocation(main, Criteria.ACCURACY_COARSE); + checkTracker(); // set min distance to 1m prefs.setGpsDistance(0); TestUtils.zoomToLevel(device, main, 19); TestUtils.clickButton(device, device.getCurrentPackageName() + ":id/follow", false); + assertTrue(main.getFollowGPS()); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream is = loader.getResourceAsStream("20110513_121244-tp.gpx"); Track track = new Track(main, false); @@ -371,8 +366,6 @@ public void followNetworkLocation() { final CountDownLatch signal = new CountDownLatch(1); TestUtils.injectLocation(main, track.getTrackPoints(), Criteria.ACCURACY_COARSE, 1000, new SignalHandler(signal)); TestUtils.sleep(TIMEOUT * 1000L); - clickGpsButton(device); - assertTrue(TestUtils.clickText(device, false, "Pause GPX track", true, false)); // compare roughly with last location TrackPoint lastPoint = track.getTrackPoints().get(track.getTrackPoints().size() - 1); ViewBox box = main.getMap().getViewBox(); diff --git a/src/main/java/de/blau/android/Main.java b/src/main/java/de/blau/android/Main.java index 26665a20ab..da28ba914a 100644 --- a/src/main/java/de/blau/android/Main.java +++ b/src/main/java/de/blau/android/Main.java @@ -2160,17 +2160,21 @@ public void onError(String message) { tipKeys.add(R.string.tip_gpx_no_elevation_key); tipMessageIds.add(R.string.tip_gpx_no_elevation); } - Tip.showDialog(Main.this, tipKeys, tipMessageIds); if (haveTracker && haveLocationProvider(getEnabledLocationProviders(), LocationManager.GPS_PROVIDER)) { getTracker().startTracking(); setFollowGPS(true); } addGpxLayer(); + mapLayout.post(() -> { + triggerMenuInvalidation(); + Tip.showDialog(Main.this, tipKeys, tipMessageIds); + }); return true; case R.id.menu_gps_pause: if (haveTracker && haveLocationProvider(getEnabledLocationProviders(), LocationManager.GPS_PROVIDER)) { getTracker().stopTracking(false); } + mapLayout.post(() -> triggerMenuInvalidation()); return true; case R.id.menu_gps_clear: if (haveTracker) { @@ -2745,7 +2749,6 @@ private void addGpxLayer() { } else { Log.e(DEBUG_TAG, "addGpxLayer tracker not available"); } - triggerMenuInvalidation(); } /** @@ -2928,6 +2931,7 @@ private List getEnabledLocationProviders() { * @param follow if true center on current location */ public synchronized void setFollowGPS(boolean follow) { + Log.d(DEBUG_TAG, "Set follow GPS " + follow); if (followGPS != follow) { followGPS = follow; if (follow) { @@ -4164,6 +4168,7 @@ public static void prepareRedownload() { public void onServiceConnected(ComponentName name, IBinder service) { Log.i(DEBUG_TAG, "Service " + name.getClassName() + " connected"); if (TrackerService.class.getCanonicalName().equals(name.getClassName())) { + Log.i(DEBUG_TAG, "Setting up tracker"); setTracker((((TrackerBinder) service).getService())); map.setTracker(getTracker()); de.blau.android.layer.gpx.MapOverlay layer = (de.blau.android.layer.gpx.MapOverlay) map.getLayer(LayerType.GPX, @@ -4193,6 +4198,7 @@ public void onServiceDisconnected(ComponentName name) { @Override public void onLocationChanged(Location location) { + Log.d(DEBUG_TAG, "follow " + followGPS + " " + location); if (followGPS) { ViewBox viewBox = map.getViewBox(); // ensure the view is zoomed in to at least the most zoomed-out diff --git a/src/main/java/de/blau/android/prefs/Preferences.java b/src/main/java/de/blau/android/prefs/Preferences.java index 463df07eb6..d7c4949109 100755 --- a/src/main/java/de/blau/android/prefs/Preferences.java +++ b/src/main/java/de/blau/android/prefs/Preferences.java @@ -116,7 +116,7 @@ public class Preferences { private final int uploadWarnLimit; private final int uploadCheckerInterval; private final int dataWarnLimit; - private final boolean useBarometricHeight; + private boolean useBarometricHeight; private final boolean useUrlForFeedback; private final int beepVolume; private final int maxOffsetDistance; @@ -1574,6 +1574,16 @@ public boolean useBarometricHeight() { return useBarometricHeight; } + /** + * Enable/disable using barometric elevation when recording GPX files + * + * @param on if true barometric elevation will be enabled + */ + public void enableBarometricHeight(boolean on) { + useBarometricHeight = on; + prefs.edit().putBoolean(r.getString(R.string.config_useBarometricHeight_key), on).commit(); + } + /** * Get f we should use an Url instead of the builtin reporter activity * diff --git a/src/main/java/de/blau/android/services/TrackerService.java b/src/main/java/de/blau/android/services/TrackerService.java index 971c52759e..542dd1710a 100644 --- a/src/main/java/de/blau/android/services/TrackerService.java +++ b/src/main/java/de/blau/android/services/TrackerService.java @@ -64,7 +64,7 @@ public class TrackerService extends Service { - private static final String DEBUG_TAG = "TrackerService"; + private static final String DEBUG_TAG = TrackerService.class.getSimpleName(); private static final float TRACK_LOCATION_MIN_ACCURACY = 200f; @@ -154,7 +154,7 @@ public void onCreate() { track = new Track(this, true); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - prefs = new Preferences(this); + prefs = App.getPreferences(this); validator = App.getDefaultValidator(this); prefInternal = getString(R.string.gps_source_internal); prefNmea = getString(R.string.gps_source_nmea); @@ -178,18 +178,8 @@ public void onCreate() { Log.e(DEBUG_TAG, "reflection didn't find addNmeaListener or removeNmeaListener " + e.getMessage()); } - // pressure sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); - Sensor pressure = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); - if (prefs.useBarometricHeight() && pressure != null) { - pressureListener = new PressureListener(); - sensorManager.registerListener(pressureListener, pressure, 1000); - Sensor temperature = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); - if (temperature != null) { - temperatureListener = new TemperatureListener(); - sensorManager.registerListener(temperatureListener, temperature, 1000); - } - } + Uri egmFile = prefs.getEgmFile(); if (egmFile != null) { try { @@ -203,6 +193,25 @@ public void onCreate() { } } + /** + * Setup the pressure and temp sensors + * + * @param sensorManager a SensorManager instance + */ + private void setupPressureSensor(@NonNull SensorManager sensorManager) { + Sensor pressure = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); + if (prefs.useBarometricHeight() && pressure != null && pressureListener == null) { + Log.d(DEBUG_TAG, "Installing pressure listener"); + pressureListener = new PressureListener(); + sensorManager.registerListener(pressureListener, pressure, 1000); + Sensor temperature = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); + if (temperature != null) { + temperatureListener = new TemperatureListener(); + sensorManager.registerListener(temperatureListener, temperature, 1000); + } + } + } + @Override public void onDestroy() { Log.d(DEBUG_TAG, "onDestroy"); @@ -279,35 +288,43 @@ public int onStartCommand(Intent intent, int flags, int startId) { Log.d(DEBUG_TAG, "Start task autodownload"); startBugAutoDownloadInternal(); } else if (intent.getBooleanExtra(CALIBRATE_KEY, false)) { - Log.d(DEBUG_TAG, "Calibrate height"); - if (pressureListener != null) { - int height = intent.getIntExtra(CALIBRATE_HEIGHT_KEY, Integer.MIN_VALUE); - if (height != Integer.MIN_VALUE) { - pressureListener.calibrate(height); - } else { - float p0 = intent.getFloatExtra(CALIBRATE_P0_KEY, 0); - if (p0 != 0) { - pressureListener.setP0(p0); - } else if (lastLocation != null) { // calibrate from GPS - if (lastLocation instanceof ExtendedLocation && ((ExtendedLocation) lastLocation).hasGeoidHeight()) { - pressureListener.calibrate((float) ((ExtendedLocation) lastLocation).getGeoidHeight()); - } else if (lastLocation.hasAltitude()) { - double offset = getGeoidOffset(lastLocation.getLongitude(), lastLocation.getLatitude()); - Log.d(DEBUG_TAG, "Geoid offset " + offset); - pressureListener.calibrate((float) (lastLocation.getAltitude() - offset)); - } + calibratePressureListener(intent); + } else { + Log.d(DEBUG_TAG, "Received intent with unknown meaning"); + } + return START_STICKY; + } + + /** + * Process a calibration intent + * + * @param intent the Intent + */ + private void calibratePressureListener(Intent intent) { + Log.d(DEBUG_TAG, "Calibrate height"); + if (pressureListener != null) { + int height = intent.getIntExtra(CALIBRATE_HEIGHT_KEY, Integer.MIN_VALUE); + if (height != Integer.MIN_VALUE) { + pressureListener.calibrate(height); + } else { + float p0 = intent.getFloatExtra(CALIBRATE_P0_KEY, 0); + if (p0 != 0) { + pressureListener.setP0(p0); + } else if (lastLocation != null) { // calibrate from GPS + if (lastLocation instanceof ExtendedLocation && ((ExtendedLocation) lastLocation).hasGeoidHeight()) { + pressureListener.calibrate((float) ((ExtendedLocation) lastLocation).getGeoidHeight()); + } else if (lastLocation.hasAltitude()) { + double offset = getGeoidOffset(lastLocation.getLongitude(), lastLocation.getLatitude()); + Log.d(DEBUG_TAG, "Geoid offset " + offset); + pressureListener.calibrate((float) (lastLocation.getAltitude() - offset)); } } - Snack.toastTopInfo(this, "New height " + pressureListener.barometricHeight + "m\nCurrent pressure " + pressureListener.millibarsOfPressure - + " hPa\nReference pressure " + pressureListener.pressureAtSeaLevel + " hPa"); - } else { - Log.e(DEBUG_TAG, "Calibration attemped but no pressure listener"); } + Snack.toastTopInfo(this, "New height " + pressureListener.barometricHeight + "m\nCurrent pressure " + pressureListener.millibarsOfPressure + + " hPa\nReference pressure " + pressureListener.pressureAtSeaLevel + " hPa"); } else { - Log.d(DEBUG_TAG, "Received intent with unknown meaning"); + Log.e(DEBUG_TAG, "Calibration attemped but no pressure listener"); } - - return START_STICKY; } /** @@ -571,7 +588,7 @@ public void onLocationChanged(Location location) { loc.setGeoidHeight(loc.getAltitude() - offset); } } - if (pressureListener != null && pressureListener.barometricHeight != 0) { + if (pressureListener != null) { if (useBarometricHeight) { loc.setUseBarometricHeight(); } @@ -708,16 +725,17 @@ public void handleMessage(Message inputMessage) { @SuppressLint("MissingPermission") @TargetApi(24) private void init() { - prefs = new Preferences(this); + prefs = App.getPreferences(this); String gpsSource = prefs.getGpsSource(); final boolean useTcpClient = gpsSource.equals(prefTcpClient); final boolean useTcpServer = gpsSource.equals(prefTcpServer); final boolean useTcp = useTcpClient || useTcpServer; - useBarometricHeight = pressureListener != null && prefs.useBarometricHeight(); - boolean needed = listenerNeedsGPS || tracking || downloading || downloadingBugs; + setupPressureSensor(sensorManager); + useBarometricHeight = pressureListener != null; + // update configuration if ((needed && !gpsEnabled) || (gpsEnabled && (useTcp && source != GpsSource.TCP) || (!useTcp && source == GpsSource.TCP))) { Log.d(DEBUG_TAG, "Enabling GPS updates"); @@ -754,13 +772,7 @@ private void init() { if (useNema) { source = GpsSource.NMEA; if (useOldNmea) { - if (addNmeaListener != null) { - try { - addNmeaListener.invoke(locationManager, oldNmeaListener); - } catch (Exception e) { // NOSONAR - // IGNORE - } - } + addNmeaListenerWIthReflection(oldNmeaListener); } else { locationManager.addNmeaListener(newNmeaListener); } @@ -797,6 +809,23 @@ private void init() { } } + /** + * + * see https://issuetracker.google.com/issues/141019880 + * + * @param listener the OldNmeaListener listener + * + */ + private void addNmeaListenerWIthReflection(OldNmeaListener listener) { + if (addNmeaListener != null) { + try { + addNmeaListener.invoke(locationManager, listener); + } catch (Exception e) { // NOSONAR + // IGNORE + } + } + } + /** * If true the listener wants to receive Location updates * @@ -816,6 +845,7 @@ public void setListenerNeedsGPS(boolean listenerNeedsGPS) { * @param listener the listener */ public void setListener(@Nullable TrackerLocationListener listener) { + Log.d(DEBUG_TAG, "setListener " + listener); if (listener == null) { setListenerNeedsGPS(false); } @@ -1028,6 +1058,7 @@ public void updateLocation(@Nullable Location location) { return; } autoLoadDataAndBugs(location); + Log.d(DEBUG_TAG,"calling onLocationChanged " + location + " " + externalListener); if (externalListener != null) { externalListener.onLocationChanged(location); }