From 96d200fd7c6159739a80eec914ffa362c6e3aa66 Mon Sep 17 00:00:00 2001 From: simonpoole Date: Sat, 23 Sep 2023 12:52:47 +0200 Subject: [PATCH 1/3] Add more digits to offset display and show approximation in meters This further adds support for displaying the offset in RTL. Fixes https://github.com/MarcusWolschon/osmeditor4android/issues/2355 --- src/main/java/de/blau/android/Logic.java | 47 ++++++-------- src/main/java/de/blau/android/Map.java | 46 ++++++++----- .../ImageryAlignmentActionModeCallback.java | 65 ++++++++++++++++++- .../blau/android/layer/grid/MapOverlay.java | 2 +- src/main/res/values/strings.xml | 2 +- 5 files changed, 114 insertions(+), 48 deletions(-) diff --git a/src/main/java/de/blau/android/Logic.java b/src/main/java/de/blau/android/Logic.java index c01efa461d..292d5256d7 100644 --- a/src/main/java/de/blau/android/Logic.java +++ b/src/main/java/de/blau/android/Logic.java @@ -70,7 +70,6 @@ import de.blau.android.filter.Filter; import de.blau.android.gpx.Track; import de.blau.android.imageryoffset.ImageryAlignmentActionModeCallback; -import de.blau.android.imageryoffset.Offset; import de.blau.android.layer.MapViewLayer; import de.blau.android.osm.BoundingBox; import de.blau.android.osm.DiscardedTags; @@ -102,7 +101,6 @@ import de.blau.android.prefs.Preferences; import de.blau.android.resources.DataStyle; import de.blau.android.resources.DataStyle.FeatureStyle; -import de.blau.android.resources.TileLayerSource; import de.blau.android.tasks.Note; import de.blau.android.tasks.Task; import de.blau.android.tasks.TransferTasks; @@ -579,10 +577,7 @@ public void zoom(final boolean zoomIn) { } else { viewBox.zoomOut(); } - DataStyle.updateStrokes(strokeWidth(viewBox.getWidth())); - if (rotatingWay) { - showCrosshairsForCentroid(); - } + onZoomChanged(map); map.postInvalidate(); } @@ -594,9 +589,20 @@ public void zoom(final boolean zoomIn) { */ public void setZoom(Map map, int z) { viewBox.setZoom(map, z); + onZoomChanged(map); + } + + /** + * Call this if zoom has changed + * + * @param map the current Map object + */ + private void onZoomChanged(@NonNull Map map) { DataStyle.updateStrokes(strokeWidth(viewBox.getWidth())); if (rotatingWay) { showCrosshairsForCentroid(); + } else if (mode == Mode.MODE_ALIGN_BACKGROUND) { + performBackgroundOffset((Main) map.getContext(), map.getZoomLevel(), 0, 0); } } @@ -1520,13 +1526,11 @@ synchronized void handleTouchEventMove(@NonNull Main main, final float absoluteX startY = absoluteY; startX = absoluteX; main.getEasyEditManager().invalidate(); // if we are in an action mode update menubar + } else if (mode == Mode.MODE_ALIGN_BACKGROUND) { + performBackgroundOffset(main, map.getZoomLevel(), relativeX, relativeY); } else { - if (mode == Mode.MODE_ALIGN_BACKGROUND) { - performBackgroundOffset(main, relativeX, relativeY); - } else { - performTranslation(map, relativeX, relativeY); - main.getEasyEditManager().invalidateOnDownload(); - } + performTranslation(map, relativeX, relativeY); + main.getEasyEditManager().invalidateOnDownload(); } invalidateMap(); } @@ -1592,27 +1596,14 @@ private void performTranslation(@NonNull Map map, final float screenTransX, fina * Converts screen-coords to gps-coords and offsets background layer. * * @param main current instance of Main + * @param zoomLevel the current zoom level * @param screenTransX Movement on the screen. * @param screenTransY Movement on the screen. */ - private void performBackgroundOffset(@NonNull Main main, final float screenTransX, final float screenTransY) { + private void performBackgroundOffset(@NonNull Main main, int zoomLevel, final float screenTransX, final float screenTransY) { ImageryAlignmentActionModeCallback callback = main.getImageryAlignmentActionModeCallback(); if (callback != null) { - TileLayerSource osmts = callback.getLayerSource(); - int height = map.getHeight(); - int lon = xToLonE7(screenTransX); - int lat = yToLatE7(height - screenTransY); - int relativeLon = lon - viewBox.getLeft(); - int relativeLat = lat - viewBox.getBottom(); - - double lonOffset = 0d; - double latOffset = 0d; - Offset o = osmts.getOffset(map.getZoomLevel()); - if (o != null) { - lonOffset = o.getDeltaLon(); - latOffset = o.getDeltaLat(); - } - osmts.setOffset(map.getZoomLevel(), lonOffset - relativeLon / 1E7d, latOffset - relativeLat / 1E7d); + callback.setOffset(zoomLevel, screenTransX, screenTransY); } else { Log.e(DEBUG_TAG, "performBackgroundOffset callback null"); } diff --git a/src/main/java/de/blau/android/Map.java b/src/main/java/de/blau/android/Map.java index efbfca4252..a372f706fb 100755 --- a/src/main/java/de/blau/android/Map.java +++ b/src/main/java/de/blau/android/Map.java @@ -12,7 +12,6 @@ import android.graphics.Bitmap.Config; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.Paint.FontMetrics; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; @@ -21,6 +20,7 @@ import android.net.Uri; import android.os.Build; import android.os.SystemClock; +import android.text.DynamicLayout; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -29,8 +29,8 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import de.blau.android.exception.OsmException; +import de.blau.android.imageryoffset.ImageryAlignmentActionModeCallback; import de.blau.android.imageryoffset.ImageryOffsetUtils; -import de.blau.android.imageryoffset.Offset; import de.blau.android.layer.AttributionInterface; import de.blau.android.layer.ClickableInterface; import de.blau.android.layer.LayerConfig; @@ -139,8 +139,6 @@ public class Map extends View implements IMapView { private Location displayLocation = null; private boolean isFollowingGPS = false; - private Paint textPaint; - /** * support for display a crosshairs at a position */ @@ -162,6 +160,9 @@ public class Map extends View implements IMapView { private Paint gpsAccuracyPaint; private Paint boxPaint; + private int distance2side; + private int offsetPos; + private long timeToStale = 60 * ONE_SECOND_IN_NS; private TrackerService tracker = null; @@ -875,16 +876,16 @@ private void paintStats(@NonNull final Canvas canvas, final float fps) { * @param canvas canvas to draw on */ private void paintZoomAndOffset(@NonNull final Canvas canvas) { - int pos = ThemeUtils.getActionBarHeight(context) + 5 + (int) de.blau.android.layer.grid.MapOverlay.LONGTICKS_DP * 3; - Offset o = ((Main) context).getImageryAlignmentActionModeCallback().getLayerSource().getOffset(zoomLevel); - String text = context.getString(R.string.zoom_and_offset, zoomLevel, o != null ? String.format(Locale.US, "%.5f", o.getDeltaLon()) : "0.00000", - o != null ? String.format(Locale.US, "%.5f", o.getDeltaLat()) : "0.00000"); - float textSize = textPaint.getTextSize(); - float textWidth = textPaint.measureText(text); - FontMetrics fm = textPaint.getFontMetrics(); - float yOffset = pos + textSize; - canvas.drawRect(5, yOffset + fm.bottom, 5 + textWidth, yOffset - textSize, labelBackground); - canvas.drawText(text, 5, pos + textSize, textPaint); + ImageryAlignmentActionModeCallback callback = ((Main) context).getImageryAlignmentActionModeCallback(); + if (callback != null) { + DynamicLayout layout = callback.getZoomAndOffsetLayout(); + canvas.save(); + canvas.translate(distance2side, offsetPos); + canvas.drawRect(0, 0, layout.getWidth(), layout.getHeight(), labelBackground); + canvas.translate((rtlLayout() ? -1f : 1f) * distance2side, 0); // padding + layout.draw(canvas); + canvas.restore(); + } } /** @@ -1093,8 +1094,6 @@ public static boolean activeOverlay(@NonNull String layerId) { public void updateStyle() { // changes when profile changes labelBackground = DataStyle.getInternal(DataStyle.LABELTEXT_BACKGROUND).getPaint(); - FeatureStyle fs = DataStyle.getInternal(DataStyle.LABELTEXT_NORMAL); - textPaint = fs.getPaint(); gpsPosFollowPaint = DataStyle.getInternal(DataStyle.GPS_POS_FOLLOW).getPaint(); gpsPosPaint = DataStyle.getInternal(DataStyle.GPS_POS).getPaint(); gpsPosFollowPaintStale = DataStyle.getInternal(DataStyle.GPS_POS_FOLLOW_STALE).getPaint(); @@ -1102,8 +1101,12 @@ public void updateStyle() { gpsAccuracyPaint = DataStyle.getInternal(DataStyle.GPS_ACCURACY).getPaint(); boxPaint = DataStyle.getInternal(DataStyle.VIEWBOX).getPaint(); for (MapViewLayer layer : getLayers(LayerType.OSMDATA, null)) { - ((de.blau.android.layer.data.MapOverlay) layer).updateStyle(); + ((de.blau.android.layer.data.MapOverlay) layer).updateStyle(); } + // offset display positioning + distance2side = (int) Density.dpToPx(getContext(), de.blau.android.layer.grid.MapOverlay.DISTANCE2SIDE_DP); + int longTicks = (int) Density.dpToPx(getContext(), de.blau.android.layer.grid.MapOverlay.LONGTICKS_DP); + offsetPos = ThemeUtils.getActionBarHeight(context) + distance2side + longTicks; } /** @@ -1289,4 +1292,13 @@ void setTracker(@Nullable TrackerService tracker) { public TrackerService getTracker() { return this.tracker; } + + /** + * Check if we have RTL layout + * + * @return true if RTL + */ + public boolean rtlLayout() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + } } \ No newline at end of file diff --git a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java index b143805076..20bea31214 100644 --- a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java +++ b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java @@ -8,6 +8,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; @@ -23,6 +24,10 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; +import android.text.DynamicLayout; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.TextPaint; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -54,7 +59,9 @@ import de.blau.android.osm.Server; import de.blau.android.osm.ViewBox; import de.blau.android.prefs.Preferences; +import de.blau.android.resources.DataStyle; import de.blau.android.resources.TileLayerSource; +import de.blau.android.util.Density; import de.blau.android.util.ExecutorTask; import de.blau.android.util.GeoMath; import de.blau.android.util.MenuUtil; @@ -76,7 +83,7 @@ */ public class ImageryAlignmentActionModeCallback implements Callback { - private static final String DEBUG_TAG = "BackgroundAlign..."; + private static final String DEBUG_TAG = "ImageryAlign..."; private static final int MENUITEM_QUERYDB = 1; private static final int MENUITEM_APPLY2ALL = 2; @@ -105,6 +112,9 @@ public class ImageryAlignmentActionModeCallback implements Callback { private boolean isService; + private final DynamicLayout zoomAndOffsetLayout; + private final SpannableStringBuilder zoomAndOffsetText; + /** * Construct a new BackgroundAlignmentActionModeCallback * @@ -126,6 +136,13 @@ public ImageryAlignmentActionModeCallback(@NonNull Main main, @NonNull Mode oldM prefs = App.getPreferences(main); String offsetServer = prefs.getOffsetServer(); offsetServerUri = Uri.parse(offsetServer); + + zoomAndOffsetText = new SpannableStringBuilder(); + zoomAndOffsetLayout = new DynamicLayout(zoomAndOffsetText, zoomAndOffsetText, + new TextPaint(DataStyle.getInternal(DataStyle.LABELTEXT_NORMAL).getPaint()), + map.getWidth() - 2 * (int) Density.dpToPx(main, de.blau.android.layer.grid.MapOverlay.DISTANCE2SIDE_DP), + map.rtlLayout() ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, true); + setOffset(map.getZoomLevel(), 0, 0); } /** @@ -250,6 +267,9 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { */ private void reset() { osmts.setOffsets(copy(oldOffsets)); + final int zoomLevel = map.getZoomLevel(); + Offset o = osmts.getOffset(zoomLevel); + setOffset(zoomLevel, o != null ? (float) o.getDeltaLon() : 0f, o != null ? (float) o.getDeltaLat() : 0f); map.invalidate(); } @@ -873,4 +893,47 @@ private AppCompatDialog createDisplayOffsetDialog(final int index) { } return dialog.create(); } + + /** + * Converts screen-coords to gps-coords and offsets background layer. + * + * @param zoomLevel the zoom level + * @param screenTransX Movement on the screen. + * @param screenTransY Movement on the screen. + */ + public void setOffset(final int zoomLevel, final float screenTransX, final float screenTransY) { + int height = map.getHeight(); + Logic logic = App.getLogic(); + int lon = logic.xToLonE7(screenTransX); + int lat = logic.yToLatE7(height - screenTransY); + ViewBox viewBox = logic.getViewBox(); + int relativeLon = lon - viewBox.getLeft(); + int relativeLat = lat - viewBox.getBottom(); + + double lonOffset = 0d; + double latOffset = 0d; + Offset o = osmts.getOffset(zoomLevel); + final boolean hasOffset = o != null; + if (hasOffset) { + lonOffset = o.getDeltaLon(); + latOffset = o.getDeltaLat(); + } + lonOffset = lonOffset - relativeLon / 1E7d; + latOffset = latOffset - relativeLat / 1E7d; + osmts.setOffset(zoomLevel, lonOffset, latOffset); + double[] center = viewBox.getCenter(); + zoomAndOffsetText.replace(0, zoomAndOffsetText.length(), + main.getString(R.string.zoom_and_offsets, zoomLevel, String.format(Locale.US, "%.7f", lonOffset), String.format(Locale.US, "%.7f", latOffset), + String.format(Locale.US, "%.2f", GeoMath.haversineDistance(center[0], center[1], center[0] + lonOffset, center[1])), + String.format(Locale.US, "%.2f", GeoMath.haversineDistance(center[0], center[1], center[0], center[1] + latOffset)))); + + } + + /** + * @return the zoomAndOffsetLayout + */ + @NonNull + public DynamicLayout getZoomAndOffsetLayout() { + return zoomAndOffsetLayout; + } } diff --git a/src/main/java/de/blau/android/layer/grid/MapOverlay.java b/src/main/java/de/blau/android/layer/grid/MapOverlay.java index ccf9f4e6a6..55d9cb593a 100644 --- a/src/main/java/de/blau/android/layer/grid/MapOverlay.java +++ b/src/main/java/de/blau/android/layer/grid/MapOverlay.java @@ -45,7 +45,7 @@ public class MapOverlay extends StyleableLayer implements DiscardInterface, Conf private transient SavingHelper savingHelper = new SavingHelper<>(); - private static final float DISTANCE2SIDE_DP = 4f; + public static final float DISTANCE2SIDE_DP = 4f; private static final float SHORTTICKS_DP = 12f; public static final float LONGTICKS_DP = 20f; private static final double METERS2FEET = 3.28084; diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 89e455d6bd..7ca32a3a00 100755 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -191,7 +191,7 @@ Created Zoom Distance - Z %1$d offset %2$s/%3$s + Zoom %1$d\nOffset\n%2$s°/%3$s°\n%4$sm/%5$sm Geodetic datum WGS84 Name suggestion From 5d5004b0f844ed8b36a4c0ddd0fc4578aed8bba0 Mon Sep 17 00:00:00 2001 From: simonpoole Date: Sat, 23 Sep 2023 16:23:02 +0200 Subject: [PATCH 2/3] Support restarting imagery alignment mode --- .../android/imageryoffset/OffsetModeTest.java | 5 +- .../android/imageryoffset/SaveResumeTest.java | 130 ++++++++++++++++++ src/main/java/de/blau/android/Main.java | 5 + .../ImageryAlignmentActionModeCallback.java | 88 +++++++++++- 4 files changed, 221 insertions(+), 7 deletions(-) create mode 100644 src/androidTest/java/de/blau/android/imageryoffset/SaveResumeTest.java diff --git a/src/androidTest/java/de/blau/android/imageryoffset/OffsetModeTest.java b/src/androidTest/java/de/blau/android/imageryoffset/OffsetModeTest.java index 8adb208aa3..f5ec9271d6 100644 --- a/src/androidTest/java/de/blau/android/imageryoffset/OffsetModeTest.java +++ b/src/androidTest/java/de/blau/android/imageryoffset/OffsetModeTest.java @@ -28,6 +28,7 @@ import de.blau.android.Main; import de.blau.android.Map; import de.blau.android.MockTileServer; +import de.blau.android.Mode; import de.blau.android.R; import de.blau.android.TestUtils; import de.blau.android.exception.OsmException; @@ -96,6 +97,7 @@ public void teardown() { if (main != null) { TestUtils.zoomToNullIsland(logic, map); TestUtils.resetOffsets(main.getMap()); + logic.setMode(main, Mode.MODE_EASYEDIT); main.deleteDatabase(TileLayerDatabase.DATABASE_NAME); main.finish(); } else { @@ -214,6 +216,7 @@ public void abortOffset() { } zoomLevel = map.getZoomLevel(); offset = tileLayerConfiguration.getOffset(zoomLevel); - assertNull(offset); + assertEquals(0D, offset.getDeltaLat(), 0.1E-4); + assertEquals(0D, offset.getDeltaLon(), 0.1E-4); } } diff --git a/src/androidTest/java/de/blau/android/imageryoffset/SaveResumeTest.java b/src/androidTest/java/de/blau/android/imageryoffset/SaveResumeTest.java new file mode 100644 index 0000000000..21ff9f0d5c --- /dev/null +++ b/src/androidTest/java/de/blau/android/imageryoffset/SaveResumeTest.java @@ -0,0 +1,130 @@ +package de.blau.android.imageryoffset; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +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 androidx.lifecycle.Lifecycle.State; +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 androidx.test.uiautomator.UiObject2; +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.TestUtils; +import de.blau.android.exception.OsmException; +import de.blau.android.layer.LayerDialogTest; +import de.blau.android.osm.BoundingBox; +import de.blau.android.prefs.AdvancedPrefDatabase; +import de.blau.android.prefs.Preferences; +import de.blau.android.util.GeoMath; +import okhttp3.mockwebserver.MockWebServer; + +/** + * 1st attempts at testing lifecycle related aspects + * + * @author simon + * + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class SaveResumeTest { + + Context context = null; + AdvancedPrefDatabase prefDB = null; + Main main = null; + UiDevice device = null; + Map map = null; + Logic logic = null; + MockWebServer tileServer = null; + ActivityScenario
scenario = null; + + @Rule + public ActivityScenarioRule
activityScenarioRule = new ActivityScenarioRule<>(Main.class); + + /** + * Pre-test setup + */ + @Before + public void setup() { + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + context = instrumentation.getTargetContext(); + ActivityMonitor monitor = instrumentation.addMonitor(Main.class.getName(), null, false); + scenario = ActivityScenario.launch(Main.class); + main = (Main) instrumentation.waitForMonitorWithTimeout(monitor, 30000); + instrumentation.removeMonitor(monitor); + Preferences prefs = new Preferences(context); + + tileServer = MockTileServer.setupTileServer(main, "ersatz_background.mbt", true); + + map = main.getMap(); + map.setPrefs(main, prefs); + logic = App.getLogic(); + logic.setPrefs(prefs); + TestUtils.resetOffsets(main.getMap()); + TestUtils.grantPermissons(device); + TestUtils.dismissStartUpDialogs(device, main); + TestUtils.stopEasyEdit(main); + TestUtils.zoomToNullIsland(logic, map); + main.invalidateOptionsMenu(); // to be sure that the menu entry is actually shown + } + + /** + * Post-test teardown + */ + @After + public void teardown() { + try { + tileServer.close(); + } catch (IOException e) { + // ignore + } + + scenario.moveToState(State.DESTROYED); + } + + /** + * Start the alignment mode then recreate + */ + @Test + public void startResumeMode() { + TestUtils.zoomToLevel(device, main, 18); + try { + BoundingBox bbox = GeoMath.createBoundingBoxForCoordinates(47.390339D, 8.38782D, 50D); + App.getLogic().getViewBox().setBorders(map, bbox); + map.setViewBox(App.getLogic().getViewBox()); + map.invalidate(); + try { + Thread.sleep(5000); // NOSONAR + } catch (InterruptedException e) { + } + main.invalidateOptionsMenu(); + } catch (OsmException e) { + fail(e.getMessage()); + } + UiObject2 menuButton = TestUtils.getLayerButton(device, "Vespucci Test", LayerDialogTest.MENU_BUTTON); + menuButton.click(); + assertTrue(TestUtils.clickText(device, false, main.getString(R.string.menu_layers_background_align), true, false)); + assertTrue(TestUtils.findText(device, false, main.getString(R.string.menu_tools_background_align))); + scenario.recreate(); + assertTrue(TestUtils.findText(device, false, main.getString(R.string.menu_tools_background_align), 10000)); + } +} diff --git a/src/main/java/de/blau/android/Main.java b/src/main/java/de/blau/android/Main.java index c926488866..88f881e3db 100644 --- a/src/main/java/de/blau/android/Main.java +++ b/src/main/java/de/blau/android/Main.java @@ -766,6 +766,8 @@ protected void onResume() { } else if (mode.elementsEditable()) { // de-select everything logic.deselectAll(); + } else if (mode == Mode.MODE_ALIGN_BACKGROUND) { + ImageryAlignmentActionModeCallback.restart(Main.this); } Intent intent = getIntent(); if (rcData != null || geoData != null || contentUri != null || shortcutExtras != null || (intent != null && intent.getAction() != null)) { @@ -1419,6 +1421,9 @@ protected void onPause() { // always save editing state App.getLogic().saveEditingState(this); + if (imageryAlignmentActionModeCallback != null) { + imageryAlignmentActionModeCallback.saveState(); + } // save tag clipboard App.getTagClipboard(this).save(this); super.onPause(); diff --git a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java index 20bea31214..a44bf35cd6 100644 --- a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java +++ b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; @@ -65,6 +67,8 @@ import de.blau.android.util.ExecutorTask; import de.blau.android.util.GeoMath; import de.blau.android.util.MenuUtil; +import de.blau.android.util.SavingHelper; +import de.blau.android.util.SerializableState; import de.blau.android.util.Snack; import de.blau.android.util.ThemeUtils; import de.blau.android.views.layers.MapTilesLayer; @@ -92,6 +96,13 @@ public class ImageryAlignmentActionModeCallback implements Callback { private static final int MENUITEM_SAVE2DB = 5; private static final int MENUITEM_HELP = 6; + private static final String LAYER_ID_KEY = "layerId"; + private static final String OLD_MODE_KEY = "oldMode"; + private static final String OFFSETS_KEY = "offsets"; + private static final String FILENAME = ImageryAlignmentActionModeCallback.class.getSimpleName() + ".res"; + + private static SavingHelper savingHelper = new SavingHelper<>(); + private final Mode oldMode; private final Preferences prefs; private final Uri offsetServerUri; @@ -120,22 +131,50 @@ public class ImageryAlignmentActionModeCallback implements Callback { * * @param main the current instance of Main * @param oldMode the Mode before we were called + * @param layerId the id for the "layer" we want to adjust */ public ImageryAlignmentActionModeCallback(@NonNull Main main, @NonNull Mode oldMode, @NonNull String layerId) { + this(main, oldMode, layerId, null); + } + + /** + * Construct a new callback from saved state + * + * @param main the current instance of Main + * @param state the saved state + */ + public ImageryAlignmentActionModeCallback(@NonNull Main main, @NonNull SerializableState state) { + this(main, (Mode) state.getSerializable(OLD_MODE_KEY), state.getString(LAYER_ID_KEY), state.getList(OFFSETS_KEY)); + } + + /** + * Actually construct an instance + * + * @param main the current instance of Main + * @param oldMode the Mode before we were called + * @param layerId the id for the "layer" we want to adjust + * @param offsetList the current (original) list of Offsets + */ + private ImageryAlignmentActionModeCallback(@NonNull Main main, @Nullable Mode oldMode, @Nullable String layerId, @Nullable List offsetList) { + this.main = main; this.oldMode = oldMode; - this.main = main; // currently we are only called from here + + prefs = App.getPreferences(main); + String offsetServer = prefs.getOffsetServer(); + offsetServerUri = Uri.parse(offsetServer); map = main.getMap(); + if (layerId == null) { + throw new IllegalStateException("Layer id is null"); + } MapTilesLayer layer = (MapTilesLayer) map.getLayer(layerId); if (layer == null) { throw new IllegalStateException("MapTilesLayer is null"); } osmts = layer.getTileLayerConfiguration(); - isService = osmts.getTileUrl().startsWith(Schemes.HTTP) || osmts.getTileUrl().startsWith(Schemes.HTTPS); - Offset[] offsets = osmts.getOffsets(); + Offset[] offsets = offsetList == null ? osmts.getOffsets() : offsetList.toArray(new Offset[0]); + oldOffsets = copy(offsets); - prefs = App.getPreferences(main); - String offsetServer = prefs.getOffsetServer(); - offsetServerUri = Uri.parse(offsetServer); + isService = osmts.getTileUrl().startsWith(Schemes.HTTP) || osmts.getTileUrl().startsWith(Schemes.HTTPS); zoomAndOffsetText = new SpannableStringBuilder(); zoomAndOffsetLayout = new DynamicLayout(zoomAndOffsetText, zoomAndOffsetText, @@ -936,4 +975,41 @@ public void setOffset(final int zoomLevel, final float screenTransX, final float public DynamicLayout getZoomAndOffsetLayout() { return zoomAndOffsetLayout; } + + /** + * Save any state that is needed to restart + */ + public void saveState() { + SerializableState state = new SerializableState(); + state.putSerializable(OLD_MODE_KEY, oldMode); + state.putString(LAYER_ID_KEY, osmts.getId()); + state.putList(OFFSETS_KEY, Arrays.asList(oldOffsets)); + savingHelper.save(main, FILENAME, state, false, true); + } + + /** + * Restart from saved state + * + * @param main current instance of main + */ + public static void restart(@NonNull Main main) { + new ExecutorTask() { + @Override + protected SerializableState doInBackground(Void param) { + return savingHelper.load(main, FILENAME, false, true, true); + } + + @Override + protected void onPostExecute(SerializableState state) { + if (state != null) { + ImageryAlignmentActionModeCallback callback = new ImageryAlignmentActionModeCallback(main, state); + main.startSupportActionMode(callback); + main.setImageryAlignmentActionModeCallback(callback); + return; + } + Log.e(DEBUG_TAG, "restart, saved state is null"); + App.getLogic().setMode(main, Mode.MODE_EASYEDIT); + } + }.execute(); + } } From e12ea3a63034672bae37310d85daab78d490f8de Mon Sep 17 00:00:00 2001 From: simonpoole Date: Sat, 23 Sep 2023 18:00:10 +0200 Subject: [PATCH 3/3] Remove unused method and add some missing annotations --- src/main/java/de/blau/android/Map.java | 1 - .../ImageryAlignmentActionModeCallback.java | 18 +++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/blau/android/Map.java b/src/main/java/de/blau/android/Map.java index a372f706fb..5348cbc311 100755 --- a/src/main/java/de/blau/android/Map.java +++ b/src/main/java/de/blau/android/Map.java @@ -47,7 +47,6 @@ import de.blau.android.prefs.AdvancedPrefDatabase; import de.blau.android.prefs.Preferences; import de.blau.android.resources.DataStyle; -import de.blau.android.resources.DataStyle.FeatureStyle; import de.blau.android.resources.TileLayerSource; import de.blau.android.resources.TileLayerSource.TileType; import de.blau.android.services.TrackerService; diff --git a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java index a44bf35cd6..58a1966519 100644 --- a/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java +++ b/src/main/java/de/blau/android/imageryoffset/ImageryAlignmentActionModeCallback.java @@ -184,16 +184,6 @@ private ImageryAlignmentActionModeCallback(@NonNull Main main, @Nullable Mode ol setOffset(map.getZoomLevel(), 0, 0); } - /** - * Get the tilelayer we are currently adjusting - * - * @return a TileLayerSource - */ - @NonNull - public TileLayerSource getLayerSource() { - return osmts; - } - /** * Deep copy an Offset array * @@ -793,7 +783,8 @@ private void displayError(@Nullable String error) { * @return a Dialog */ @SuppressLint("InflateParams") - private AppCompatDialog createSaveOffsetDialog(final int index, final List saveOffsetList) { + @NonNull + private AppCompatDialog createSaveOffsetDialog(final int index, @NonNull final List saveOffsetList) { final LayoutInflater inflater = ThemeUtils.getLayoutInflater(main); Builder dialog = new AlertDialog.Builder(main); dialog.setTitle(R.string.imagery_offset_title); @@ -837,8 +828,9 @@ private AppCompatDialog createSaveOffsetDialog(final int index, final List saveOffsetList) { + @NonNull + private OnClickListener createSaveButtonListener(@NonNull final EditText description, @NonNull final EditText author, final int index, + @NonNull final List saveOffsetList) { return (dialog, which) -> { String error = null; ImageryOffset offset = saveOffsetList.get(index);