Compare commits

..
8 Commits
Author SHA1 Message Date
konggd 61a1f588c0 Update classic style 2026-04-16 08:38:16 +02:00
konggd 4698f9dcb6 Fix typo 2026-04-16 06:33:18 +00:00
konggd 549e2e3b67 Fix raster sources 2026-04-16 08:30:56 +02:00
konggd 92d685021b Update README.md 2026-04-15 23:09:28 +00:00
konggd 1e9a421df2 Small fixes 2026-04-16 01:08:09 +02:00
konggd b4bc8feec5 Data namespace 2026-04-16 00:59:17 +02:00
konggd 4a625323d4 Refractor UI factories into UI namespace 2026-04-16 00:56:49 +02:00
konggd 9c9d74d5e4 Furhter work on MapStyle 2026-04-16 00:50:49 +02:00
66 changed files with 284 additions and 2376 deletions
-1
View File
@@ -9,4 +9,3 @@ DS_Store
.cxx .cxx
local.properties local.properties
/legacy_code/ /legacy_code/
maptiler*
+3 -4
View File
@@ -28,8 +28,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_15 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_15 targetCompatibility = JavaVersion.VERSION_11
} }
kotlin { kotlin {
@@ -46,8 +46,6 @@ dependencies {
implementation(libs.constraintlayout) implementation(libs.constraintlayout)
implementation(libs.viewpager2) implementation(libs.viewpager2)
implementation("androidx.core:core-ktx:1.17.0")
implementation("org.osmdroid:osmdroid-android:6.1.20")
implementation("org.maplibre.gl:android-sdk:11.13.0") implementation("org.maplibre.gl:android-sdk:11.13.0")
implementation("com.github.mapsforge.vtm:vtm:0.27.0") implementation("com.github.mapsforge.vtm:vtm:0.27.0")
implementation("com.github.mapsforge.vtm:vtm-android:0.27.0") implementation("com.github.mapsforge.vtm:vtm-android:0.27.0")
@@ -62,6 +60,7 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0") implementation("com.fasterxml.jackson.core:jackson-databind:2.15.0")
implementation("org.apache.commons:commons-lang3:3.14.0") implementation("org.apache.commons:commons-lang3:3.14.0")
implementation(libs.core.ktx)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit) androidTestImplementation(libs.ext.junit)
@@ -5,7 +5,6 @@
"sources": { "sources": {
"satelite": { "satelite": {
"type": "raster", "type": "raster",
"schema" : "raster",
"tiles": [ "tiles": [
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
], ],
@@ -15,6 +14,8 @@
"tileSize": 512 "tileSize": 512
} }
}, },
"sprite": "",
"glyphs": "https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf",
"layers": [ "layers": [
{ {
"id": "satelite", "id": "satelite",
@@ -1,126 +0,0 @@
#
Builds included inside /dist correspond to MapLibre GL JS version 5.24.0 <br>
The source for these builds is available on https://github.com/maplibre/maplibre-gl-js
#
All files inside the /dist folder are under the following license
-------------------------------------------------------------------------------
Copyright (c) 2023, MapLibre contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of MapLibre GL JS nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Contains code from mapbox-gl-js v1.13 and earlier
Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
Copyright (c) 2020, Mapbox
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Mapbox GL JS nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Contains code from glfx.js
Copyright (C) 2011 by Evan Wallace
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
--------------------------------------------------------------------------------
Contains a portion of d3-color https://github.com/d3/d3-color
Copyright 2010-2016 Mike Bostock
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="dist/maplibre-gl.js"></script>
<link href="dist/maplibre-gl.css" rel="stylesheet"/>
<style>
html, body, #map {
margin: 0;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map" style="width: 100%; height: 100%"></div>
<script>
const map = new maplibregl.Map({
center: [13.388, 52.517],
zoom: 9.5,
container: 'map',
attributionControl: false
})
</script>
</body>
</html>
@@ -18,7 +18,6 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
app = new AppController(this); app = new AppController(this);
setContentView(R.layout.view_main);
app.init(); app.init();
} }
@@ -29,10 +28,4 @@ public class MainActivity extends AppCompatActivity {
public void logcat(String log) { public void logcat(String log) {
Log.i("LogcatGeneric", log); Log.i("LogcatGeneric", log);
} }
@Override
public void onBackPressed() {
if (!app.getUi().back())
super.onBackPressed();
}
} }
@@ -7,7 +7,6 @@ import eu.konggdev.strikemaps.MainActivity;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
import static android.content.Context.MODE_PRIVATE; import static android.content.Context.MODE_PRIVATE;
public class AppController { public class AppController {
@@ -35,11 +34,12 @@ public class AppController {
public AppCompatActivity getActivity() { return appActivity; } public AppCompatActivity getActivity() { return appActivity; }
public void init() { public void init() {
if (getActivity().getSupportActionBar() != null) if (getActivity().getSupportActionBar() != null)
getActivity().getSupportActionBar().show(); getActivity().getSupportActionBar().hide();
if(map == null) map = new MapComponent(this); if(map == null) map = new MapComponent(this);
if(ui == null) { if(ui == null) {
ui = new UIComponent(this, map); ui = new UIComponent(this, map);
ui.swapScreen(DefinedScreen.MAIN); //Initial ui.swapScreen(R.layout.screen_main); //Initial
} }
} }
} }
@@ -1,93 +0,0 @@
package eu.konggdev.strikemaps.app.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.stream.StreamSupport;
public final class JsonPatcher {
//Takes a json and a patch and applies it
public static JsonNode patch(JsonNode current, JsonNode patch) throws Exception {
if (current.isObject()) {
ObjectNode result = current.deepCopy();
patch.fields().forEachRemaining(entry -> { //For each
String key = entry.getKey();
JsonNode patchValue = entry.getValue();
if(!result.has(key)) { //Not at all in current
result.set(key, patchValue); //Just append it to result
} else if (result.has(key) && !patchValue.isEmpty()) { //Deeper depth possible
try {
result.set(key, patch(result.get(key), patchValue)); //Patch iteration with patch's value (traverse deeper)
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (result.has(key) && patchValue.isEmpty() && result.get(key).isEmpty()) { //Max depth match
result.set(key, patchValue); //Replace with patch's value
}
});
return result;
}
if (current.isArray()) {
ArrayNode result = current.deepCopy();
for (JsonNode entry : patch) {
if (StreamSupport.stream(result.spliterator(), false)
.noneMatch(node -> node.equals(entry))) { //If no exact entry, add entry (prevents duplicates)
result.add(entry);
}
}
return result;
}
throw new Exception();
}
//Takes an original json, a patched json and a patch and removes that specific patch's changes from it
public static JsonNode unpatch(JsonNode original, JsonNode current, JsonNode patch) throws Exception {
if (current.isObject()) {
ObjectNode result = current.deepCopy(); //Current contains the patch, deepcopy it and operate on the copy
current.fields().forEachRemaining(entry -> {
String key = entry.getKey();
JsonNode value = entry.getValue();
if (!original.has(key) && patch.has(key)) { //Value is in patch and current but was not at all in the original
result.remove(key); //Just remove the value
} else if (!value.isEmpty() && patch.has(key)) { //Depper depth possible
try {
result.set(key, unpatch(original.get(key), result.get(key), patch.get(key))); //Replace with further unpatch of deeper level
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (value.isEmpty() && patch.has(key)) { //Max depth match
result.set(key, original.get(key)); //Replace with original unpatched value
}
});
return result;
}
if (current.isArray()) {
ArrayNode result = current.deepCopy();
for (int i = 0; i < result.size(); i++) {
JsonNode entry = result.get(i);
if (StreamSupport.stream(original.spliterator(), false)
.noneMatch(on -> on.equals(entry)) //If not in original
&&
StreamSupport.stream(patch.spliterator(), false)
.noneMatch(rn -> rn.equals(entry))) { //But also in patch
result.remove(i); //Remove from result
}
}
return result;
}
throw new Exception();
}
}
@@ -26,6 +26,7 @@ public final class FileHelper {
public static String loadStringFromUserFile(String filePath) { public static String loadStringFromUserFile(String filePath) {
File file = new File(filePath); File file = new File(filePath);
try (FileInputStream fis = new FileInputStream(file)) { try (FileInputStream fis = new FileInputStream(file)) {
int size = fis.available(); int size = fis.available();
byte[] buffer = new byte[size]; byte[] buffer = new byte[size];
@@ -37,39 +38,6 @@ public final class FileHelper {
} }
} }
public static void writeUserFile(String path, String fileName, String content, AppController app) throws IOException {
try {
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
if (!userDirectory.exists() && !userDirectory.mkdirs()) {
app.logcat("Failed to create directory: " + userDirectory.getAbsolutePath());
return;
}
File file = new File(userDirectory, fileName);
try (FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
writer.write(content);
writer.flush();
}
} catch (IOException e) {
throw e;
}
}
public static boolean deleteFile(String filePath) {
File file = new File(filePath);
if (!file.exists() || !file.isFile()) {
return false;
}
return file.delete();
}
public static String[] getAssetFiles(String path, String fileExt, AppController app) { public static String[] getAssetFiles(String path, String fileExt, AppController app) {
AssetManager assetManager = app.getActivity().getAssets(); AssetManager assetManager = app.getActivity().getAssets();
try { try {
@@ -109,20 +77,9 @@ public final class FileHelper {
} }
} }
public static boolean userFileExists(String path, String fileName, AppController app) {
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
if (!userDirectory.exists() || !userDirectory.isDirectory()) {
return false;
}
File file = new File(userDirectory, fileName);
return file.exists() && file.isFile();
}
public static String[] getUserFiles(String path, String fileExt, AppController app) { public static String[] getUserFiles(String path, String fileExt, AppController app) {
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path); String packageName = app.getActivity().getPackageName();
File userDirectory = new File(Environment.getExternalStorageDirectory(), "Android/data/" + packageName + "/" + path);
if (!userDirectory.exists() || !userDirectory.isDirectory()) if (!userDirectory.exists() || !userDirectory.isDirectory())
return new String[0]; return new String[0];
@@ -13,24 +13,17 @@ public final class UserPrefsHelper {
//Defaults //Defaults
private static final String DEFAULT_MAP_STYLE = "bundled/style/classic.style.json"; private static final String DEFAULT_MAP_STYLE = "bundled/style/classic.style.json";
private static final Integer DEFAULT_MAP_RENDERER = 0; private static final String DEFAULT_MAP_RENDERER = "mapLibre";
private static final boolean DEFAULT_PERSIST_LOCATION_ENABLED = true; private static final boolean DEFAULT_PERSIST_LOCATION_ENABLED = true;
private static final boolean DEFAULT_LAST_LOCATION_ENABLED = false; private static final boolean DEFAULT_LAST_LOCATION_ENABLED = false;
public static String startupMapStyle(SharedPreferences prefs) { public static String startupMapStyle(SharedPreferences prefs) {
return prefs.getString(KEY_STARTUP_MAP_STYLE, DEFAULT_MAP_STYLE); return prefs.getString(KEY_STARTUP_MAP_STYLE, DEFAULT_MAP_STYLE);
} }
public static boolean startupMapStyle(SharedPreferences prefs, String updated) { public static String mapRenderer(SharedPreferences prefs) {
return prefs.edit().putString(KEY_STARTUP_MAP_STYLE, updated).commit(); return prefs.getString(KEY_MAP_RENDERER, DEFAULT_MAP_RENDERER);
}
public static Integer mapRenderer(SharedPreferences prefs) {
return prefs.getInt(KEY_MAP_RENDERER, DEFAULT_MAP_RENDERER);
}
public static boolean mapRenderer(SharedPreferences prefs, Integer updated) {
return prefs.edit().putInt(KEY_MAP_RENDERER, updated).commit();
} }
public static boolean persistLocationEnabled(SharedPreferences prefs) { public static boolean persistLocationEnabled(SharedPreferences prefs) {
@@ -1,10 +1,7 @@
package eu.konggdev.strikemaps.map; package eu.konggdev.strikemaps.map;
import java.util.*; import java.util.*;
import android.widget.Toast;
import eu.konggdev.strikemaps.Component; import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory; import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper; import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer; import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer;
@@ -24,22 +21,15 @@ public class MapComponent implements Component {
public MapStyle style; public MapStyle style;
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>(); public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
public MapComponent(AppController ref) { public MapComponent(AppController ref) {
this.app = ref; this.app = ref;
switch(UserPrefsHelper.mapRenderer(app.getPrefs())) { switch(UserPrefsHelper.mapRenderer(app.getPrefs())) {
case 0: case "vtm":
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
break;
case 1:
this.mapRenderer = new MapLibreNativeRenderer(app, this);
break;
case 2:
this.mapRenderer = new VtmRenderer(app, this); this.mapRenderer = new VtmRenderer(app, this);
break; break;
case "mapLibre":
default: //This shouldn't happen default: //This shouldn't happen
Toast.makeText(app.getActivity(), "Invalid renderer value in preferences\nFalling back to MapLibre GL JS", Toast.LENGTH_SHORT).show(); this.mapRenderer = new MapLibreNativeRenderer(app, this);
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
break; break;
}; };
} }
@@ -50,13 +40,13 @@ public class MapComponent implements Component {
public void setStyle(MapStyle style) { public void setStyle(MapStyle style) {
this.style = style; this.style = style;
mapRenderer.styleUpdate(style); mapRenderer.reload();
} }
public void switchOverlay(MapOverlay overlay) { public void switchOverlay(MapOverlay overlay) {
if (hasOverlay(overlay)) overlays.remove(overlay.getClass()); if (hasOverlay(overlay)) overlays.remove(overlay.getClass());
else overlays.put(overlay.getClass(), overlay); else overlays.put(overlay.getClass(), overlay);
overlayUpdate(overlay); update();
} }
public boolean hasOverlay(MapOverlay overlay) { public boolean hasOverlay(MapOverlay overlay) {
@@ -71,8 +61,12 @@ public class MapComponent implements Component {
//FIXME: Put back FragmentPointPreviewPopup (private code atm) //FIXME: Put back FragmentPointPreviewPopup (private code atm)
} }
public void overlayUpdate(MapOverlay in) { public void onOverlayUpdate() {
mapRenderer.overlayUpdate(in); update();
}
public void update() {
if(mapRenderer != null && style != null) mapRenderer.reload();
} }
public boolean onMapClick(LatLng point) { public boolean onMapClick(LatLng point) {
@@ -0,0 +1,16 @@
package eu.konggdev.strikemaps.map.layer;
import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.style.sources.GeoJsonSource;
//FIXME: Get rid of reliance on MapLibre!
//Most likely implement an "AdditionalMapLayer" or something of that sorts (?)
public class MapLayer {
public GeoJsonSource source;
public Layer layer;
public MapLayer(GeoJsonSource source, Layer layer) {
this.source = source;
this.layer = layer;
}
}
@@ -1,10 +0,0 @@
package eu.konggdev.strikemaps.map.offline;
import java.io.IOException;
public final class OfflineTileResolver {
public byte[] resolve(String url) throws IOException {
//byte[] data = getOfflineTile();
return null;
}
}
@@ -1,22 +0,0 @@
package eu.konggdev.strikemaps.map.offline.download;
import eu.konggdev.strikemaps.map.source.MapSource;
import java.net.URI;
public final class OfflineDownloader {
public static String[] fetchAvailableExports(MapSource source) {
URI uri = URI.create(source.url);
String host = uri.getHost();
return new String[0];
}
public static void download(MapSource source, String export) {
//TODO
}
public static void download(MapSource source, int[] bounds) {
//TODO
}
}
@@ -1,8 +1,8 @@
package eu.konggdev.strikemaps.map.overlay; package eu.konggdev.strikemaps.map.overlay;
import com.fasterxml.jackson.databind.JsonNode; import eu.konggdev.strikemaps.map.layer.MapLayer;
/* More or less a data-driven layer factory */ /* More or less a data-driven layer factory */
public interface MapOverlay { public interface MapOverlay {
public JsonNode makePatch(); public MapLayer makeLayer();
} }
@@ -1,19 +1,23 @@
package eu.konggdev.strikemaps.map.overlay.implementation; package eu.konggdev.strikemaps.map.overlay.implementation;
import android.graphics.Color;
import android.location.Location; import android.location.Location;
import android.location.LocationListener; import android.location.LocationListener;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.data.provider.LocationDataProvider; import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
import org.maplibre.android.style.layers.CircleLayer;
import org.maplibre.android.style.layers.Property;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.geojson.Feature;
import org.maplibre.geojson.FeatureCollection;
import org.maplibre.geojson.Point;
import com.fasterxml.jackson.databind.ObjectMapper; import static org.maplibre.android.style.layers.PropertyFactory.*;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
public class LocationOverlay implements MapOverlay, LocationListener { public class LocationOverlay implements MapOverlay, LocationListener {
LocationDataProvider locationDataProvider; LocationDataProvider locationDataProvider;
@@ -29,60 +33,30 @@ public class LocationOverlay implements MapOverlay, LocationListener {
} }
@Override @Override
public JsonNode makePatch() { public MapLayer makeLayer() {
ObjectMapper mapper = new ObjectMapper(); GeoJsonSource source = new GeoJsonSource(
"location",
FeatureCollection.fromFeatures(new Feature[]{}) // empty
);
ObjectNode root = mapper.createObjectNode(); if (currentLocation != null)
source.setGeoJson(Feature.fromGeometry(Point.fromLngLat(currentLocation.getLongitude(), currentLocation.getLatitude())));
ObjectNode sources = mapper.createObjectNode(); CircleLayer layer = new CircleLayer("location", "location");
ObjectNode location = mapper.createObjectNode(); layer.setProperties(
ObjectNode data = mapper.createObjectNode(); circleRadius(5f),
ObjectNode geometry = mapper.createObjectNode(); circleColor(Color.parseColor("#1E88E5")),
ObjectNode properties = mapper.createObjectNode(); circleStrokeColor(Color.WHITE),
circleStrokeWidth(1.5f),
circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP)
);
ArrayNode coordinates = mapper.createArrayNode(); return new MapLayer(source, layer);
coordinates.add(currentLocation.getLongitude());
coordinates.add(currentLocation.getLatitude());
geometry.put("type", "Point");
geometry.set("coordinates", coordinates);
data.put("type", "Feature");
data.set("geometry", geometry);
data.set("properties", properties);
location.put("type", "geojson");
location.set("data", data);
sources.set("location", location);
root.set("sources", sources);
// layers
ArrayNode layers = mapper.createArrayNode();
ObjectNode layer = mapper.createObjectNode();
layer.put("id", "location");
layer.put("type", "circle");
layer.put("source", "location");
ObjectNode paint = mapper.createObjectNode();
paint.put("circle-radius", 5);
paint.put("circle-color", "#1E88E5");
paint.put("circle-stroke-color", "#FFFFFF");
paint.put("circle-stroke-width", 1.5);
layer.set("paint", paint);
layers.add(layer);
root.set("layers", layers);
return root;
} }
@Override @Override
public void onLocationChanged(@NonNull Location location) { public void onLocationChanged(@NonNull Location location) {
this.currentLocation = location; this.currentLocation = location;
map.overlayUpdate(this); map.onOverlayUpdate();
} }
} }
@@ -1,15 +1,11 @@
package eu.konggdev.strikemaps.map.overlay.implementation; package eu.konggdev.strikemaps.map.overlay.implementation;
import com.fasterxml.jackson.databind.JsonNode; import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.overlay.MapOverlay;
public class PointSelectionOverlay implements MapOverlay { public class PointSelectionOverlay implements MapOverlay {
AppController app;
MapComponent map;
@Override @Override
public JsonNode makePatch() { public MapLayer makeLayer() {
return null; return null;
} }
} }
@@ -1,21 +1,19 @@
package eu.konggdev.strikemaps.map.renderer; package eu.konggdev.strikemaps.map.renderer;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.style.MapStyle; import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.geometry.LatLng; import org.maplibre.android.geometry.LatLng;
import org.maplibre.geojson.Feature; import org.maplibre.geojson.Feature;
import java.util.List; import java.util.List;
public interface MapRenderer { public interface MapRenderer {
void styleUpdate(MapStyle style); void reload();
void overlayUpdate(MapOverlay overlay);
View getView(); View getView();
//TODO: Get rid of MapLibre Feature class dependence
List<Feature> featuresAtPoint(LatLng point); List<Feature> featuresAtPoint(LatLng point);
} }
@@ -1,149 +0,0 @@
package eu.konggdev.strikemaps.map.renderer.implementation;
import android.annotation.SuppressLint;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonInclude;
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.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.style.MapStyle;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.maps.Style;
import org.maplibre.geojson.Feature;
import java.util.Collections;
import java.util.List;
//Stub for now
public class MapLibreGLJSRenderer implements MapRenderer {
@NonNull AppController app;
@NonNull MapComponent controller;
final WebView webView;
private JsonNode origin;
@SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
public MapLibreGLJSRenderer(AppController app, MapComponent controller) {
this.app = app;
this.controller = controller;
webView = new WebView(app.getActivity());
webView.setLayoutParams(
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
);
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
webView.setWebChromeClient(new WebChromeClient());
webView.addJavascriptInterface(new Bridge(), "AndroidBridge");
webView.loadUrl("file:///android_asset/library/maplibre/gl-js/index.html");
}
@Override
public void styleUpdate(MapStyle style) {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//Thanks to this, styleUpdate(null) can be used as an origin reload
if (style != null) {
try {
ObjectNode root = style.metadata.deepCopy();
//Sources
ObjectNode sources = mapper.createObjectNode();
if (style.sources != null)
style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v)));
//Layers
ArrayNode layers = mapper.createArrayNode();
if (style.layerDefinitions != null)
layers.addAll((ArrayNode) style.layerDefinitions);
//Set all to root
root.set("sources", sources);
root.set("layers", layers);
this.origin = root;
} catch (Exception e) {
app.logcat("Failed to parse style: " + style.name);
e.printStackTrace();
}
}
try {
final String mapped = mapper.writeValueAsString(origin);
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
} catch (Exception e) {
app.logcat("Failed to set style: " + style.name);
e.printStackTrace();
}
//Since we just annihilated all overlays from the face of the earth, lets repatch them
if (controller.overlays != null) {
for(MapOverlay overlay : controller.overlays.values())
overlayUpdate(overlay);
}
}
@Override
public void overlayUpdate(MapOverlay overlay) {
ObjectMapper mapper = new ObjectMapper();
webView.evaluateJavascript(
"JSON.stringify(map.getStyle())",
style -> {
try {
JsonNode current = mapper.readTree(style);
JsonNode merged;
if (controller.hasOverlay(overlay)) {
merged = JsonPatcher.patch(current, overlay.makePatch());
} else {
merged = JsonPatcher.unpatch(origin, current, overlay.makePatch());
}
final String mapped = mapper.writeValueAsString(merged);
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
} catch (Exception e) {
app.logcat("Failed to patch overlay: " + overlay.toString());
e.printStackTrace();
}
}
);
}
@Override
public View getView() {
return webView;
}
@Override
public List<Feature> featuresAtPoint(LatLng point) {
return Collections.emptyList();
}
class Bridge { }
}
@@ -4,14 +4,11 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; 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.data.helper.UserPrefsHelper; import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.overlay.MapOverlay; import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.renderer.MapRenderer; import eu.konggdev.strikemaps.map.renderer.MapRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.map.style.MapStyle;
import org.maplibre.android.MapLibre; import org.maplibre.android.MapLibre;
@@ -28,13 +25,11 @@ import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.MapComponent;
public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback { public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
@NonNull AppController app; AppController app;
@NonNull MapComponent controller; MapComponent controller;
MapLibreMap map; MapLibreMap map;
final MapView mapView; final MapView mapView;
private JsonNode origin;
public MapLibreNativeRenderer(AppController app, MapComponent controller) { public MapLibreNativeRenderer(AppController app, MapComponent controller) {
this.app = app; this.app = app;
this.controller = controller; this.controller = controller;
@@ -44,72 +39,25 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
mapView.getMapAsync(this); mapView.getMapAsync(this);
} }
@Override void passLayer(MapLayer layer) {
public void styleUpdate(MapStyle style) { map.getStyle().addSource(layer.source);
ObjectMapper mapper = new ObjectMapper(); map.getStyle().addLayer(layer.layer);
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); }
//Thanks to this, styleUpdate(null) can be used as an origin reload @Override
if (style != null) { public void reload() {
ObjectMapper mapper = new ObjectMapper();
MapStyle style = controller.style;
try { try {
ObjectNode root = style.metadata.deepCopy(); ObjectNode root = style.metadata.deepCopy();
root.set("sources", mapper.valueToTree(style.sources));
//Sources root.set("layers", style.layerDefinitions);
ObjectNode sources = mapper.createObjectNode(); map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(root)), intStyle -> {
if (style.sources != null) for(MapOverlay overlay : controller.overlays.values()) {
style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v))); passLayer(overlay.makeLayer());
}
//Layers });
ArrayNode layers = mapper.createArrayNode();
if (style.layerDefinitions != null)
layers.addAll((ArrayNode) style.layerDefinitions);
//Set all to root
root.set("sources", sources);
root.set("layers", layers);
this.origin = root;
} catch (Exception e) { } catch (Exception e) {
app.logcat("Failed to parse style: " + style.name);
e.printStackTrace();
}
}
try {
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(origin)));
} catch (Exception e) {
app.logcat("Failed to set style: " + style.name);
e.printStackTrace();
}
//Since we just annihilated all overlays from the face of the earth, lets repatch them
if (controller.overlays != null) {
for(MapOverlay overlay : controller.overlays.values())
overlayUpdate(overlay);
}
}
@Override
public void overlayUpdate(MapOverlay overlay) {
if(map == null) return;
Style style = map.getStyle();
ObjectMapper mapper = new ObjectMapper();
if(style == null) return;
try {
JsonNode current = mapper.readTree(style.getJson());
JsonNode merged;
if (controller.hasOverlay(overlay)) {
merged = JsonPatcher.patch(current, overlay.makePatch());
} else {
merged = JsonPatcher.unpatch(origin, current, overlay.makePatch());
}
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(merged)));
} catch (Exception e) {
app.logcat("Failed to patch overlay: " + overlay.toString());
e.printStackTrace(); e.printStackTrace();
} }
} }
@@ -128,7 +76,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
public void onMapReady(@NonNull MapLibreMap maplibreMap) { public void onMapReady(@NonNull MapLibreMap maplibreMap) {
this.map = maplibreMap; this.map = maplibreMap;
controller.setStyle(MapStyle.fromFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app)); controller.setStyle(MapStyle.fromMapLibreJsonFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app));
//I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :) //I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :)
map.getUiSettings().setLogoEnabled(false); map.getUiSettings().setLogoEnabled(false);
@@ -3,9 +3,7 @@ package eu.konggdev.strikemaps.map.renderer.implementation;
import android.view.View; import android.view.View;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent; 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.renderer.MapRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import org.maplibre.android.geometry.LatLng; import org.maplibre.android.geometry.LatLng;
import org.maplibre.geojson.Feature; import org.maplibre.geojson.Feature;
@@ -18,7 +16,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
public class VtmRenderer implements MapRenderer { public class VtmRenderer implements MapRenderer {
AppController app; AppController app;
MapComponent controller; MapComponent controller;
@@ -33,12 +30,7 @@ public class VtmRenderer implements MapRenderer {
} }
@Override @Override
public void overlayUpdate(MapOverlay overlay) { public void reload() {
//TODO
}
@Override
public void styleUpdate(MapStyle style) {
//TODO //TODO
OkHttpClient.Builder builder = new OkHttpClient.Builder(); OkHttpClient.Builder builder = new OkHttpClient.Builder();
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build(); OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
@@ -4,18 +4,15 @@ import com.fasterxml.jackson.databind.JsonNode;
public class MapSource { public class MapSource {
public String url; public String url;
public JsonNode data;
public String type; public String type;
public String schema; public String schema;
public String attribution;
/* For raster sources */
public JsonNode tiles; public JsonNode tiles;
public int minzoom; public int minzoom;
public int maxzoom; public int maxzoom;
public String scheme;
public int tileSize;
public String attribution;
public String encoding;
public MapSource() { } public MapSource() { }
} }
@@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.data.helper.FileHelper; import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.source.MapSource; import eu.konggdev.strikemaps.map.source.MapSource;
@@ -20,10 +19,10 @@ public class MapStyle {
public JsonNode metadata; // everything except layers + sources public JsonNode metadata; // everything except layers + sources
public Map<String, MapSource> sources; public Map<String, MapSource> sources;
public ArrayNode layerDefinitions; // "layers" array public JsonNode layerDefinitions; // the "layers" array
//FIXME //FIXME
public static MapStyle fromFile(String filename, AppController app) { public static MapStyle fromMapLibreJsonFile(String filename, AppController app) {
String styleContents; String styleContents;
if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename); if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename);
else styleContents = FileHelper.loadStringFromAssetFile(filename, app); else styleContents = FileHelper.loadStringFromAssetFile(filename, app);
@@ -41,7 +40,7 @@ public class MapStyle {
new TypeReference<Map<String, MapSource>>() {} new TypeReference<Map<String, MapSource>>() {}
); );
style.layerDefinitions = root.withArray("layers"); style.layerDefinitions = root.path("layers");
ObjectNode metadata = root.deepCopy(); ObjectNode metadata = root.deepCopy();
metadata.remove("layers"); metadata.remove("layers");
@@ -3,83 +3,64 @@ package eu.konggdev.strikemaps.ui;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar; import com.google.common.collect.BiMap;
import eu.konggdev.strikemaps.Component; import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent; import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.ui.element.region.content.MainContentRegion; import eu.konggdev.strikemaps.ui.element.UIRegion;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutControls; import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutControls;
import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutSearch;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentOfflineMaps;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentSettings; import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentSettings;
import eu.konggdev.strikemaps.ui.screen.Screen; import eu.konggdev.strikemaps.ui.screen.Screen;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
import java.util.ArrayDeque; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
public class UIComponent implements Component { public class UIComponent implements Component {
@NonNull AppController app; @NonNull AppController app;
MapComponent map; private Map<Integer, Screen> screens;
private Integer currentScreen;
private final ArrayDeque<Screen> screenStack = new ArrayDeque<>();
public UIComponent(AppController app, MapComponent map) { public UIComponent(AppController app, MapComponent map) {
this.app = app; this.app = app;
this.map = map; this.screens = Map.of(
}
public Map<DefinedScreen, Screen> getScreens(MapComponent map) {
return Map.of(
//Main screen //Main screen
DefinedScreen.MAIN, new Screen( R.layout.screen_main, new Screen(
//App reference //App reference
app, app,
//Map view
map.toFragment(), //FragmentLayoutContentMap
//Main screen init regions definition //Main screen init regions definition
Map.of( Map.of(R.id.bottomUi, new UIRegion(new FragmentLayoutControls(app, R.id.bottomUi), R.id.bottomUi)), //TODO: Probably stop referencing layout 3(!) times everytime
R.id.mainContentView, new MainContentRegion(map.toFragment(), R.id.mainContentView), //Layout
R.id.bottomUi, new UIRegion(new FragmentLayoutControls(app, R.id.bottomUi), R.id.bottomUi), R.layout.screen_main //TODO: Define this for the Screen without duplicating the reference
R.id.topUi, new UIRegion(new FragmentLayoutSearch(app, R.id.topUi), R.id.topUi)
) //TODO: Probably stop referencing layout 3(!) times everytime
), ),
//Settings screen //Settings screen
DefinedScreen.SETTINGS, new Screen( R.layout.screen_settings, new Screen(
app, app,
//Just the settings content fragment //Settings
Map.of( new FragmentLayoutContentSettings(),
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentSettings(app), R.id.mainContentView) /* No regions defined in settings
) Entire screen is just the main view */
), new HashMap<>(),
//Offline maps screen //Layout
DefinedScreen.OFFLINE, new Screen( R.layout.screen_settings
app,
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentOfflineMaps(app), R.id.mainContentView)
)
) )
); );
} }
public void swapScreen(DefinedScreen screenKey) { public void swapScreen(Integer screen) {
if (!screenStack.isEmpty()) getCurrentScreen().detachAll(); currentScreen = screen;
screenStack.add(getScreens(map).get(screenKey));
getCurrentScreen().attachAll(); getCurrentScreen().attachAll();
} }
public boolean back() {
if (screenStack.size() <= 1) return false;
getCurrentScreen().detachAll();
screenStack.removeLast();
getCurrentScreen().attachAll();
return true;
}
public Screen getCurrentScreen() { public Screen getCurrentScreen() {
return screenStack.getLast(); return getScreen(currentScreen);
}
public Screen getScreen(Integer screen) {
return screens.get(screen);
} }
public void alert(AlertDialog dialog) { public void alert(AlertDialog dialog) {
@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.ui.element.region; package eu.konggdev.strikemaps.ui.element;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
@@ -40,4 +40,5 @@ public class UIRegion {
currentFragment = stockFragment; currentFragment = stockFragment;
} }
} }
} }
@@ -15,7 +15,6 @@ public class GenericItem implements UIItem {
@NonNull public String name; @NonNull public String name;
public Bitmap image; public Bitmap image;
public Runnable onClick; public Runnable onClick;
public Runnable onLongClick;
boolean hasImage; boolean hasImage;
public GenericItem(String refName) { public GenericItem(String refName) {
@@ -27,12 +26,6 @@ public class GenericItem implements UIItem {
this.onClick = onClick; this.onClick = onClick;
hasImage = false; hasImage = false;
} }
public GenericItem(String refName, Runnable onClick, Runnable onLongClick) {
this.name = refName;
this.onClick = onClick;
this.onLongClick = onLongClick;
hasImage = false;
}
public GenericItem(String refName, Bitmap refImage) { public GenericItem(String refName, Bitmap refImage) {
this.name = refName; this.name = refName;
this.image = refImage; this.image = refImage;
@@ -45,34 +38,18 @@ public class GenericItem implements UIItem {
hasImage = true; hasImage = true;
} }
public GenericItem(String refName, Bitmap refImage, Runnable onClick, Runnable onLongClick) { public final static GenericItem fromStyle(MapStyle style, MapComponent map) {
this.name = refName;
this.image = refImage;
this.onClick = onClick;
this.onLongClick = onLongClick;
hasImage = true;
}
public final static GenericItem fromStyle(MapStyle style, MapComponent map, Runnable onClick) {
if(style == null) return new GenericItem("Unknown"); if(style == null) return new GenericItem("Unknown");
if(style.icon != null) if(style.icon != null)
return new GenericItem(style.name, style.icon, onClick); return new GenericItem(style.name, style.icon, () -> map.setStyle(style));
return new GenericItem(style.name, onClick); return new GenericItem(style.name, () -> map.setStyle(style));
} }
public final static GenericItem fromStyle(MapStyle style, MapComponent map, Runnable onClick, Runnable onLongClick) {
if(style == null) return new GenericItem("Unknown");
if(style.icon != null)
return new GenericItem(style.name, style.icon, onClick, onLongClick);
return new GenericItem(style.name, onClick, onLongClick);
}
public View makeView(UIComponent spawner) { public View makeView(UIComponent spawner) {
View v = spawner.inflateUi(R.layout.item_generic); View v = spawner.inflateUi(R.layout.item_generic);
//FIXME: These shouldn't be casted like that! //FIXME: These shouldn't be casted like that!
((TextView) v.findViewById(R.id.name)).setText(name); ((TextView) v.findViewById(R.id.name)).setText(name);
if(image != null) ((ImageButton) v.findViewById(R.id.image)).setImageBitmap(image); if(image != null) ((ImageButton) v.findViewById(R.id.image)).setImageBitmap(image);
if(onClick != null) v.findViewById(R.id.image).setOnClickListener(click(onClick)); if(onClick != null) v.findViewById(R.id.image).setOnClickListener(click(onClick));
if(onLongClick != null) v.findViewById(R.id.image).setOnLongClickListener(longClick(onLongClick));
return v; return v;
} }
@@ -1,28 +0,0 @@
package eu.konggdev.strikemaps.ui.element.item;
import android.view.View;
import android.widget.TextView;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.ui.UIComponent;
public class InlineItem implements UIItem {
public String text;
public Runnable onClick;
public InlineItem(String refText) {
this.text = refText;
}
public InlineItem(String refText, Runnable onClick) {
this.text = refText;
this.onClick = onClick;
}
@Override
public View makeView(UIComponent spawner) {
View view = spawner.inflateUi(R.layout.item_inline);
((TextView) view.findViewById(R.id.inline)).setText(text);
if(onClick != null) view.findViewById(R.id.inline).setOnClickListener(click(onClick));
return view;
}
}
@@ -4,36 +4,33 @@ import android.graphics.Bitmap;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.ui.UIComponent; import eu.konggdev.strikemaps.ui.UIComponent;
import org.maplibre.geojson.Feature; import org.maplibre.geojson.Feature;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
public class PreviewItem implements UIItem { public class PreviewItem implements UIItem {
@NonNull public String name; public String name;
public String details; public String type;
public Bitmap image; public Bitmap image;
boolean hasImage; boolean hasImage;
public PreviewItem(String refName, String refType) { public PreviewItem(String refName, String refType) {
this.name = refName; this.name = refName;
this.details = refType; this.type = refType;
hasImage = false; hasImage = false;
} }
public PreviewItem(String refName, String refType, Bitmap refImage) { public PreviewItem(String refName, String refType, Bitmap refImage) {
this.name = refName; this.name = refName;
this.details = refType; this.type = refType;
this.image = refImage; this.image = refImage;
hasImage = true; hasImage = true;
} }
public static PreviewItem fromFeature(Feature feature) { public static PreviewItem fromFeature(Feature feature) {
return new PreviewItem(feature.getStringProperty("name"), feature.getStringProperty("class")); return new PreviewItem(feature.getStringProperty("name"), feature.getStringProperty("class"));
} }
public View makeView(UIComponent spawner) { public View makeView(UIComponent spawner) {
View view = spawner.inflateUi(R.layout.item_preview); View view = spawner.inflateUi(R.layout.item_preview);
((TextView) view.findViewById(R.id.choiceName)).setText(name); ((TextView) view.findViewById(R.id.choiceName)).setText(name);
if (details != null) ((TextView) view.findViewById(R.id.type)).setText(type);
((TextView) view.findViewById(R.id.details)).setText(details);
return view; return view;
} }
public View makeView(UIComponent spawner, View.OnClickListener onClick) { public View makeView(UIComponent spawner, View.OnClickListener onClick) {
@@ -12,4 +12,5 @@ public interface UIItem {
} }
default View.OnLongClickListener longClick(Runnable action) { return v -> { action.run(); return true; };} default View.OnLongClickListener longClick(Runnable action) { return v -> { action.run(); return true; };}
} }
@@ -1,24 +0,0 @@
package eu.konggdev.strikemaps.ui.element.region.content;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
public class MainContentRegion extends UIRegion {
public Fragment fragment;
public Integer layoutId;
public MainContentRegion(@NonNull Fragment initFragment, Integer refLayoutId) {
super(initFragment, refLayoutId);
this.fragment = initFragment;
this.layoutId = refLayoutId;
}
public Fragment getFragment() {
return this.fragment;
}
public void setFragment(Fragment fragment) {
this.fragment = fragment;
}
}
@@ -1,257 +1,20 @@
package eu.konggdev.strikemaps.ui.factory; package eu.konggdev.strikemaps.ui.factory;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.inputmethod.EditorInfo; import android.widget.LinearLayout;
import android.view.inputmethod.InputMethodManager; import android.widget.ScrollView;
import android.widget.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.util.JsonPatcher;
import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
import eu.konggdev.strikemaps.ui.element.item.PreviewItem; import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
import org.maplibre.geojson.Feature; import org.maplibre.geojson.Feature;
import java.io.IOException;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.function.Consumer; import java.util.function.Consumer;
import static androidx.core.content.ContextCompat.getSystemService;
//FIXME: Move Item functions into specific classes for specific types - e.g. StyleItem //FIXME: Move Item functions into specific classes for specific types - e.g. StyleItem
public final class AlertDialogFactory { public final class AlertDialogFactory {
public static AlertDialog copyBuiltInStyle(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
//TODO: Use an UI element thats supposed to be vertical, instead of GenericItem
List<String> styles = Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app));
LinearLayout container = new LinearLayout(app.getActivity());
container.setOrientation(LinearLayout.VERTICAL);
for (String style : styles) {
View itemView = GenericItem
.fromStyle(
MapStyle.fromFile(style, app),
map,
() -> ui.alert(AlertDialogFactory.createStyle(app, FileHelper.loadStringFromAssetFile(style, app), mapChangePopup))
)
.makeView(ui);
container.addView(itemView);
}
ScrollView scrollView = new ScrollView(app.getActivity());
scrollView.addView(container);
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
.setTitle("Copy from")
.setView(scrollView)
.setNegativeButton("Cancel", null)
.create();
return dialog;
}
public static AlertDialog createStyle(AppController app, String baseStyleContents, FragmentMapChangePopup mapChangePopup) {
final EditText nameInput = new EditText(app.getActivity());
nameInput.setHint("Name");
final CheckBox inferFileName = new CheckBox(app.getActivity());
inferFileName.setText("Infer filename automatically");
inferFileName.setChecked(true);
final EditText fileInput = new EditText(app.getActivity());
fileInput.setHint("Filename");
final TextView inferedFileName = new TextView(app.getActivity());
inferedFileName.setPadding(0, 2, 0, 0);
LinearLayout container = new LinearLayout(app.getActivity());
container.setOrientation(LinearLayout.VERTICAL);
int padding = (int) (20 * app.getActivity().getResources().getDisplayMetrics().density);
container.setPadding(padding, padding, padding, 0);
container.addView(nameInput);
container.addView(inferFileName);
container.addView(fileInput);
container.addView(inferedFileName);
inferFileName.setOnCheckedChangeListener((buttonView, isChecked) -> {
fileInput.setVisibility(isChecked ? View.GONE : View.VISIBLE);
inferedFileName.setVisibility(isChecked ? View.VISIBLE : View.GONE);
});
fileInput.setVisibility(inferFileName.isChecked() ? View.GONE : View.VISIBLE);
inferedFileName.setVisibility(inferFileName.isChecked() ? View.VISIBLE : View.GONE);
nameInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String nameText = "";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
if (!s.isEmpty() && s != null) {
nameText = "File name will be: " + s.toString().toLowerCase(Locale.ROOT) + ".style.json";
}
} else if (s != null) {
nameText = "File name will be: " + s.toString().toLowerCase(Locale.ROOT) + ".style.json";
}
inferedFileName.setText(nameText);
}
@Override
public void afterTextChanged(Editable s) {}
});
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
.setTitle("Create")
.setView(container)
.setPositiveButton("Create", null)
.setNegativeButton("Cancel", (d, w) -> d.dismiss())
.create();
dialog.setOnShowListener(d -> {
Button createButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
createButton.setOnClickListener(v -> {
ObjectMapper mapper = new ObjectMapper();
boolean nameEntryRequired = true;
String styleContentName = "";
if (baseStyleContents != null) {
try {
JsonNode root = mapper.readTree(baseStyleContents);
if (!root.path("name").asText().isEmpty()) {
nameEntryRequired = false; //We can take the name from the style
styleContentName = root.path("name").asText();
}
} catch (Exception e) {
e.printStackTrace();
}
}
String name = nameInput.getText().toString().trim();
if (name.isEmpty() && nameEntryRequired) {
nameInput.setError("Name required");
return;
} else if (name.isEmpty() && !nameEntryRequired) {
name = styleContentName;
}
assert !name.isEmpty();
String fileName;
if (inferFileName.isChecked()) {
fileName = name.toLowerCase(Locale.ROOT) + ".style.json";
} else {
fileName = fileInput.getText().toString();
if (fileName.isEmpty()) {
fileInput.setError("File name required");
return;
}
if (!fileName.endsWith(".style.json")) {
fileInput.setError("File must end with .style.json");
return;
}
}
try {
JsonNode root;
if(baseStyleContents != null) {
if(!baseStyleContents.isEmpty()) {
root = mapper.readTree(baseStyleContents);
} else {
root = mapper.createObjectNode();
}
} else {
root = mapper.createObjectNode();
}
if (!root.path("name").asText().isEmpty()) {
ObjectNode node = mapper.createObjectNode();
node.put("name", name);
root = JsonPatcher.patch(root, node);
}
if (FileHelper.userFileExists("style", fileName, app)) {
app.getUi().alert(askUserOverwriteFile(app, fileName, "style", mapper.writeValueAsString(root), dialog, mapChangePopup));
} else {
FileHelper.writeUserFile("style", fileName, mapper.writeValueAsString(root), app);
}
dialog.dismiss();
} catch (Exception e) {
Toast.makeText(app.getActivity(), "Failed to create", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
mapChangePopup.reloadStyles();
});
});
return dialog;
}
public static AlertDialog askUserOverwriteFile(AppController app, String fileName, String path, String content, FragmentMapChangePopup mapChangePopup) {
return new AlertDialog.Builder(app.getActivity())
.setMessage("Style of filename " + fileName + " already exists, do you wish to overwrite it?")
.setPositiveButton("Yes", (dialog, which) -> {
try {
FileHelper.writeUserFile(path, fileName, content, app);
} catch (Exception e) {
e.printStackTrace();
}
mapChangePopup.reloadStyles();
})
.setNegativeButton("No", null)
.create();
}
public static AlertDialog askUserOverwriteFile(AppController app, String fileName, String path, String content, AlertDialog originDialog, FragmentMapChangePopup mapChangePopup) {
return new AlertDialog.Builder(app.getActivity())
.setMessage("Style of filename: " + fileName + " already exists, do you wish to overwrite it?")
.setPositiveButton("Yes", (dialog, which) -> {
try {
FileHelper.writeUserFile(path, fileName, content, app);
} catch (IOException e) {
e.printStackTrace();
}
if (originDialog != null) {
originDialog.dismiss();
}
mapChangePopup.reloadStyles();
dialog.dismiss();
})
.setNegativeButton("No", (dialog, which) -> {
if (originDialog != null) {
originDialog.dismiss();
}
dialog.dismiss();
})
.create();
}
public static AlertDialog pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) { public static AlertDialog pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) {
LinearLayout layout = new LinearLayout(app.getActivity()); LinearLayout layout = new LinearLayout(app.getActivity());
layout.setOrientation(LinearLayout.VERTICAL); layout.setOrientation(LinearLayout.VERTICAL);
@@ -267,7 +30,7 @@ public final class AlertDialogFactory {
for (Feature feature : features) { for (Feature feature : features) {
View itemView = PreviewItem.fromFeature(feature).makeView(app.getUi(), v -> { View itemView = PreviewItem.fromFeature(feature).makeView(app.getUi(), v -> {
dialog.dismiss(); dialog.dismiss();
new Handler(Looper.getMainLooper()) new android.os.Handler(android.os.Looper.getMainLooper())
.post(() -> callback.accept(feature)); .post(() -> callback.accept(feature));
}); });
layout.addView(itemView); layout.addView(itemView);
@@ -275,34 +38,4 @@ public final class AlertDialogFactory {
return dialog; return dialog;
} }
public static AlertDialog searchSettings(AppController app) {
return new AlertDialog.Builder(app.getActivity())
.setTitle("Configure Search")
.setPositiveButton("OK", null)
.create();
}
public static AlertDialog restartDialog(AppController app) {
return new AlertDialog.Builder(app.getActivity())
.setTitle("Restart required")
.setMessage("Restart the app to apply changes.")
.setCancelable(false)
.setNegativeButton("Cancel", (d, w) -> {
Toast.makeText(app.getActivity(),
"Changes will be applied on next restart",
Toast.LENGTH_SHORT).show();
d.dismiss();
})
.setPositiveButton("Restart", (d, w) -> {
Intent i = app.getActivity().getPackageManager()
.getLaunchIntentForPackage(app.getActivity().getPackageName());
if (i != null) {
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
app.getActivity().startActivity(i);
}
Runtime.getRuntime().exit(0);
})
.create();
}
} }
@@ -4,6 +4,7 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.ui.element.UIRegion;
public interface ContainerFragment { public interface ContainerFragment {
abstract public Integer getRegion(); abstract public Integer getRegion();
@@ -1,148 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.dialog;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiContext;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
import java.io.*;
import java.util.stream.Collectors;
public class NewStyleBottomSheet extends BottomSheetDialogFragment {
private final String styleBase = "{\"name\":\"None\"}";
@NonNull AppController app;
@NonNull MapComponent map;
@NonNull UIComponent ui;
@NonNull
FragmentMapChangePopup mapChangePopup;
private final ActivityResultLauncher<Intent> importLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == android.app.Activity.RESULT_OK
&& result.getData() != null) {
Uri uri = result.getData().getData();
try (InputStream in = requireContext()
.getContentResolver()
.openInputStream(uri)) {
try (Cursor cursor = requireContext()
.getContentResolver()
.query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex >= 0) {
String fileName = cursor.getString(nameIndex);
BufferedReader reader = new BufferedReader(
new InputStreamReader(in)
);
StringBuilder contentBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
contentBuilder.append(line).append(System.lineSeparator());
}
reader.close();
String content = contentBuilder.toString();
if (fileName != null && !fileName.endsWith(".style.json")) {
app.getUi().alert(
AlertDialogFactory.createStyle(
app,
content,
mapChangePopup
)
);
return;
} else {
if (!FileHelper.userFileExists("style", fileName, app)) {
FileHelper.writeUserFile("style", fileName, content, app);
} else {
app.getUi().alert(AlertDialogFactory.askUserOverwriteFile(app, fileName, "style", content, mapChangePopup));
}
mapChangePopup.reloadStyles();
return;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
private void showImportDialog() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("application/json");
intent.addCategory(Intent.CATEGORY_OPENABLE);
importLauncher.launch(intent);
}
public NewStyleBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
this.app = app;
this.map = map;
this.ui = ui;
this.mapChangePopup = mapChangePopup;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_new_style, container, false);
Button builtInBtn = view.findViewById(R.id.buttonFromBuiltInStyle);
Button fileBtn = view.findViewById(R.id.buttonFromFile);
Button emptyBtn = view.findViewById(R.id.buttonCreateEmpty);
builtInBtn.setOnClickListener(v -> {
app.getUi().alert(AlertDialogFactory.copyBuiltInStyle(app, map, ui, mapChangePopup));
});
fileBtn.setOnClickListener(v -> {
showImportDialog();
});
emptyBtn.setOnClickListener(v -> {
app.getUi().alert(AlertDialogFactory.createStyle(app, styleBase, mapChangePopup));
});
return view;
}
}
@@ -1,156 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButton;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Objects;
import static android.media.MediaExtractor.MetricsConstants.MIME_TYPE;
public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
@NonNull
AppController app;
@NonNull
MapComponent map;
@NonNull
UIComponent ui;
@NonNull
FragmentMapChangePopup mapChangePopup;
private final MapStyle style;
private final String stylePath;
private final ActivityResultLauncher<Intent> exportLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == android.app.Activity.RESULT_OK
&& result.getData() != null) {
Uri uri = result.getData().getData();
try (OutputStream out =
requireContext()
.getContentResolver()
.openOutputStream(uri)) {
out.write(getContents().getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
//Action definitions
//*//
void deleteStyle() {
boolean deleted = FileHelper.deleteFile(stylePath);
if (!deleted) {
Toast.makeText(requireContext(), "Failed deleting style", Toast.LENGTH_SHORT).show();
return;
}
mapChangePopup.reloadStyles();
dismiss();
}
private void showExportDialog(String fileName) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/json");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
exportLauncher.launch(intent);
}
//*//
String getContents() {
if (stylePath.startsWith("/storage")) return FileHelper.loadStringFromUserFile(stylePath);
else return FileHelper.loadStringFromAssetFile(stylePath, app);
}
public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, String stylePath) {
this.app = app;
this.map = map;
this.ui = ui;
this.mapChangePopup = mapChangePopup;
this.style = style;
this.stylePath = stylePath;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_style_details, container, false);
TextView styleNameView = view.findViewById(R.id.styleName);
TextView fileNameView = view.findViewById(R.id.fileName);
TextView styleTypeView = view.findViewById(R.id.styleType);
TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert);
ConstraintLayout editButtonLayout = view.findViewById(R.id.editButton);
ConstraintLayout copyButtonLayout = view.findViewById(R.id.copyButton);
ConstraintLayout exportButtonLayout = view.findViewById(R.id.exportButton);
ConstraintLayout deleteButtonLayout = view.findViewById(R.id.deleteButton);
ConstraintLayout closeButtonLayout = view.findViewById(R.id.closeButton);
styleNameView.setText(style.name);
String[] pathSplit = stylePath.split("/");
String fileName = pathSplit[pathSplit.length - 1];
fileNameView.setText(fileName);
if (Objects.equals(pathSplit[0], "bundled") && pathSplit.length > 1) {
styleTypeView.setText("Built-In Style");
fileNameView.setVisibility(View.GONE);
editButtonLayout.setVisibility(View.GONE);
builtInStyleAlert.setVisibility(View.VISIBLE);
deleteButtonLayout.setVisibility(View.GONE);
} else {
styleTypeView.setText("User Style");
}
editButtonLayout.setOnClickListener(v -> Toast.makeText(requireContext(), "Editor not implemented yet\nWait for release", Toast.LENGTH_SHORT).show());
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, getContents(), mapChangePopup)));
exportButtonLayout.setOnClickListener(v -> showExportDialog(fileName));
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
closeButtonLayout.setOnClickListener(v -> dismiss());
return view;
}
}
@@ -1,125 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.layout;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
public class FragmentLayoutSearch extends Fragment implements Layout {
AppController app;
private final Integer region;
public FragmentLayoutSearch(AppController app, Integer region) {
super(R.layout.fragment_search);
this.app = app;
this.region = region;
}
@Override
public Integer getRegion() {
return region;
}
@Override
public Fragment toFragment() {
return this;
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setupButton(view, R.id.hamburgerButton, click(() -> {
View menuView = getLayoutInflater().inflate(R.layout.dropdown_main, null);
PopupWindow popupWindow = new PopupWindow(
menuView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
true
);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setupButton(menuView, R.id.menuSearchSettings, click(() -> {
app.getUi().alert(AlertDialogFactory.searchSettings(app));
}));
setupButton(menuView, R.id.menuSettings, click(() -> {
popupWindow.dismiss();
app.getUi().swapScreen(DefinedScreen.SETTINGS);
}));
menuView.findViewById(R.id.menuSearchSettings).setOnClickListener(v -> popupWindow.dismiss());
menuView.findViewById(R.id.menuAbout).setOnClickListener(v -> popupWindow.dismiss());
View anchor = view.findViewById(R.id.searchContainer);
anchor.post(() -> {
menuView.measure(
View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED
);
int popupWidth = menuView.getMeasuredWidth();
int containerWidth = anchor.getWidth();
int xOffset = containerWidth - popupWidth;
popupWindow.showAsDropDown(anchor, xOffset, 1);
});
}));
setupButton(view, R.id.offlineMapsButton, click(() -> {
View offlineMapsPopupView = getLayoutInflater().inflate(R.layout.dropdown_offline, null);
PopupWindow popupWindow = new PopupWindow(
offlineMapsPopupView,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
true
);
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
View anchor = view.findViewById(R.id.searchContainer);
setupButton(offlineMapsPopupView, R.id.offlineMaps, click(() -> {
popupWindow.dismiss();
app.getUi().swapScreen(DefinedScreen.OFFLINE);
}));
anchor.post(() -> {
offlineMapsPopupView.measure(
View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED
);
int popupWidth = offlineMapsPopupView.getMeasuredWidth();
int containerWidth = anchor.getWidth();
int xOffset = containerWidth - popupWidth;
popupWindow.showAsDropDown(anchor, xOffset, 1);
});
}));
}
}
@@ -2,11 +2,13 @@ package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.ui.element.UIRegion;
import eu.konggdev.strikemaps.R; import eu.konggdev.strikemaps.R;
public class FragmentLayoutContentMap extends Fragment implements MainContentLayout { public class FragmentLayoutContentMap extends Fragment implements MainContentLayout {
@@ -26,7 +28,6 @@ public class FragmentLayoutContentMap extends Fragment implements MainContentLay
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
LinearLayout layout = (LinearLayout) view; LinearLayout layout = (LinearLayout) view;
if(mapView.getParent() != null) ((ViewGroup) mapView.getParent()).removeView(mapView);
layout.addView(mapView); layout.addView(mapView);
} }
} }
@@ -1,60 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
import eu.konggdev.strikemaps.ui.element.item.InlineItem;
import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class FragmentLayoutContentOfflineMaps extends Fragment implements MainContentLayout {
@NonNull AppController app;
public FragmentLayoutContentOfflineMaps(AppController app) {
super(R.layout.fragment_offline_maps);
this.app = app;
}
@Override
public Fragment toFragment() {
return this;
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
List<MapSource> sources = new ArrayList<>();
List<String> stylePaths = new ArrayList<>();
stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app)));
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
/* Parsing an entire MapStyle is absolutely unnecessary and resource wasteful
TODO: A method should be implemented to parse a List<MapSource> directly from the JSON */
for (String stylePath : stylePaths) {
MapStyle parsedStyle = MapStyle.fromFile(stylePath, app);
sources.addAll(parsedStyle.sources.values());
}
LinearLayout sourcesLayout = view.findViewById(R.id.llDownloadContainer);
for (MapSource source : sources) {
if (!Objects.equals(source.type, "raster"))
sourcesLayout.addView(new InlineItem(source.url, () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
else
sourcesLayout.addView(new InlineItem(source.tiles.toString(), () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
}
}
}
@@ -1,66 +1,10 @@
package eu.konggdev.strikemaps.ui.fragment.layout.content.main; package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.app.ActionBar;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import com.google.android.material.appbar.MaterialToolbar;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import java.util.Arrays;
public class FragmentLayoutContentSettings extends Fragment implements MainContentLayout {
@NonNull AppController app;
public FragmentLayoutContentSettings(AppController app) {
super(R.layout.fragment_settings);
this.app = app;
}
public class FragmentLayoutContentSettings implements MainContentLayout {
@Override @Override
public Fragment toFragment() { public Fragment toFragment() {
return this; return null;
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
Spinner mapRendererSelector = view.findViewById(R.id.mapRendererSelector);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
view.getContext(),
R.array.map_renderers,
android.R.layout.simple_spinner_item
);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mapRendererSelector.setAdapter(adapter);
mapRendererSelector.setSelection(UserPrefsHelper.mapRenderer(app.getPrefs()));
final boolean[] ignoreFirst = {true};
mapRendererSelector.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (ignoreFirst[0]) {
ignoreFirst[0] = false;
return;
}
UserPrefsHelper.mapRenderer(app.getPrefs(), position);
app.getUi().alert(AlertDialogFactory.restartDialog(app));
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
}
);
} }
} }
@@ -1,7 +1,5 @@
package eu.konggdev.strikemaps.ui.fragment.popup; package eu.konggdev.strikemaps.ui.fragment.popup;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@@ -16,8 +14,6 @@ import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle; import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.UIComponent; 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 eu.konggdev.strikemaps.ui.element.item.GenericItem;
import java.util.ArrayList; import java.util.ArrayList;
@@ -32,19 +28,6 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
private final Integer region; private final Integer region;
private View view;
// Action definitions
//*//
void newStyleFlow() {
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
}
void styleDetails(MapStyle style, String stylePath) {
new StyleDetailsBottomSheet(app, map, ui, this, style, stylePath).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
}
//*//
public FragmentMapChangePopup(AppController app, Integer region) { public FragmentMapChangePopup(AppController app, Integer region) {
super(R.layout.popup_map_change); super(R.layout.popup_map_change);
this.app = app; this.app = app;
@@ -63,30 +46,16 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
return this; return this;
} }
public void reloadStyles() {
List<String> stylePaths = new ArrayList<>();
stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app)));
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
stylesLayout.removeAllViews();
for (String stylePath : stylePaths) {
MapStyle parsedStyle = MapStyle.fromFile(stylePath, app);
stylesLayout.addView(GenericItem.fromStyle(parsedStyle, map,
() -> map.setStyle(parsedStyle),
() -> this.styleDetails(parsedStyle, stylePath)).makeView(ui));
}
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
stylesLayout.addView(new GenericItem("",
addNewIcon,
this::newStyleFlow).makeView(ui));
}
@Override @Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
//FIXME //FIXME
setupButton(view, R.id.closeButton, click(() -> ui.getCurrentScreen().closePopup())); setupButton(view, R.id.closeButton, click(() -> ui.getCurrentScreen().closePopup()));
setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup()); setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup());
this.view = view; List<String> stylePaths = new ArrayList<>();
reloadStyles(); stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app)));
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
for (String style : stylePaths)
stylesLayout.addView(GenericItem.fromStyle(MapStyle.fromMapLibreJsonFile(style, app), map).makeView(ui));
} }
} }
@@ -1,25 +1,31 @@
package eu.konggdev.strikemaps.ui.screen; package eu.konggdev.strikemaps.ui.screen;
import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.Toolbar;
import java.util.List;
import java.util.Map; import java.util.Map;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController; import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.ui.fragment.ContainerFragment; import eu.konggdev.strikemaps.ui.fragment.ContainerFragment;
import eu.konggdev.strikemaps.ui.fragment.FragmentEmptyPlaceholder; import eu.konggdev.strikemaps.ui.fragment.layout.content.main.MainContentLayout;
import eu.konggdev.strikemaps.ui.fragment.popup.Popup; import eu.konggdev.strikemaps.ui.fragment.popup.Popup;
import eu.konggdev.strikemaps.ui.element.region.UIRegion; import eu.konggdev.strikemaps.ui.element.UIRegion;
public class Screen { public class Screen {
@NonNull AppController app; @NonNull AppController app;
Toolbar toolbar; public Screen(AppController app, MainContentLayout mainContent, Map<Integer, UIRegion> regions, Integer layout) {
public Screen(AppController app, Map<Integer, UIRegion> regions) {
this.app = app; this.app = app;
this.layout = layout;
this.mainContent = mainContent;
this.uiRegions = regions; this.uiRegions = regions;
} }
private final Integer layout;
private MainContentLayout mainContent;
Map<Integer, UIRegion> uiRegions; Map<Integer, UIRegion> uiRegions;
public Integer popup; public Integer popup;
@@ -62,10 +68,10 @@ public class Screen {
} }
public void attachAll() { public void attachAll() {
for (UIRegion region : uiRegions.values()) setFragment(region, region.getFragment()); app.getActivity().setContentView(layout);
fragmentTransaction(R.id.mainContentView, mainContent.toFragment());
for (UIRegion region : uiRegions.values()) {
setFragment(region, region.getFragment());
} }
public void detachAll() {
for (UIRegion region : uiRegions.values()) fragmentTransaction(region.layoutId, new FragmentEmptyPlaceholder());
} }
} }
@@ -1,6 +0,0 @@
package eu.konggdev.strikemaps.ui.screen.definition;
public enum DefinedScreen {
MAIN,
SETTINGS,
OFFLINE
}
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#2A2100" />
<corners android:radius="10dp" />
<stroke
android:width="1dp"
android:color="#4D3D00" />
</shape>
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFF"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 214 B

-5
View File
@@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10s10,-4.49 10,-10S17.51,2 12,2zM11,10V6h2v4h3l-4,4l-4,-4H11zM17,17H7v-2h10V17z"/>
</vector>
@@ -1,60 +0,0 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#000000"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New style"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold" />
<Space
android:layout_width="match_parent"
android:layout_height="6dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonFromBuiltInStyle"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="From built-in style"
android:textAllCaps="false"
android:textColor="#FFFFFF"
app:backgroundTint="#1A1A1A"
app:cornerRadius="24dp" />
<Space
android:layout_width="match_parent"
android:layout_height="4dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonFromFile"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="From file"
android:textAllCaps="false"
android:textColor="#FFFFFF"
app:backgroundTint="#1A1A1A"
app:cornerRadius="24dp" />
<Space
android:layout_width="match_parent"
android:layout_height="4dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonCreateEmpty"
android:layout_width="match_parent"
android:layout_height="56dp"
android:text="Create empty"
android:textAllCaps="false"
android:textColor="#FFFFFF"
app:backgroundTint="#1A1A1A"
app:cornerRadius="24dp" />
</LinearLayout>
@@ -1,250 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="#000000"
android:padding="12dp">
<TextView
android:id="@+id/styleName"
android:layout_width="164dp"
android:layout_height="34dp"
android:gravity="center"
android:text="Style Name"
android:textColor="#FFFFFF"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/fileName"
android:layout_width="wrap_content"
android:layout_height="27dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:text="\?.style.json"
android:textColor="#BDBDBD"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/styleName" />
<TextView
android:id="@+id/styleType"
android:layout_width="112dp"
android:layout_height="27dp"
android:layout_marginTop="4dp"
android:text="Style Type?"
android:textColor="#FDFDFD"
android:textSize="18sp"
android:gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fileName" />
<TextView
android:id="@+id/builtInStyleAlert"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:paddingHorizontal="14dp"
android:paddingVertical="12dp"
android:background="@drawable/bg_alert_warning"
android:text="Built-in styles can't be edited directly\nMake a copy to customize it"
android:textColor="#FFD54F"
android:textSize="14sp"
android:lineSpacingExtra="2dp"
android:gravity="center"
android:drawablePadding="8dp"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/styleType"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/editButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="2dp"
android:background="#1B1A1A"
app:layout_constraintTop_toBottomOf="@id/builtInStyleAlert"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:id="@+id/hideIcon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/hideBtnText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Edit style"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/copyButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="2dp"
android:background="#1B1A1A"
app:layout_constraintTop_toBottomOf="@id/editButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:id="@+id/copyIcon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="16dp"
android:src="@drawable/ic_copy"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/copyBtnText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Copy style"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/exportButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="2dp"
android:background="#1B1A1A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/copyButton">
<ImageView
android:id="@+id/exportIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/exportBtnText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Export style"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/deleteButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="5dp"
android:background="#1B1A1A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/exportButton"
app:layout_constraintVertical_bias="0.081">
<ImageView
android:id="@+id/deleteIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/deleteText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Delete style"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/closeButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="10dp"
android:background="#1B1A1A"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/deleteButton"
tools:layout_editor_absoluteY="251dp">
<ImageView
android:id="@+id/closeIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_more"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/closeText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Close"
android:textColor="#FFFFFF"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
-52
View File
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="200dp"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="12dp"
app:cardBackgroundColor="#000000">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/menuSearchSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="Configure Search"
android:textColor="#FFFFFF"
android:background="?android:attr/selectableItemBackground"/>
<TextView
android:id="@+id/menuSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="Settings"
android:textColor="#FFFFFF"
android:background="?android:attr/selectableItemBackground"/>
<TextView
android:id="@+id/menuAbout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="About"
android:textColor="#FFFFFF"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="200dp"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="12dp"
app:cardBackgroundColor="#000000">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/offlineMaps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="Offline Maps"
android:textColor="#FFFFFF"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
+1
View File
@@ -2,4 +2,5 @@
<android.widget.LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.widget.LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
</android.widget.LinearLayout> </android.widget.LinearLayout>
@@ -1,41 +0,0 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
<TextView
android:id="@+id/tvDownloadLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Download for source:"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:includeFontPadding="true"
android:paddingBottom="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollDownloadContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/tvDownloadLabel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="8dp"
android:fillViewport="true">
<LinearLayout
android:id="@+id/llDownloadContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp">
<LinearLayout
android:id="@+id/searchContainer"
android:layout_width="320dp"
android:layout_height="35dp"
android:layout_marginTop="2dp"
android:orientation="horizontal"
android:background="#000000"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<SearchView
android:id="@+id/searchView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:iconifiedByDefault="false"
android:queryHint="Search..."
android:background="@android:color/transparent" />
<ImageButton
android:id="@+id/offlineMapsButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:contentDescription="Offline Maps"
android:src="@drawable/ic_offline"
android:padding="6dp"
android:scaleType="fitCenter"
app:tint="#B0B0B0" />
<ImageButton
android:id="@+id/hamburgerButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:contentDescription="Menu"
android:src="@drawable/ic_hamburger_menu" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp"
android:background="@android:color/background_dark">
<TextView
android:id="@+id/mapRendererLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Map Renderer:"
android:textColor="@android:color/white"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/mapRendererSelector"/>
<Spinner
android:id="@+id/mapRendererSelector"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
app:layout_constraintStart_toEndOf="@id/mapRendererLabel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
-28
View File
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#000000"
android:foregroundTint="#FFFFFF">
<TextView
android:id="@+id/inline"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:paddingVertical="12dp"
android:text=""
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableEnd="@drawable/ic_arrow_forward"
android:drawablePadding="8dp"
android:gravity="center_vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:clickable="true"
android:focusable="true"/>
</androidx.constraintlayout.widget.ConstraintLayout>
+8 -7
View File
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="#000000" android:background="#0E0000"
android:foregroundTint="#FFFFFF"> android:foregroundTint="#FFFFFF">
<TextView <TextView
@@ -15,7 +16,7 @@
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:text="Choice Item" android:text="Choice Item"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="20sp" android:textSize="24sp"
android:ellipsize="end" android:ellipsize="end"
android:maxLines="1" android:maxLines="1"
android:scrollHorizontally="false" android:scrollHorizontally="false"
@@ -23,12 +24,12 @@
android:autoSizeMinTextSize="12sp" android:autoSizeMinTextSize="12sp"
android:autoSizeMaxTextSize="24sp" android:autoSizeMaxTextSize="24sp"
android:autoSizeStepGranularity="1sp" android:autoSizeStepGranularity="1sp"
app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/poiIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/poiIcon"
android:layout_width="55dp" android:layout_width="55dp"
android:layout_height="55dp" android:layout_height="55dp"
android:layout_marginStart="7dp" android:layout_marginStart="7dp"
@@ -38,20 +39,20 @@
app:srcCompat="@drawable/maplibre_info_bg_selector" /> app:srcCompat="@drawable/maplibre_info_bg_selector" />
<TextView <TextView
android:id="@+id/details" android:id="@+id/type"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="7dp" android:layout_marginStart="7dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:text="Type"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
android:ellipsize="end" android:ellipsize="end"
android:singleLine="true" android:singleLine="true"
app:layout_constraintStart_toEndOf="@id/icon" app:layout_constraintStart_toEndOf="@id/poiIcon"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/choiceName" app:layout_constraintTop_toBottomOf="@id/choiceName"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
+80
View File
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" >
<LinearLayout
android:id="@+id/popupHolder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
</LinearLayout>
<LinearLayout
android:id="@+id/mainContentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
</LinearLayout>
<FrameLayout
android:id="@+id/bottomUi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/devIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DEVELOPMENT BUILD!"
android:textColor="#000000"
android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/searchContainer"
android:layout_width="280dp"
android:layout_height="33dp"
android:layout_marginTop="16dp"
android:orientation="horizontal"
android:background="#000000"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<SearchView
android:id="@+id/searchView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:iconifiedByDefault="false"
android:queryHint="Search..."
android:background="@android:color/transparent" />
<ImageButton
android:id="@+id/hamburgerButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:contentDescription="Menu"
android:src="@drawable/ic_hamburger_menu" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>
-41
View File
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" >
<LinearLayout
android:id="@+id/mainContentView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:id="@+id/bottomUi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.0" />
<FrameLayout
android:id="@+id/topUi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:orientation="horizontal"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
+2 -1
View File
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item> <item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorPrimaryVariant">@color/purple_700</item>
@@ -11,5 +11,6 @@
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
+1 -7
View File
@@ -1,11 +1,5 @@
<resources> <resources>
<string name="app_name">Strike Maps</string> <string name="app_name">Strike Maps</string>
<string name="settings_title">Settings</string>
<string name="attribution_title"> Attribution </string> <string name="attribution_title"> Attribution </string>
<string name="shipped_attribution">Map Rendering libraries Included;\n - MapLibre developed by MapLibre Organization\n - Vtm developed by Mapsforge\nStrike Maps made with &lt;3 by konggdev</string> <string name="shipped_attribution">Earth map data Included;\n - © OpenStreetMap Contributors\nMap Rendering libraries Included;\n - MapLibre developed by MapLibre Organization\n - Vtm developed by Mapsforge\nStrike Maps made with &lt;3 by konggdev</string>
<string-array name="map_renderers">
<item>MapLibre GL JS</item>
<item>MapLibre Native</item>
<item>VTM</item>
</string-array>
</resources> </resources>
+3 -2
View File
@@ -1,6 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. --> <!-- Base application theme. -->
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.NoActionBar"> <style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. --> <!-- Primary brand color. -->
<item name="colorPrimary">@color/black</item> <item name="colorPrimary">@color/black</item>
<item name="colorPrimaryVariant">@color/black</item> <item name="colorPrimaryVariant">@color/black</item>
@@ -11,5 +11,6 @@
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item> <item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style> </style>
</resources> </resources>
+3
View File
@@ -10,6 +10,7 @@ constraintlayout = "2.1.4"
playServicesMaps = "19.0.0" playServicesMaps = "19.0.0"
viewpager2 = "1.1.0" viewpager2 = "1.1.0"
kotlin = "2.3.10" kotlin = "2.3.10"
coreKtx = "1.18.0"
[libraries] [libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
@@ -19,7 +20,9 @@ appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "a
material = { group = "com.google.android.material", name = "material", version.ref = "material" } material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" } constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
play-services-maps = { group = "com.google.android.gms", name = "play-services-maps", version.ref = "playServicesMaps" }
viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" } viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }