From 19d0a768fd3f211e2067458966d8438a3cc27f2f Mon Sep 17 00:00:00 2001 From: konggdev Date: Thu, 10 Sep 2026 03:25:59 +0200 Subject: [PATCH] Seperate style source fields from MapSource --- app/build.gradle.kts | 1 + .../strikemaps/helper/UserPrefsHelper.java | 21 ++--- .../konggdev/strikemaps/map/MapComponent.java | 12 ++- .../implementation/MapLibreGLJSRenderer.java | 7 +- .../MapLibreNativeRenderer.java | 12 ++- .../strikemaps/map/source/MapSource.java | 89 +++++-------------- .../source/properties/SourceProperties.java | 14 +++ .../SourceTiles.java} | 21 ++--- .../strikemaps/map/style/MapStyle.java | 11 ++- .../map/style/document/StyleDocument.java | 24 +++-- .../map/style/source/StyleSource.java | 28 ++++++ .../storage/RegistryStorageComponent.java | 10 ++- .../dialog/StyleDetailsBottomSheet.java | 37 +++++--- .../popup/FragmentMapChangePopup.java | 25 ++++-- 14 files changed, 174 insertions(+), 138 deletions(-) create mode 100644 app/src/main/java/eu/konggdev/strikemaps/map/source/properties/SourceProperties.java rename app/src/main/java/eu/konggdev/strikemaps/map/source/{model/TileSource.java => tiles/SourceTiles.java} (69%) create mode 100644 app/src/main/java/eu/konggdev/strikemaps/map/style/source/StyleSource.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4751dd8..9ffa9eb 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { implementation("com.caverock:androidsvg:1.4") implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0") + implementation("commons-codec:commons-codec:1.17.2") implementation("org.apache.commons:commons-lang3:3.14.0") testImplementation(libs.junit) diff --git a/app/src/main/java/eu/konggdev/strikemaps/helper/UserPrefsHelper.java b/app/src/main/java/eu/konggdev/strikemaps/helper/UserPrefsHelper.java index f6d16ad..ba42c12 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/helper/UserPrefsHelper.java +++ b/app/src/main/java/eu/konggdev/strikemaps/helper/UserPrefsHelper.java @@ -5,12 +5,11 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.source.MapSource; -import eu.konggdev.strikemaps.map.source.MapSource.MapSourceContractType; -import eu.konggdev.strikemaps.map.source.model.TileSource; +import eu.konggdev.strikemaps.map.source.tiles.SourceTiles; import eu.konggdev.strikemaps.map.style.MapStyle; -import eu.konggdev.strikemaps.map.style.document.StyleDocument; import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata; import eu.konggdev.strikemaps.map.style.options.StyleOptions; +import org.apache.commons.codec.digest.DigestUtils; import java.lang.reflect.Type; import java.util.HashMap; @@ -36,11 +35,15 @@ public final class UserPrefsHelper { public static Map DEFAULT_STYLES(AppController app) { Map styles = new HashMap<>(); String[] styleAssets = FileHelper.getAssetFiles("bundled/style", ".style.json", app); - for (int i = 0; i < styleAssets.length; i++) { styles.put( i, + for (int i = 0; i < styleAssets.length; i++) { + String styleContents = FileHelper.loadStringFromAssetFile(styleAssets[i], app); + styles.put( i, new MapStyle( - FileHelper.loadStringFromAssetFile(styleAssets[i], app), + styleContents, new StyleOptions(), - new StyleManagementMetadata() + new StyleManagementMetadata( //Set the style to automatically update from assets + false, true, true, + "assets://" + styleAssets[i], DigestUtils.sha256Hex(styleContents)) )); } return styles; @@ -48,16 +51,14 @@ public final class UserPrefsHelper { private static final Map DEFAULT_SOURCES = Map.of( 0, new MapSource( - MapSourceContractType.DEFINITION, "Strike Maps Planet", - new TileSource("https://tiles.strikemaps.eu/planet"), + new SourceTiles("https://tiles.strikemaps.eu/planet"), "vector", "smts" ), 1, new MapSource( - MapSourceContractType.DEFINITION, "ArcGIS Imagery", - new TileSource(new String[]{"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}), + new SourceTiles(new String[]{"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}), "raster", "raster" ) diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java b/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java index 4f5728a..97aca21 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/MapComponent.java @@ -22,7 +22,8 @@ public class MapComponent implements Component { private final MapRenderer mapRenderer; private final AppController app; - public MapStyle style; + public Integer styleId; + public Map, MapOverlay> overlays = new HashMap<>(); public MapComponent(AppController ref) { @@ -48,9 +49,8 @@ public class MapComponent implements Component { return new FragmentLayoutContentMap(mapRenderer.getView()); } - public void setStyle(MapStyle style) { - this.style = style; - mapRenderer.styleUpdate(style.effectiveDocument()); + public void setStyle(Integer id) { + styleId = id; } public void switchOverlay(MapOverlay overlay) { @@ -102,9 +102,7 @@ public class MapComponent implements Component { public void onMapInit() { setStyle( - app.getRegistry().getStyle( - UserPrefsHelper.startupMapStyle(app.getPrefs()) - ) + UserPrefsHelper.startupMapStyle(app.getPrefs()) ); } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreGLJSRenderer.java b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreGLJSRenderer.java index f405a90..5bde459 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreGLJSRenderer.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreGLJSRenderer.java @@ -17,8 +17,8 @@ import eu.konggdev.strikemaps.app.util.JsonPatcher; import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.renderer.MapRenderer; -import eu.konggdev.strikemaps.map.source.MapSource; import eu.konggdev.strikemaps.map.style.document.StyleDocument; +import eu.konggdev.strikemaps.map.style.source.StyleSource; import org.maplibre.android.geometry.LatLng; import org.maplibre.geojson.Feature; @@ -72,8 +72,9 @@ public class MapLibreGLJSRenderer implements MapRenderer { //Sources ObjectNode sources = mapper.createObjectNode(); if (style.sources != null) - for (MapSource source : style.sources) - sources.set(source.name, source.makeJson()); + for (StyleSource source : style.sources) + sources.set(source.key, + source.current.makeJson()); //Layers ArrayNode layers = mapper.createArrayNode(); diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java index 4b1bd3e..10092fd 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/renderer/implementation/MapLibreNativeRenderer.java @@ -10,16 +10,19 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ArrayNode; import eu.konggdev.strikemaps.app.util.JsonPatcher; +import eu.konggdev.strikemaps.map.offline.OfflineTileResolver; import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.renderer.MapRenderer; -import eu.konggdev.strikemaps.map.source.MapSource; import eu.konggdev.strikemaps.map.style.document.StyleDocument; +import eu.konggdev.strikemaps.map.style.source.StyleSource; import org.maplibre.android.MapLibre; import org.maplibre.android.geometry.LatLng; import org.maplibre.android.maps.MapLibreMap; import org.maplibre.android.maps.MapView; import org.maplibre.android.maps.OnMapReadyCallback; import org.maplibre.android.maps.Style; +import org.maplibre.android.module.http.HttpRequestUtil; +import org.maplibre.android.offline.OfflineManager; import org.maplibre.geojson.Feature; import java.util.List; @@ -39,7 +42,9 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { this.controller = controller; MapLibre.getInstance(app.getActivity()); this.mapView = new MapView(app.getActivity()); + //TODO: Investigate if we might want to restore any savedInstanceState for MapLibre mapView.onCreate(null); + //We need this to get the onMapReady callback mapView.getMapAsync(this); } @@ -56,8 +61,9 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { //Sources ObjectNode sources = mapper.createObjectNode(); if (style.sources != null) - for (MapSource source : style.sources) - sources.set(source.name, source.makeJson()); + for (StyleSource source : style.sources) + sources.set(source.key, + source.current.makeJson()); //Layers ArrayNode layers = mapper.createArrayNode(); diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java b/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java index 0f72383..f563af7 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/source/MapSource.java @@ -1,52 +1,14 @@ package eu.konggdev.strikemaps.map.source; -import androidx.annotation.NonNull; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import eu.konggdev.strikemaps.map.source.model.TileSource; +import eu.konggdev.strikemaps.map.source.tiles.SourceTiles; public class MapSource { - public enum MapSourceContractType { - REQUEST, - DEFINITION - } - - /* - * Contract describes the "purpose" of the source. - * - * This exists because we effectively have two types - * of sources: - * - Sources that are the style asking for a specific type of source - * and defining a fallback, which is also used as a default for - * - * and - * - Sources that are the actual source - */ - @NonNull - public final MapSourceContractType contract; - - /* - * For a request contract: - * name is the internal key used by the style. - * - * For a definition contract: - * name is the user-facing source name - * (e.g. "ArcGIS Imagery"). - * - * When converting a request into a definition - * (when importing a style whose requests - * cannot be satisfied), we look for a name - * field and fall back to normalizing the key instead - */ public String name; - /* - * For a request contract, this is the fallback - * for when we absolutely cannot satisfy the requirement, - * or the defaults for converting into a definition contract. - */ - public TileSource tileSource; + public SourceTiles tileSource; public String type; public String schema; @@ -59,41 +21,33 @@ public class MapSource { public String encoding; - public MapSource(@NonNull MapSourceContractType contract, String name, TileSource tileSource, String type, String schema) { - this.contract = contract; + public MapSource(String name, SourceTiles tileSource, String type, String schema) { this.name = name; this.tileSource = tileSource; this.type = type; this.schema = schema; } - private MapSource(@NonNull MapSourceContractType contract) { - this.contract = contract; + public MapSource(String key, JsonNode sourceNode) { + this.name = key; + + this.type = sourceNode.path("type").asText(null); + this.schema = sourceNode.path("schema").asText(null); + this.scheme = sourceNode.path("scheme").asText(null); + this.encoding = sourceNode.path("encoding").asText(null); + + this.minzoom = sourceNode.path("minZoom").asInt(0); + this.maxzoom = sourceNode.path("maxZoom").asInt(24); + this.tileSize = sourceNode.path("tileSize").asInt(256); + + this.tileSource = handleJsonTileSource(sourceNode); } - public static MapSource fromJson(MapSourceContractType contract, String key, JsonNode sourceNode) { - MapSource result = new MapSource(contract); - result.name = key; - - result.schema = sourceNode.path("schema").asText(null); - result.scheme = sourceNode.path("scheme").asText(null); - result.encoding = sourceNode.path("encoding").asText(null); - result.type = sourceNode.path("type").asText(null); - - result.minzoom = sourceNode.path("minZoom").asInt(0); - result.maxzoom = sourceNode.path("maxZoom").asInt(24); - result.tileSize = sourceNode.path("tileSize").asInt(256); - - result.tileSource = handleJsonTileSource(contract, sourceNode); - - return result; - } - - private static TileSource handleJsonTileSource(MapSourceContractType contract, JsonNode sourceNode) { + private static SourceTiles handleJsonTileSource(JsonNode sourceNode) { // By design, a source must use either "url" or "tiles", never both // In case both are present, we prefer URL over tiles... because I don't know, we just do, m'kay? if (sourceNode.has("url")) - return new TileSource(sourceNode.get("url").asText()); + return new SourceTiles(sourceNode.get("url").asText()); if (sourceNode.has("tiles")) { String[] tiles = new String[0]; @@ -105,15 +59,12 @@ public class MapSource { e.printStackTrace(); } - return new TileSource(tiles); + return new SourceTiles(tiles); } - if (contract == MapSourceContractType.REQUEST) //No tile source is only acceptable for a request contract - //TODO: Define an empty tile source? - return null; //TODO: Decide what to do when we have a definition that doesn't define the most important part - the tile source - //Maybe throwing some custom exception, catching it in fromJson calls and propagating it back to the user would be appropriate + //Maybe throwing some custom exception and propagating it back to the user would be appropriate return null; } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/source/properties/SourceProperties.java b/app/src/main/java/eu/konggdev/strikemaps/map/source/properties/SourceProperties.java new file mode 100644 index 0000000..ea19281 --- /dev/null +++ b/app/src/main/java/eu/konggdev/strikemaps/map/source/properties/SourceProperties.java @@ -0,0 +1,14 @@ +package eu.konggdev.strikemaps.map.source.properties; + +import com.fasterxml.jackson.databind.JsonNode; + +public class SourceProperties { + public String type; + public String schema; + + // Json constructor + public SourceProperties(JsonNode sourceNode) { + this.type = sourceNode.path("type").asText(null); + this.schema = sourceNode.path("schema").asText(null); + } +} diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/source/model/TileSource.java b/app/src/main/java/eu/konggdev/strikemaps/map/source/tiles/SourceTiles.java similarity index 69% rename from app/src/main/java/eu/konggdev/strikemaps/map/source/model/TileSource.java rename to app/src/main/java/eu/konggdev/strikemaps/map/source/tiles/SourceTiles.java index 43d38f2..4479976 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/source/model/TileSource.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/source/tiles/SourceTiles.java @@ -1,35 +1,36 @@ -package eu.konggdev.strikemaps.map.source.model; +package eu.konggdev.strikemaps.map.source.tiles; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -public class TileSource { - public enum TileSourceType { +public class SourceTiles { + public enum TilesType { URL, TILES, DATA } - private TileSourceType type; + + private final TilesType type; private String url; private String[] tiles; private JsonNode data; - public TileSource(String url) { + public SourceTiles(String url) { this.url = url; - this.type = TileSourceType.URL; + this.type = TilesType.URL; } - public TileSource(String[] tiles) { + public SourceTiles(String[] tiles) { this.tiles = tiles; - this.type = TileSourceType.TILES; + this.type = TilesType.TILES; } - public TileSource(JsonNode data) { + public SourceTiles(JsonNode data) { this.data = data; - this.type = TileSourceType.DATA; + this.type = TilesType.DATA; } public void makeJson(ObjectMapper mapper, ObjectNode node) { diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java b/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java index e60ab80..afe3327 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/style/MapStyle.java @@ -2,9 +2,11 @@ package eu.konggdev.strikemaps.map.style; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.style.document.StyleDocument; import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata; import eu.konggdev.strikemaps.map.style.options.StyleOptions; +import eu.konggdev.strikemaps.storage.RegistryStorageComponent; public class MapStyle { public static final class StoredRepresentation { @@ -20,13 +22,11 @@ public class MapStyle { } public MapStyle restore() { - MapStyle style = new MapStyle( + return new MapStyle( json, options, managementMetadata ); - - return style; } } @@ -39,7 +39,6 @@ public class MapStyle { // Original json representation of the style document, as we got it @NonNull public final String json; - public MapStyle(@NonNull String json, @NonNull StyleOptions styleOptions, @Nullable StyleManagementMetadata managementMetadata) { this.json = json; this.document = new StyleDocument(json); @@ -58,7 +57,7 @@ public class MapStyle { return new StoredRepresentation(json, options, managementMetadata); } - public StyleDocument effectiveDocument() { - return document.effectiveDocument(options); + public StyleDocument effectiveDocument(RegistryStorageComponent registry) { + return document.effectiveDocument(options, registry); } } diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/style/document/StyleDocument.java b/app/src/main/java/eu/konggdev/strikemaps/map/style/document/StyleDocument.java index ed052d6..e63f500 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/map/style/document/StyleDocument.java +++ b/app/src/main/java/eu/konggdev/strikemaps/map/style/document/StyleDocument.java @@ -1,27 +1,25 @@ package eu.konggdev.strikemaps.map.style.document; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import eu.konggdev.strikemaps.app.AppController; -import eu.konggdev.strikemaps.helper.FileHelper; import eu.konggdev.strikemaps.map.source.MapSource; import eu.konggdev.strikemaps.map.style.options.StyleOptions; +import eu.konggdev.strikemaps.map.style.source.StyleSource; +import eu.konggdev.strikemaps.storage.RegistryStorageComponent; import java.util.ArrayList; import java.util.List; public class StyleDocument { - //Only local data public String name; public String icon; public JsonNode metadata; // everything except layers + sources - public List sources; + public List sources; public ArrayNode layerDefinitions; // "layers" array // Json constructor @@ -35,9 +33,9 @@ public class StyleDocument { this.icon = root.path("icon").asText(); JsonNode jsonSources = root.path("sources"); - List sources = new ArrayList<>(); + List sources = new ArrayList<>(); jsonSources.fields().forEachRemaining(entry -> { - sources.add(MapSource.fromJson(MapSource.MapSourceContractType.REQUEST, entry.getKey(), entry.getValue())); + sources.add(new StyleSource(entry.getKey(), entry.getValue())); }); this.sources = sources; @@ -47,8 +45,8 @@ public class StyleDocument { metadata.remove("layers"); metadata.remove("sources"); this.metadata = metadata; - } catch (Exception e) { + e.printStackTrace(); throw new IllegalArgumentException("Invalid style document", e); } } @@ -63,8 +61,16 @@ public class StyleDocument { } // The style that is presented to the renderer, with its options applied - public StyleDocument effectiveDocument(StyleOptions options) { + public StyleDocument effectiveDocument(StyleOptions options, RegistryStorageComponent registry) { StyleDocument result = new StyleDocument(this); //Copy + for (int i = 0; i < sources.size(); i++) { + StyleSource source = sources.get(i); + MapSource effectiveSource = registry.getSource( + options.getInteger(source.key, 0) + ); + if (effectiveSource != null) source.current = effectiveSource; + } + for (JsonNode layer : result.layerDefinitions) { JsonNode option = layer.get("option"); diff --git a/app/src/main/java/eu/konggdev/strikemaps/map/style/source/StyleSource.java b/app/src/main/java/eu/konggdev/strikemaps/map/style/source/StyleSource.java new file mode 100644 index 0000000..4da19f5 --- /dev/null +++ b/app/src/main/java/eu/konggdev/strikemaps/map/style/source/StyleSource.java @@ -0,0 +1,28 @@ +package eu.konggdev.strikemaps.map.style.source; + +import androidx.annotation.NonNull; +import com.fasterxml.jackson.databind.JsonNode; +import eu.konggdev.strikemaps.map.source.MapSource; +import eu.konggdev.strikemaps.map.source.properties.SourceProperties; + +// Stores a source currently used by a style +// Along with requirements of what source the style needs here +public class StyleSource { + public String key; + + // Requirement properties + public SourceProperties properties; + + // In an effective style, this is the actual source matched to the + // style's requirements according to user's preferences (StyleOptions). + // Otherwise, this is the fallback source. + // It also preserves the default source defined by the JSON for non-effective style documents + @NonNull public MapSource current; + + // Json constructor + public StyleSource(String key, JsonNode sourceNode) { + this.key = key; + this.properties = new SourceProperties(sourceNode); + this.current = new MapSource(key, sourceNode); + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/konggdev/strikemaps/storage/RegistryStorageComponent.java b/app/src/main/java/eu/konggdev/strikemaps/storage/RegistryStorageComponent.java index 598be48..3adc8bd 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/storage/RegistryStorageComponent.java +++ b/app/src/main/java/eu/konggdev/strikemaps/storage/RegistryStorageComponent.java @@ -1,5 +1,6 @@ package eu.konggdev.strikemaps.storage; +import androidx.annotation.Nullable; import eu.konggdev.strikemaps.Component; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.source.MapSource; @@ -8,6 +9,9 @@ import eu.konggdev.strikemaps.helper.UserPrefsHelper; import java.util.Map; +import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata; +import org.apache.commons.codec.digest.DigestUtils; + public class RegistryStorageComponent implements Component { private AppController app; @@ -29,6 +33,10 @@ public class RegistryStorageComponent implements Component { return sources(); } + @Nullable public MapSource getSource(Integer id) { + return sources().get(id); + } + private Map styles() { if (styles == null) styles = UserPrefsHelper.styles(app.getPrefs(), app); return styles; @@ -38,7 +46,7 @@ public class RegistryStorageComponent implements Component { return styles(); } - public MapStyle getStyle(Integer id) { + @Nullable public MapStyle getStyle(Integer id) { return styles().get(id); } diff --git a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/dialog/StyleDetailsBottomSheet.java b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/dialog/StyleDetailsBottomSheet.java index 118fb0f..9a478ab 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/dialog/StyleDetailsBottomSheet.java +++ b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/dialog/StyleDetailsBottomSheet.java @@ -6,6 +6,8 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.activity.result.ActivityResultLauncher; @@ -18,6 +20,7 @@ import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.style.MapStyle; +import eu.konggdev.strikemaps.storage.RegistryStorageComponent; import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory; import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup; @@ -33,15 +36,16 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment { @NonNull UIComponent ui; @NonNull + RegistryStorageComponent registry; + @NonNull final FragmentMapChangePopup mapChangePopup; - private final MapStyle style; - private final Integer id; + private final Integer styleId; private ActivityResultLauncher exportLauncher; void deleteStyle() { - app.getRegistry().deleteStyle(id); + registry.deleteStyle(styleId); mapChangePopup.reloadStyles(); dismiss(); @@ -54,13 +58,13 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment { exportLauncher.launch(intent); } - public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, Integer id) { + public StyleDetailsBottomSheet(AppController app, FragmentMapChangePopup mapChangePopup, Integer styleId) { this.app = app; - this.map = map; - this.ui = ui; + this.map = app.getMap(); + this.ui = app.getUi(); + this.registry = app.getRegistry(); this.mapChangePopup = mapChangePopup; - this.style = style; - this.id = id; + this.styleId = styleId; } @Override @@ -81,7 +85,8 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment { .openOutputStream(uri)) { if (out != null) { - out.write(style.json.getBytes()); + String styleJson = registry.getStyle(styleId).json; + if (styleJson != null) out.write(styleJson.getBytes()); } } catch (IOException e) { @@ -96,20 +101,27 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.dialog_style_details, container, false); + MapStyle style = registry.getStyle(styleId); TextView styleNameView = view.findViewById(R.id.styleName); + LinearLayout styleManagementInfo = view.findViewById(R.id.styleManagementInfo); TextView styleTypeView = view.findViewById(R.id.styleType); + ImageView managedStyleIndicator = view.findViewById(R.id.managedStyleIndicator); - TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert); MaterialCardView editButtonLayout = view.findViewById(R.id.editButton); MaterialCardView copyButtonLayout = view.findViewById(R.id.copyButton); MaterialCardView exportButtonLayout = view.findViewById(R.id.exportButton); MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton); - MaterialCardView closeButtonLayout = view.findViewById(R.id.closeButton); - styleNameView.setText(style.document.name); if (style.managementMetadata != null) { styleTypeView.setText("Built-In Style"); + managedStyleIndicator.setVisibility(View.VISIBLE); + styleManagementInfo.setOnClickListener(v -> + ui.alert( + AlertDialogFactory.styleManagementOptions(app, style.managementMetadata) + ) + ); + } else { styleTypeView.setText("User Style"); } @@ -118,7 +130,6 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment { copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, style.json, mapChangePopup))); exportButtonLayout.setOnClickListener(v -> showExportDialog()); deleteButtonLayout.setOnClickListener(v -> deleteStyle()); - closeButtonLayout.setOnClickListener(v -> dismiss()); return view; } diff --git a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java index 954a94b..d3d1731 100644 --- a/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java +++ b/app/src/main/java/eu/konggdev/strikemaps/ui/fragment/popup/FragmentMapChangePopup.java @@ -18,17 +18,20 @@ import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.style.MapStyle; +import eu.konggdev.strikemaps.storage.RegistryStorageComponent; import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.fragment.dialog.NewStyleBottomSheet; import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet; import eu.konggdev.strikemaps.ui.element.item.GenericItem; import java.util.Map; +import java.util.Objects; public class FragmentMapChangePopup extends Fragment implements Popup { @NonNull AppController app; @NonNull MapComponent map; @NonNull UIComponent ui; + @NonNull RegistryStorageComponent registry; private final Integer region; @@ -49,11 +52,13 @@ public class FragmentMapChangePopup extends Fragment implements Popup { public void reloadStyles() { LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout); stylesLayout.removeAllViews(); - app.getRegistry().getStyles().forEach((id, style) -> - stylesLayout.addView(GenericItem.fromStyle(style.document, app, - () -> map.setStyle(style), - () -> this.styleDetails(style, id)).makeView(ui)) - ); + app.getRegistry().getStyles().forEach((id, style) -> { + View item = new GenericItem( + style.document, + app, + () -> this.setStyle(id), + () -> this.styleDetails(id)).makeView(ui); + }); Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add); stylesLayout.addView(new GenericItem("", addNewIcon, @@ -90,8 +95,13 @@ public class FragmentMapChangePopup extends Fragment implements Popup { new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet"); } - void styleDetails(MapStyle entry, Integer id) { - new StyleDetailsBottomSheet(app, map, ui, this, entry, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet"); + void styleDetails(Integer id) { + new StyleDetailsBottomSheet(app, this, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet"); + } + + void setStyle(Integer id) { + map.setStyle(id); + reloadStyles(); } public FragmentMapChangePopup(AppController app, Integer region) { @@ -99,6 +109,7 @@ public class FragmentMapChangePopup extends Fragment implements Popup { this.app = app; this.map = app.getMap(); this.ui = app.getUi(); + this.registry = app.getRegistry(); this.region = region; }