Compare commits

..
8 Commits
Author SHA1 Message Date
konggd ce4ce2f2ac Abstract style model further from MapLibre 2026-04-16 17:46:32 +02:00
konggd cb892d1bbd Update classic style 2026-04-16 17:46:32 +02:00
konggd b2a07b80d0 Fix raster sources
Fix typo
2026-04-16 17:46:32 +02:00
konggd ea67fbd352 Update README.md 2026-04-16 17:46:32 +02:00
konggd 38fb1500b0 Small fixes 2026-04-16 17:46:30 +02:00
konggd 881dedf050 Create data namespace 2026-04-16 17:46:26 +02:00
konggd ce13d0c8aa Refractor AlertDialogFactory into UI namespace 2026-04-16 17:46:15 +02:00
konggd 63d41d14d1 Furhter work on MapStyle 2026-04-16 17:42:25 +02:00
88 changed files with 571 additions and 3290 deletions
+4 -7
View File
@@ -1,11 +1,8 @@
## Strike Maps
## Strike Maps Project Android Client
Strike Maps is a FOSS mapping app for Android,
focusing on customization and offline features
<br>
It is developed as part of a broader project, hoping to create a multi-platform,
user-facing, complete FOSS mapping stack
Strike Maps is a project hoping to create a multi-platform, <br>
user-facing, complete FOSS mapping stack, focusing on customization <br>
and offline features.
<br>
This Android app has been in development since around January 2026
+3 -4
View File
@@ -28,8 +28,8 @@ android {
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_15
targetCompatibility = JavaVersion.VERSION_15
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlin {
@@ -46,8 +46,6 @@ dependencies {
implementation(libs.constraintlayout)
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("com.github.mapsforge.vtm:vtm: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.fasterxml.jackson.core:jackson-databind:2.15.0")
implementation("org.apache.commons:commons-lang3:3.14.0")
implementation(libs.core.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 768 KiB

@@ -1,7 +1,7 @@
{
"version": 8,
"name": "Satellite",
"icon": "assets://satellite.bmp",
"name": "Satelite",
"icon": "assets://satelite.bmp",
"sources": {
"satelite": {
"type": "raster",
@@ -1,125 +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 subject to 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>
@@ -1,5 +1,8 @@
package eu.konggdev.strikemaps;
import static org.maplibre.android.style.layers.PropertyFactory.lineColor;
import static org.maplibre.android.style.layers.PropertyFactory.lineWidth;
import eu.konggdev.strikemaps.app.AppController;
import android.os.Bundle;
@@ -15,7 +18,6 @@ public class MainActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
app = new AppController(this);
setContentView(R.layout.view_main);
app.init();
}
@@ -26,10 +28,4 @@ public class MainActivity extends AppCompatActivity {
public void logcat(String log) {
Log.i("LogcatGeneric", log);
}
@Override
public void onBackPressed() {
if (!app.getUi().back())
super.onBackPressed();
}
}
@@ -4,57 +4,42 @@ import android.content.SharedPreferences;
import androidx.appcompat.app.AppCompatActivity;
import eu.konggdev.strikemaps.MainActivity;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
import static android.content.Context.MODE_PRIVATE;
public class AppController {
private final MainActivity appActivity;
private MapComponent map;
private UIComponent ui;
private RegistryStorageComponent registry;
public AppController(MainActivity appActivity) { this.appActivity = appActivity;}
public AppController(MainActivity refActivity) {
appActivity = refActivity;
}
public void logcat(String log) {
appActivity.logcat(log);
}
public UIComponent getUi() {
if (ui == null) init();
return ui;
}
public MapComponent getMap() {
if (map == null) init();
return map;
}
public RegistryStorageComponent getRegistry() {
if (registry == null) init();
return registry;
}
public SharedPreferences getPrefs() {
return getActivity().getSharedPreferences("user_prefs", MODE_PRIVATE);
}
public AppCompatActivity getActivity() { return appActivity; }
public void init() {
if (getActivity().getSupportActionBar() != null)
getActivity().getSupportActionBar().show();
if(registry == null) registry = new RegistryStorageComponent(this);
getActivity().getSupportActionBar().hide();
if(map == null) map = new MapComponent(this);
if(ui == null) {
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();
}
}
@@ -1,29 +1,17 @@
package eu.konggdev.strikemaps.helper;
package eu.konggdev.strikemaps.data.helper;
import android.content.res.AssetManager;
import android.os.Environment;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import eu.konggdev.strikemaps.app.AppController;
//FIXME: Ugly
public final class FileHelper {
public static Bitmap getIcon(String iconLocator, AppController app) {
switch (iconLocator.split("//")[0]) {
//TODO: https
case "assets:":
return BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], app));
default:
app.logcat("Unimplemented icon locator space: " + iconLocator);
return null;
}
}
public static String loadStringFromAssetFile(String filePath, AppController app) {
try (InputStream is = app.getActivity().getAssets().open(filePath)) {
int size = is.available();
@@ -36,26 +24,17 @@ 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);
public static String loadStringFromUserFile(String filePath) {
File file = new File(filePath);
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;
try (FileInputStream fis = new FileInputStream(file)) {
int size = fis.available();
byte[] buffer = new byte[size];
fis.read(buffer);
return new String(buffer, StandardCharsets.UTF_8);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
@@ -98,4 +77,33 @@ public final class FileHelper {
}
}
public static String[] getUserFiles(String path, String fileExt, AppController app) {
String packageName = app.getActivity().getPackageName();
File userDirectory = new File(Environment.getExternalStorageDirectory(), "Android/data/" + packageName + "/" + path);
if (!userDirectory.exists() || !userDirectory.isDirectory())
return new String[0];
File[] files = userDirectory.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
if (fileExt == null || fileExt.isEmpty()) {
return true;
}
return filename.toLowerCase().endsWith(fileExt.toLowerCase());
}
});
if (files == null || files.length == 0) {
return new String[0];
}
List<String> fileList = new ArrayList<>();
for (File file : files) {
fileList.add(file.getAbsolutePath());
}
return fileList.toArray(new String[0]);
}
}
@@ -0,0 +1,40 @@
package eu.konggdev.strikemaps.data.helper;
import android.content.SharedPreferences;
public final class UserPrefsHelper {
private UserPrefsHelper() {} // prevent instantiation
//Keys
private static final String KEY_STARTUP_MAP_STYLE = "startupMapStyle";
private static final String KEY_MAP_RENDERER = "mapRenderer";
private static final String KEY_PERSIST_LOCATION_ENABLED = "persistLocationEnabled";
private static final String KEY_LAST_LOCATION_ENABLED = "lastLocationEnabled";
//Defaults
private static final String DEFAULT_MAP_STYLE = "bundled/style/classic.style.json";
private static final String DEFAULT_MAP_RENDERER = "mapLibre";
private static final boolean DEFAULT_PERSIST_LOCATION_ENABLED = true;
private static final boolean DEFAULT_LAST_LOCATION_ENABLED = false;
public static String startupMapStyle(SharedPreferences prefs) {
return prefs.getString(KEY_STARTUP_MAP_STYLE, DEFAULT_MAP_STYLE);
}
public static String mapRenderer(SharedPreferences prefs) {
return prefs.getString(KEY_MAP_RENDERER, DEFAULT_MAP_RENDERER);
}
public static boolean persistLocationEnabled(SharedPreferences prefs) {
return prefs.getBoolean(KEY_PERSIST_LOCATION_ENABLED, DEFAULT_PERSIST_LOCATION_ENABLED);
}
public static boolean lastLocationEnabled(SharedPreferences prefs) {
return prefs.getBoolean(KEY_LAST_LOCATION_ENABLED, DEFAULT_LAST_LOCATION_ENABLED);
}
public static boolean lastLocationEnabled(SharedPreferences prefs, boolean status) {
return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit();
}
}
@@ -1,127 +0,0 @@
package eu.konggdev.strikemaps.helper;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.source.MapSource.MapSourceContractType;
import eu.konggdev.strikemaps.map.source.model.TileSource;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public final class UserPrefsHelper {
private UserPrefsHelper() {} // prevent instantiation
//Keys
private static final String KEY_STARTUP_MAP_STYLE = "startupMapStyle";
private static final String KEY_MAP_RENDERER = "mapRenderer";
private static final String KEY_PERSIST_LOCATION_ENABLED = "persistLocationEnabled";
private static final String KEY_LAST_LOCATION_ENABLED = "lastLocationEnabled";
private static final String KEY_STYLES = "styles";
private static final String KEY_SOURCES = "sources";
//Defaults
private static final Integer DEFAULT_MAP_STYLE = 0;
private static final Integer DEFAULT_MAP_RENDERER = 0;
private static final boolean DEFAULT_PERSIST_LOCATION_ENABLED = true;
private static final boolean DEFAULT_LAST_LOCATION_ENABLED = false;
public static Map<Integer, MapStyle> DEFAULT_STYLES(AppController app) {
Map<Integer, MapStyle> styles = new HashMap<>();
String[] styleAssets = FileHelper.getAssetFiles("bundled/style", ".style.json", app);
for (int i = 0; i < styleAssets.length; i++) { styles.put( i,
new MapStyle(
FileHelper.loadStringFromAssetFile(styleAssets[i], app),
new StyleOptions(),
new StyleManagementMetadata()
));
}
return styles;
} //Built-in Styles
private static final Map<Integer, MapSource> DEFAULT_SOURCES = Map.of(
0, new MapSource(
MapSourceContractType.DEFINITION,
"Strike Maps Planet",
new TileSource("https://tiles.strikemaps.eu/planet"),
"vector",
"smts"
),
1, new MapSource(
MapSourceContractType.DEFINITION,
"ArcGIS Imagery",
new TileSource(new String[]{"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"}),
"raster",
"raster"
)
); //Built-in Sources
public static Integer startupMapStyle(SharedPreferences prefs) {
return prefs.getInt(KEY_STARTUP_MAP_STYLE, DEFAULT_MAP_STYLE);
}
public static boolean startupMapStyle(SharedPreferences prefs, Integer updated) {
return prefs.edit().putInt(KEY_STARTUP_MAP_STYLE, updated).commit();
}
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) {
return prefs.getBoolean(KEY_PERSIST_LOCATION_ENABLED, DEFAULT_PERSIST_LOCATION_ENABLED);
}
public static boolean lastLocationEnabled(SharedPreferences prefs) {
return prefs.getBoolean(KEY_LAST_LOCATION_ENABLED, DEFAULT_LAST_LOCATION_ENABLED);
}
public static boolean lastLocationEnabled(SharedPreferences prefs, boolean status) {
return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit();
}
public static Map<Integer, MapStyle> styles(SharedPreferences prefs, AppController app) {
String json = prefs.getString(KEY_STYLES, null);
if (json == null) return DEFAULT_STYLES(app);
Type type = new TypeToken<Map<Integer, MapStyle.StoredRepresentation>>() {}.getType();
Map<Integer, MapStyle.StoredRepresentation> stored =
new Gson().fromJson(json, type);
Map<Integer, MapStyle> result = new HashMap<>();
for (var entry : stored.entrySet())
result.put(entry.getKey(), entry.getValue().restore());
return result;
}
public static boolean styles(SharedPreferences prefs, Map<Integer, MapStyle> updated) {
Map<Integer, MapStyle.StoredRepresentation> stored = new HashMap<>();
for (var entry : updated.entrySet())
stored.put(entry.getKey(), entry.getValue().makeStoredRepresentation());
return prefs.edit()
.putString(KEY_STYLES, new Gson().toJson(stored))
.commit();
}
public static Map<Integer, MapSource> sources(SharedPreferences prefs) {
String json = prefs.getString(KEY_SOURCES, null);
if (json == null) return DEFAULT_SOURCES;
Type type = new TypeToken<Map<Integer, MapSource>>() {}.getType();
return new Gson().fromJson(json, type);
}
public static boolean sources(SharedPreferences prefs, Map<Integer, MapSource> updated) {
return prefs.edit()
.putString(KEY_SOURCES, new Gson().toJson(updated))
.commit();
}
}
@@ -1,14 +1,11 @@
package eu.konggdev.strikemaps.map;
import java.util.*;
import android.widget.Toast;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.geojson.Feature;
@@ -19,27 +16,20 @@ import eu.konggdev.strikemaps.map.renderer.MapRenderer;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
public class MapComponent implements Component {
private final MapRenderer mapRenderer;
private final AppController app;
MapRenderer mapRenderer;
AppController app;
public MapStyle style;
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
public MapComponent(AppController ref) {
this.app = ref;
switch(UserPrefsHelper.mapRenderer(app.getPrefs())) {
case 0:
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
break;
case 1:
this.mapRenderer = new MapLibreNativeRenderer(app, this);
break;
case 2:
case "vtm":
this.mapRenderer = new VtmRenderer(app, this);
break;
case "mapLibre":
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 MapLibreGLJSRenderer(app, this);
this.mapRenderer = new MapLibreNativeRenderer(app, this);
break;
};
}
@@ -50,13 +40,13 @@ public class MapComponent implements Component {
public void setStyle(MapStyle style) {
this.style = style;
mapRenderer.styleUpdate(style.effectiveDocument());
mapRenderer.reload();
}
public void switchOverlay(MapOverlay overlay) {
if (hasOverlay(overlay)) overlays.remove(overlay.getClass());
else overlays.put(overlay.getClass(), overlay);
overlayUpdate(overlay);
update();
}
public boolean hasOverlay(MapOverlay overlay) {
@@ -67,14 +57,18 @@ public class MapComponent implements Component {
return overlays.containsKey(overlay);
}
public void overlayUpdate(MapOverlay in) {
mapRenderer.overlayUpdate(in);
}
public void selectPoint(Feature selection) {
//FIXME: Put back FragmentPointPreviewPopup (private code atm)
}
public void onOverlayUpdate() {
update();
}
public void update() {
if(mapRenderer != null && style != null) mapRenderer.reload();
}
public boolean onMapClick(LatLng point) {
List<Feature> features = mapRenderer.featuresAtPoint(point);
@@ -99,12 +93,4 @@ public class MapComponent implements Component {
//TODO: Likely Nonfeature(?) point selection
return true;
}
public void onMapInit() {
setStyle(
app.getRegistry().getStyle(
UserPrefsHelper.startupMapStyle(app.getPrefs())
)
);
}
}
@@ -0,0 +1,11 @@
package eu.konggdev.strikemaps.map.layer;
import com.fasterxml.jackson.databind.JsonNode;
public class MapLayer {
public JsonNode layer;
public MapLayer(JsonNode layer) {
this.layer = layer;
}
}
@@ -0,0 +1,16 @@
package eu.konggdev.strikemaps.map.layer;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.map.source.MapSource;
public class SourcedMapLayer {
public String key;
public MapSource source;
public JsonNode layer;
public SourcedMapLayer(String key, MapSource source, JsonNode layer) {
this.key = key;
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;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
/* More or less a data-driven layer factory */
public interface MapOverlay {
public JsonNode makePatch();
public SourcedMapLayer makeLayer();
}
@@ -1,14 +1,19 @@
package eu.konggdev.strikemaps.map.overlay.implementation;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import androidx.annotation.NonNull;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
import org.maplibre.geojson.Feature;
import org.maplibre.geojson.FeatureCollection;
import org.maplibre.geojson.Point;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -28,38 +33,34 @@ public class LocationOverlay implements MapOverlay, LocationListener {
}
@Override
public JsonNode makePatch() {
public SourcedMapLayer makeLayer() {
MapSource source = new MapSource();
source.type = "geojson";
ObjectMapper mapper = new ObjectMapper();
ObjectNode root = mapper.createObjectNode();
ObjectNode sources = mapper.createObjectNode();
ObjectNode location = mapper.createObjectNode();
try {
ObjectNode data = mapper.createObjectNode();
data.put("type", "Feature");
if(currentLocation != null) {
ObjectNode geometry = mapper.createObjectNode();
ObjectNode properties = mapper.createObjectNode();
geometry.put("type", "Point");
ArrayNode coordinates = mapper.createArrayNode();
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);
data.set("properties", mapper.createObjectNode());
}
source.data = data;
} catch (Exception e) {
e.printStackTrace();
}
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");
@@ -67,21 +68,25 @@ public class LocationOverlay implements MapOverlay, LocationListener {
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);
ObjectNode layout = mapper.createObjectNode();
layout.put("circle-pitch-alignment", "map");
return root;
layer.set("layout", layout);
ArrayNode layers = mapper.createArrayNode();
layers.add(layer);
return new SourcedMapLayer("location", source, layers);
}
@Override
public void onLocationChanged(@NonNull Location location) {
this.currentLocation = location;
map.overlayUpdate(this);
map.onOverlayUpdate();
}
}
@@ -1,15 +1,12 @@
package eu.konggdev.strikemaps.map.overlay.implementation;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.layer.MapLayer;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
public class PointSelectionOverlay implements MapOverlay {
AppController app;
MapComponent map;
@Override
public JsonNode makePatch() {
public SourcedMapLayer makeLayer() {
return null;
}
}
@@ -1,18 +1,17 @@
package eu.konggdev.strikemaps.map.renderer;
import android.view.View;
import android.view.ViewGroup;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.layer.MapLayer;
import org.maplibre.android.style.layers.Layer;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.geojson.Feature;
import java.util.List;
public interface MapRenderer {
void styleUpdate(StyleDocument style);
void overlayUpdate(MapOverlay overlay);
void reload();
View getView();
@@ -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.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.source.MapSource;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import org.maplibre.android.geometry.LatLng;
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(StyleDocument 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)
for (MapSource source : style.sources)
sources.set(source.name, source.makeJson());
//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();
}
static class Bridge { }
}
@@ -5,15 +5,14 @@ import android.view.View;
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.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.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.layer.SourcedMapLayer;
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.style.MapStyle;
import org.maplibre.android.MapLibre;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.maps.MapLibreMap;
@@ -28,11 +27,10 @@ import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
@NonNull AppController app;
@NonNull MapComponent controller;
AppController app;
MapComponent controller;
MapLibreMap map;
final MapView mapView;
private JsonNode origin;
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
this.app = app;
@@ -44,72 +42,37 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
}
@Override
public void styleUpdate(StyleDocument style) {
public void reload() {
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) {
MapStyle style = controller.style;
try {
/* Take metadata from MapStyle
everything outside sources, layers */
ObjectNode root = style.metadata.deepCopy();
//Sources
ObjectNode sources = mapper.createObjectNode();
if (style.sources != null)
for (MapSource source : style.sources)
sources.set(source.name, source.makeJson());
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);
//Overlays
for (MapOverlay overlay : controller.overlays.values()) {
SourcedMapLayer overlayLayer = overlay.makeLayer();
sources.set(overlayLayer.key, mapper.valueToTree(overlayLayer.source));
layers.addAll((ArrayNode) overlayLayer.layer);
}
//Set all to root
root.set("sources", sources);
root.set("layers", layers);
this.origin = root;
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(root)));
} 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());
app.logcat("Failed to reload Map");
e.printStackTrace();
}
}
@@ -128,7 +91,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
public void onMapReady(@NonNull MapLibreMap maplibreMap) {
this.map = maplibreMap;
controller.onMapInit();
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 :)
map.getUiSettings().setLogoEnabled(false);
@@ -3,9 +3,7 @@ package eu.konggdev.strikemaps.map.renderer.implementation;
import android.view.View;
import eu.konggdev.strikemaps.app.AppController;
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.document.StyleDocument;
import okhttp3.OkHttpClient;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.geojson.Feature;
@@ -18,7 +16,6 @@ import java.util.Collections;
import java.util.List;
public class VtmRenderer implements MapRenderer {
AppController app;
MapComponent controller;
@@ -33,12 +30,7 @@ public class VtmRenderer implements MapRenderer {
}
@Override
public void overlayUpdate(MapOverlay overlay) {
//TODO
}
@Override
public void styleUpdate(StyleDocument style) {
public void reload() {
//TODO
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
@@ -1,139 +1,21 @@
package eu.konggdev.strikemaps.map.source;
import androidx.annotation.NonNull;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.konggdev.strikemaps.map.source.model.TileSource;
public class MapSource {
public enum MapSourceContractType {
REQUEST,
DEFINITION
}
/*
* Contract describes the "purpose" of the source.
*
* This exists because we effectively have two types
* of sources:
* - Sources that are the style asking for a specific type of source
* and defining a fallback, which is also used as a default for
*
* and
* - Sources that are the actual source
*/
@NonNull
public final MapSourceContractType contract;
/*
* For a request contract:
* name is the internal key used by the style.
*
* For a definition contract:
* name is the user-facing source name
* (e.g. "ArcGIS Imagery").
*
* When converting a request into a definition
* (when importing a style whose requests
* cannot be satisfied), we look for a name
* field and fall back to normalizing the key instead
*/
public String name;
/*
* For a request contract, this is the fallback
* for when we absolutely cannot satisfy the requirement,
* or the defaults for converting into a definition contract.
*/
public TileSource tileSource;
public String url;
public JsonNode data;
public String type;
public String schema;
public JsonNode tiles;
public int minzoom;
public int maxzoom;
public String scheme;
public int tileSize;
public String attribution;
public String encoding;
public MapSource(@NonNull MapSourceContractType contract, String name, TileSource tileSource, String type, String schema) {
this.contract = contract;
this.name = name;
this.tileSource = tileSource;
this.type = type;
this.schema = schema;
}
private MapSource(@NonNull MapSourceContractType contract) {
this.contract = contract;
}
public static MapSource fromJson(MapSourceContractType contract, String key, JsonNode sourceNode) {
MapSource result = new MapSource(contract);
result.name = key;
result.schema = sourceNode.path("schema").asText(null);
result.scheme = sourceNode.path("scheme").asText(null);
result.encoding = sourceNode.path("encoding").asText(null);
result.type = sourceNode.path("type").asText(null);
result.minzoom = sourceNode.path("minZoom").asInt(0);
result.maxzoom = sourceNode.path("maxZoom").asInt(24);
result.tileSize = sourceNode.path("tileSize").asInt(256);
result.tileSource = handleJsonTileSource(contract, sourceNode);
return result;
}
private static TileSource handleJsonTileSource(MapSourceContractType contract, JsonNode sourceNode) {
// By design, a source must use either "url" or "tiles", never both
// In case both are present, we prefer URL over tiles... because I don't know, we just do, m'kay?
if (sourceNode.has("url"))
return new TileSource(sourceNode.get("url").asText());
if (sourceNode.has("tiles")) {
String[] tiles = new String[0];
ObjectMapper mapper = new ObjectMapper();
try {
tiles = mapper.treeToValue(sourceNode.get("tiles"), String[].class);
} catch (Exception e) { // If we can't parse it, lets just keep it an empty array
e.printStackTrace();
}
return new TileSource(tiles);
}
if (contract == MapSourceContractType.REQUEST) //No tile source is only acceptable for a request contract
//TODO: Define an empty tile source?
return null;
//TODO: Decide what to do when we have a definition that doesn't define the most important part - the tile source
//Maybe throwing some custom exception, catching it in fromJson calls and propagating it back to the user would be appropriate
return null;
}
public ObjectNode makeJson() {
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
try {
if (type != null) node.put("type", type);
if (schema != null) node.put("schema", schema);
if (minzoom != 0) node.put("minzoom", minzoom);
if (maxzoom != 24) node.put("maxzoom", maxzoom);
if (scheme != null) node.put("scheme", scheme);
if (tileSize != 256) node.put("tileSize", tileSize);
if (encoding != null) node.put("encoding", encoding);
if (tileSource != null) tileSource.makeJson(mapper, node);
} catch (Exception e) {
e.printStackTrace();
}
return node;
}
public MapSource() { }
}
@@ -1,51 +0,0 @@
package eu.konggdev.strikemaps.map.source.model;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class TileSource {
public enum TileSourceType {
URL,
TILES,
DATA
}
private TileSourceType type;
private String url;
private String[] tiles;
private JsonNode data;
public TileSource(String url) {
this.url = url;
this.type = TileSourceType.URL;
}
public TileSource(String[] tiles) {
this.tiles = tiles;
this.type = TileSourceType.TILES;
}
public TileSource(JsonNode data) {
this.data = data;
this.type = TileSourceType.DATA;
}
public void makeJson(ObjectMapper mapper, ObjectNode node) {
switch (type) {
case URL -> node.put("url", url);
case TILES -> {
ArrayNode tilesNode = mapper.createArrayNode();
for (String tile : tiles)
tilesNode.add(tile);
node.set("tiles", tilesNode);
}
case DATA -> node.set("data", data);
}
}
}
@@ -1,64 +1,68 @@
package eu.konggdev.strikemaps.map.style;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.data.helper.FileHelper;
import eu.konggdev.strikemaps.map.source.MapSource;
import java.util.*;
public class MapStyle {
public static final class StoredRepresentation {
public final String json;
public final StyleOptions options;
//Only local data
public String name;
public Bitmap icon;
public final StyleManagementMetadata managementMetadata;
public JsonNode metadata; // everything except layers + sources
public Map<String, MapSource> sources;
public ArrayNode layerDefinitions; // the "layers" array
public StoredRepresentation(String json, StyleOptions options, StyleManagementMetadata managementMetadata) {
this.json = json;
this.options = options;
this.managementMetadata = managementMetadata;
}
//FIXME
public static MapStyle fromMapLibreJsonFile(String filename, AppController app) {
String styleContents;
if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename);
else styleContents = FileHelper.loadStringFromAssetFile(filename, app);
public MapStyle restore() {
MapStyle style = new MapStyle(
json,
options,
managementMetadata
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode root = mapper.readTree(styleContents);
MapStyle style = new MapStyle();
style.name = root.path("name").asText();
style.icon = getIcon(root.path("icon").asText(), app);
style.sources = mapper.convertValue(
root.path("sources"),
new TypeReference<Map<String, MapSource>>() {}
);
style.layerDefinitions = root.withArray("layers");
ObjectNode metadata = root.deepCopy();
metadata.remove("layers");
metadata.remove("sources");
style.metadata = metadata;
return style;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@NonNull public final StyleDocument document;
@NonNull public final StyleOptions options;
// Null when the style is not managed
@Nullable public StyleManagementMetadata managementMetadata;
// Original json representation of the style document, as we got it
@NonNull public final String json;
public MapStyle(@NonNull String json, @NonNull StyleOptions styleOptions, @Nullable StyleManagementMetadata managementMetadata) {
this.json = json;
this.document = new StyleDocument(json);
this.options = styleOptions;
this.managementMetadata = managementMetadata;
public static Bitmap getIcon(String iconLocator, AppController app) {
switch(iconLocator.split("//")[0]) {
//TODO: https
case "assets:":
return BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], app));
default:
app.logcat("Unimplemented icon locator space: " + iconLocator);
return null;
}
public MapStyle(@NonNull String json, @NonNull StyleDocument style, @NonNull StyleOptions styleOptions, @Nullable StyleManagementMetadata managementMetadata) {
this.json = json;
this.document = style;
this.options = styleOptions;
this.managementMetadata = managementMetadata;
}
public StoredRepresentation makeStoredRepresentation() {
return new StoredRepresentation(json, options, managementMetadata);
}
public StyleDocument effectiveDocument() {
return document.effectiveDocument(options);
}
}
@@ -1,88 +0,0 @@
package eu.konggdev.strikemaps.map.style.document;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.FileHelper;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import java.util.ArrayList;
import java.util.List;
public class StyleDocument {
//Only local data
public String name;
public String icon;
public JsonNode metadata; // everything except layers + sources
public List<MapSource> sources;
public ArrayNode layerDefinitions; // "layers" array
// Json constructor
public StyleDocument(String json) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode root = mapper.readTree(json);
this.name = root.path("name").asText();
this.icon = root.path("icon").asText();
JsonNode jsonSources = root.path("sources");
List<MapSource> sources = new ArrayList<>();
jsonSources.fields().forEachRemaining(entry -> {
sources.add(MapSource.fromJson(MapSource.MapSourceContractType.REQUEST, entry.getKey(), entry.getValue()));
});
this.sources = sources;
this.layerDefinitions = root.withArray("layers");
ObjectNode metadata = root.deepCopy();
metadata.remove("layers");
metadata.remove("sources");
this.metadata = metadata;
} catch (Exception e) {
throw new IllegalArgumentException("Invalid style document", e);
}
}
// Copy constructor
public StyleDocument(StyleDocument other) {
this.name = other.name;
this.icon = other.icon;
this.metadata = other.metadata.deepCopy();
this.sources = new ArrayList<>(other.sources);
this.layerDefinitions = other.layerDefinitions.deepCopy();
}
// The style that is presented to the renderer, with its options applied
public StyleDocument effectiveDocument(StyleOptions options) {
StyleDocument result = new StyleDocument(this); //Copy
for (JsonNode layer : result.layerDefinitions) {
JsonNode option = layer.get("option");
if (option == null)
continue;
if ("enable".equals(option.path("type").asText())) {
String id = layer.path("id").asText();
boolean enabled = options.getBoolean(
id,
option.path("default").asBoolean(true)
);
}
}
return result;
}
}
@@ -1,20 +0,0 @@
package eu.konggdev.strikemaps.map.style.management;
public class StyleManagementMetadata {
public boolean modified;
public boolean doUpdates;
public boolean autoUpdate;
public String source;
public String sourceHash;
public StyleManagementMetadata() { }
public StyleManagementMetadata(boolean modified, boolean doUpdates, boolean autoUpdate, String source, String sourceHash) {
this.modified = modified;
this.doUpdates = doUpdates;
this.autoUpdate = autoUpdate;
this.source = source;
this.sourceHash = sourceHash;
}
}
@@ -1,30 +0,0 @@
package eu.konggdev.strikemaps.map.style.options;
import eu.konggdev.strikemaps.map.source.MapSource;
import java.util.HashMap;
import java.util.Map;
public class StyleOptions {
private Map<String, Object> values;
public StyleOptions() {
this.values = new HashMap<>();
}
public StyleOptions(Map<String, Object> values) {
this.values = values;
}
public boolean getBoolean(String id, boolean defaultValue) {
Object value = values.get(id);
return value instanceof Boolean ? (Boolean) value : defaultValue;
}
public int getInteger(String id, int defaultValue) {
Object value = values.get(id);
return value instanceof Number
? ((Number) value).intValue()
: defaultValue;
}
}
@@ -1,74 +0,0 @@
package eu.konggdev.strikemaps.storage;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import java.util.Map;
public class RegistryStorageComponent implements Component {
private AppController app;
private Map<Integer, MapStyle> styles;
private Map<Integer, MapSource> sources;
public RegistryStorageComponent(AppController app) {
this.app = app;
styles();
}
private Map<Integer, MapSource> sources() {
if (sources == null) sources = UserPrefsHelper.sources(app.getPrefs());
return sources;
}
public Map<Integer, MapSource> getSources() {
return sources();
}
private Map<Integer, MapStyle> styles() {
if (styles == null) styles = UserPrefsHelper.styles(app.getPrefs(), app);
return styles;
}
public Map<Integer, MapStyle> getStyles() {
return styles();
}
public MapStyle getStyle(Integer id) {
return styles().get(id);
}
public int addStyle(MapStyle style) {
Map<Integer, MapStyle> styles = styles();
int id = styles.keySet().stream()
.mapToInt(Integer::intValue)
.max()
.orElse(-1) + 1;
styles.put(id, style);
save();
return id;
}
public void updateStyle(int id, MapStyle style) {
styles().put(id, style);
save();
}
public void deleteStyle(int id) {
styles().remove(id);
save();
}
private void save() {
UserPrefsHelper.styles(app.getPrefs(), styles);
}
public void checkForUpdates() {
//
}
}
@@ -3,83 +3,64 @@ package eu.konggdev.strikemaps.ui;
import android.app.AlertDialog;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import com.google.common.collect.BiMap;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.ui.element.region.content.MainContentRegion;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
import eu.konggdev.strikemaps.ui.element.UIRegion;
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.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.function.Consumer;
public class UIComponent implements Component {
@NonNull AppController app;
MapComponent map;
private final ArrayDeque<Screen> screenStack = new ArrayDeque<>();
private Map<Integer, Screen> screens;
private Integer currentScreen;
public UIComponent(AppController app, MapComponent map) {
this.app = app;
this.map = map;
}
public Map<DefinedScreen, Screen> getScreens(MapComponent map) {
return Map.of(
this.screens = Map.of(
//Main screen
DefinedScreen.MAIN, new Screen(
R.layout.screen_main, new Screen(
//App reference
app,
//Map view
map.toFragment(), //FragmentLayoutContentMap
//Main screen init regions definition
Map.of(
R.id.mainContentView, new MainContentRegion(map.toFragment(), R.id.mainContentView),
R.id.bottomUi, new UIRegion(new FragmentLayoutControls(app, R.id.bottomUi), R.id.bottomUi),
R.id.topUi, new UIRegion(new FragmentLayoutSearch(app, R.id.topUi), R.id.topUi)
) //TODO: Probably stop referencing layout 3(!) times everytime
Map.of(R.id.bottomUi, new UIRegion(new FragmentLayoutControls(app, R.id.bottomUi), R.id.bottomUi)), //TODO: Probably stop referencing layout 3(!) times everytime
//Layout
R.layout.screen_main //TODO: Define this for the Screen without duplicating the reference
),
//Settings screen
DefinedScreen.SETTINGS, new Screen(
R.layout.screen_settings, new Screen(
app,
//Just the settings content fragment
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentSettings(app), R.id.mainContentView)
)
),
//Offline maps screen
DefinedScreen.OFFLINE, new Screen(
app,
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentOfflineMaps(app), R.id.mainContentView)
)
//Settings
new FragmentLayoutContentSettings(),
/* No regions defined in settings
Entire screen is just the main view */
new HashMap<>(),
//Layout
R.layout.screen_settings
)
);
}
public void swapScreen(DefinedScreen screenKey) {
if (!screenStack.isEmpty()) getCurrentScreen().detachAll();
screenStack.add(getScreens(map).get(screenKey));
public void swapScreen(Integer screen) {
currentScreen = screen;
getCurrentScreen().attachAll();
}
public boolean back() {
if (screenStack.size() <= 1) return false;
getCurrentScreen().detachAll();
screenStack.removeLast();
getCurrentScreen().attachAll();
return true;
}
public Screen getCurrentScreen() {
return screenStack.getLast();
return getScreen(currentScreen);
}
public Screen getScreen(Integer screen) {
return screens.get(screen);
}
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.fragment.app.Fragment;
@@ -40,4 +40,5 @@ public class UIRegion {
currentFragment = stockFragment;
}
}
}
@@ -7,17 +7,14 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.FileHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.ui.UIComponent;
public class GenericItem implements UIItem {
@NonNull public String name;
public Bitmap image;
public Runnable onClick;
public Runnable onLongClick;
boolean hasImage;
public GenericItem(String refName) {
@@ -29,12 +26,6 @@ public class GenericItem implements UIItem {
this.onClick = onClick;
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) {
this.name = refName;
this.image = refImage;
@@ -47,35 +38,18 @@ public class GenericItem implements UIItem {
hasImage = true;
}
public GenericItem(String refName, Bitmap refImage, Runnable onClick, Runnable onLongClick) {
this.name = refName;
this.image = refImage;
this.onClick = onClick;
this.onLongClick = onLongClick;
hasImage = true;
}
public static GenericItem fromStyle(StyleDocument style, AppController app, Runnable onClick) {
public final static GenericItem fromStyle(MapStyle style, MapComponent map) {
if(style == null) return new GenericItem("Unknown");
if(style.icon != null)
return new GenericItem(style.name, FileHelper.getIcon(style.icon, app), onClick);
return new GenericItem(style.name, onClick);
return new GenericItem(style.name, style.icon, () -> map.setStyle(style));
return new GenericItem(style.name, () -> map.setStyle(style));
}
public static GenericItem fromStyle(StyleDocument style, AppController app, Runnable onClick, Runnable onLongClick) {
if(style == null) return new GenericItem("Unknown");
if(style.icon != null)
return new GenericItem(style.name, FileHelper.getIcon(style.icon, app), onClick, onLongClick);
return new GenericItem(style.name, onClick, onLongClick);
}
@Override
public View makeView(UIComponent spawner) {
View v = spawner.inflateUi(R.layout.item_generic);
//FIXME: These shouldn't be casted like that!
((TextView) v.findViewById(R.id.name)).setText(name);
if(image != null) ((ImageButton) v.findViewById(R.id.image)).setImageBitmap(image);
if(onClick != null) v.findViewById(R.id.image).setOnClickListener(click(onClick));
if(onLongClick != null) v.findViewById(R.id.image).setOnLongClickListener(longClick(onLongClick));
return v;
}
@@ -1,38 +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.map.source.MapSource;
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;
}
public static InlineItem fromSource(MapSource source) {
return new InlineItem(source.name);
}
public static InlineItem fromSource(MapSource source, Runnable onClick) {
return new InlineItem(source.name, 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,41 +4,35 @@ import android.graphics.Bitmap;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.ui.UIComponent;
import org.maplibre.geojson.Feature;
import eu.konggdev.strikemaps.R;
public class PreviewItem implements UIItem {
@NonNull public String name;
public String details;
public String name;
public String type;
public Bitmap image;
boolean hasImage;
public PreviewItem(String refName, String refType) {
this.name = refName;
this.details = refType;
this.type = refType;
hasImage = false;
}
public PreviewItem(String refName, String refType, Bitmap refImage) {
this.name = refName;
this.details = refType;
this.type = refType;
this.image = refImage;
hasImage = true;
}
public static PreviewItem fromFeature(Feature feature) {
return new PreviewItem(feature.getStringProperty("name"), feature.getStringProperty("class"));
}
@Override
public View makeView(UIComponent spawner) {
View view = spawner.inflateUi(R.layout.item_preview);
((TextView) view.findViewById(R.id.choiceName)).setText(name);
if (details != null)
((TextView) view.findViewById(R.id.details)).setText(details);
((TextView) view.findViewById(R.id.type)).setText(type);
return view;
}
public View makeView(UIComponent spawner, View.OnClickListener onClick) {
View view = makeView(spawner);
view.setOnClickListener(onClick);
@@ -1,12 +0,0 @@
package eu.konggdev.strikemaps.ui.element.item;
import android.view.View;
import eu.konggdev.strikemaps.ui.UIComponent;
public class SelectionItem implements UIItem {
@Override
public View makeView(UIComponent spawner) {
return null;
}
}
@@ -12,4 +12,5 @@ public interface UIItem {
}
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,135 +1,20 @@
package eu.konggdev.strikemaps.ui.factory;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.util.JsonPatcher;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
import org.maplibre.geojson.Feature;
import java.util.List;
import java.util.function.Consumer;
//FIXME: Cleaner architecture would be having a class for each AlertDialog type
//FIXME: Move Item functions into specific classes for specific types - e.g. StyleItem
public final class AlertDialogFactory {
public static AlertDialog copyBuiltInStyle(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
//TODO: Use an UI element that's supposed to be vertical, instead of GenericItem
LinearLayout container = new LinearLayout(app.getActivity());
container.setOrientation(LinearLayout.VERTICAL);
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");
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);
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();
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);
}
app.getRegistry().addStyle(new MapStyle(
mapper.writeValueAsString(root),
new StyleOptions(),
null
));
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 pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) {
LinearLayout layout = new LinearLayout(app.getActivity());
layout.setOrientation(LinearLayout.VERTICAL);
@@ -145,7 +30,7 @@ public final class AlertDialogFactory {
for (Feature feature : features) {
View itemView = PreviewItem.fromFeature(feature).makeView(app.getUi(), v -> {
dialog.dismiss();
new Handler(Looper.getMainLooper())
new android.os.Handler(android.os.Looper.getMainLooper())
.post(() -> callback.accept(feature));
});
layout.addView(itemView);
@@ -153,34 +38,4 @@ public final class AlertDialogFactory {
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 androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.ui.element.UIRegion;
public interface ContainerFragment {
abstract public Integer getRegion();
@@ -1,142 +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 androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.card.MaterialCardView;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
import java.io.*;
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
)
);
} else {
app.getRegistry().addStyle(
new MapStyle(
content,
new StyleOptions(),
null
)
);
mapChangePopup.reloadStyles();
}
}
}
}
} 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);
MaterialCardView builtInBtn = view.findViewById(R.id.buttonFromBuiltInStyle);
MaterialCardView fileBtn = view.findViewById(R.id.buttonFromFile);
MaterialCardView 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,125 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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 com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.card.MaterialCardView;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.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;
public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
@NonNull
AppController app;
@NonNull
MapComponent map;
@NonNull
UIComponent ui;
@NonNull
final FragmentMapChangePopup mapChangePopup;
private final MapStyle style;
private final Integer id;
private ActivityResultLauncher<Intent> exportLauncher;
void deleteStyle() {
app.getRegistry().deleteStyle(id);
mapChangePopup.reloadStyles();
dismiss();
}
private void showExportDialog() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("application/json");
exportLauncher.launch(intent);
}
public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, Integer id) {
this.app = app;
this.map = map;
this.ui = ui;
this.mapChangePopup = mapChangePopup;
this.style = style;
this.id = id;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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)) {
if (out != null) {
out.write(style.json.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
}
@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 styleTypeView = view.findViewById(R.id.styleType);
TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert);
MaterialCardView editButtonLayout = view.findViewById(R.id.editButton);
MaterialCardView copyButtonLayout = view.findViewById(R.id.copyButton);
MaterialCardView exportButtonLayout = view.findViewById(R.id.exportButton);
MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton);
MaterialCardView closeButtonLayout = view.findViewById(R.id.closeButton);
styleNameView.setText(style.document.name);
if (style.managementMetadata != null) {
styleTypeView.setText("Built-In Style");
} 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, style.json, mapChangePopup)));
exportButtonLayout.setOnClickListener(v -> showExportDialog());
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
closeButtonLayout.setOnClickListener(v -> dismiss());
return view;
}
}
@@ -15,7 +15,7 @@ import android.widget.Toast;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.overlay.implementation.LocationOverlay;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
@@ -1,86 +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);
});
}));
}
}
@@ -2,11 +2,13 @@ package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.ui.element.UIRegion;
import eu.konggdev.strikemaps.R;
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) {
super.onViewCreated(view, savedInstanceState);
LinearLayout layout = (LinearLayout) view;
if(mapView.getParent() != null) ((ViewGroup) mapView.getParent()).removeView(mapView);
layout.addView(mapView);
}
}
@@ -1,32 +0,0 @@
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
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);
// LinearLayout sourcesLayout = view.findViewById(R.id.llDownloadContainer);
// for (MapSource source : sources)
// sourcesLayout.addView(new InlineItem(source.name, () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
}
}
@@ -1,60 +1,10 @@
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
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.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
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
public Fragment toFragment() {
return this;
}
@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) {}
}
);
return null;
}
}
@@ -1,29 +1,25 @@
package eu.konggdev.strikemaps.ui.fragment.popup;
import android.annotation.SuppressLint;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import android.widget.LinearLayout;
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.fragment.dialog.NewStyleBottomSheet;
import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet;
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
import java.util.Map;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FragmentMapChangePopup extends Fragment implements Popup {
@NonNull AppController app;
@@ -32,68 +28,6 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
private final Integer region;
private View view;
Map<Integer, Integer> tabs = Map.of(
R.id.stylesTab, R.id.styles,
R.id.optionsTab, R.id.options,
R.id.overlaysTab, R.id.overlays
);
Map<Integer, Runnable> tabLoadActions = Map.of(
R.id.stylesTab, this::reloadStyles,
R.id.optionsTab, this::loadStyleOptions,
R.id.overlaysTab, this::reloadOverlays
);
public void reloadStyles() {
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
stylesLayout.removeAllViews();
app.getRegistry().getStyles().forEach((id, style) ->
stylesLayout.addView(GenericItem.fromStyle(style.document, app,
() -> map.setStyle(style),
() -> this.styleDetails(style, id)).makeView(ui))
);
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
stylesLayout.addView(new GenericItem("",
addNewIcon,
this::newStyleFlow).makeView(ui));
}
public void loadStyleOptions() {
LinearLayout optionsLayout = view.findViewById(R.id.optionsLayout);
// optionsLayout.removeAllViews();
// TextView sources = new AppCompatTextView(requireContext()) {{
// setText("Sources");
// setTextSize(16);
// setLayoutParams(new ViewGroup.MarginLayoutParams(
// ViewGroup.LayoutParams.WRAP_CONTENT,
// ViewGroup.LayoutParams.WRAP_CONTENT
// ) {{ leftMargin = 16; }});
// }};
// optionsLayout.addView(sources);
// for (MapSource source : map.style.sources) {
// LinearLayout sourceSelectionLayout = new LinearLayout(requireContext());
// TextView sourceName = new TextView(requireContext());
// sourceName.setText(source.name);
// sourceSelectionLayout.addView(sourceName);
// optionsLayout.addView(sourceSelectionLayout);
// }
}
public void reloadOverlays() {
}
void newStyleFlow() {
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
}
void styleDetails(MapStyle entry, Integer id) {
new StyleDetailsBottomSheet(app, map, ui, this, entry, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
}
public FragmentMapChangePopup(AppController app, Integer region) {
super(R.layout.popup_map_change);
this.app = app;
@@ -112,30 +46,16 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
return this;
}
@SuppressLint("UseCompatTextViewDrawableApis")
void switchTab(int target) {
tabs.forEach((button, content) -> {
view.findViewById(content).setVisibility(button == target ? View.VISIBLE : View.GONE);
int color = (button == target
? Color.WHITE
: Color.parseColor("#888888"));
((TextView) view.findViewById(button)).setTextColor(color);
((TextView) view.findViewById(button)).setCompoundDrawableTintList(ColorStateList.valueOf(color));
});
tabLoadActions.get(target).run();
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
//FIXME
this.view = view;
tabs.keySet().forEach(button -> {
view.findViewById(button).setOnClickListener(v -> switchTab(button));
});
switchTab(R.id.stylesTab); // Show styles
setupButton(view, R.id.closeButton, click(() -> ui.getCurrentScreen().closePopup()));
setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup());
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);
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;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.Toolbar;
import java.util.List;
import java.util.Map;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
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.element.region.UIRegion;
import eu.konggdev.strikemaps.ui.element.UIRegion;
public class Screen {
@NonNull AppController app;
Toolbar toolbar;
public Screen(AppController app, Map<Integer, UIRegion> regions) {
public Screen(AppController app, MainContentLayout mainContent, Map<Integer, UIRegion> regions, Integer layout) {
this.app = app;
this.layout = layout;
this.mainContent = mainContent;
this.uiRegions = regions;
}
private final Integer layout;
private MainContentLayout mainContent;
Map<Integer, UIRegion> uiRegions;
public Integer popup;
@@ -62,10 +68,10 @@ public class Screen {
}
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
}
-16
View File
@@ -1,16 +0,0 @@
The following files included inside the /res/drawable folder are derived from Google Material Icons and are subject to the Apache License, Version 2.0:
* `ic_sliders.xml`
* `ic_bubble.xml`
* `ic_stack.xml`
* `ic_clarify.xml`
*
The following files included inside the /res/drawable folder are copies of Google Material Icons and are subject to the Apache License, Version 2.0:
* `ic_file.xml`
* `ic_offline.xml`
* `ic_arrow_forward.xml`
* `ic_hide.bmp`
* `ic_dropdown.xml`
Unless otherwise noted in this file, all other files included in the `res` folder are subject to the standard license of this project
@@ -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,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="4dp"
android:insetTop="4dp"
android:insetRight="4dp"
android:insetBottom="4dp">
<ripple android:color="#33FFFFFF">
<item>
<shape android:shape="rectangle">
<solid android:color="#000000" />
<corners android:radius="3dp" />
</shape>
</item>
</ripple>
</inset>
@@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M400,680L400,280L600,480L400,680Z"/>
</vector>
-5
View File
@@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="18dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="18dp">
<path android:fillColor="@android:color/white" android:pathData="M619.5,148.5Q685,177 734,226Q783,275 811.5,340.5Q840,406 840,480Q840,554 811.5,619.5Q783,685 734,734Q685,783 619.5,811.5Q554,840 480,840Q439,840 401,831Q363,822 325,805L386,744Q409,752 432.5,756Q456,760 480,760Q596,760 678,678Q760,596 760,480Q760,364 678,282Q596,200 480,200Q364,200 282,282Q200,364 200,480Q200,504 204,527.5Q208,551 216,574L156,634Q138,598 129,559.5Q120,521 120,480Q120,406 148.5,340.5Q177,275 226,226Q275,177 340.5,148.5Q406,120 480,120Q554,120 619.5,148.5ZM520,640L520,496L176,840L120,784L464,440L320,440L320,360L600,360L600,640L520,640Z"/>
</vector>
-17
View File
@@ -1,17 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<group
android:translateY="-40">
<path
android:fillColor="@android:color/white"
android:pathData="M240,680L520,680L520,600L240,600L240,680ZM640,680L720,680L720,280L640,280L640,680ZM240,520L520,520L520,440L240,440L240,520ZM240,360L520,360L520,280L240,280L240,360ZM160,840Q127,840 103.5,816.5Q80,793 80,760L80,200Q80,167 103.5,143.5Q127,120 160,120L800,120Q833,120 856.5,143.5Q880,167 880,200L880,760Q880,793 856.5,816.5Q833,840 800,840L160,840ZM160,760L800,760Q800,760 800,760Q800,760 800,760L800,200Q800,200 800,200Q800,200 800,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760ZM160,760L160,760Q160,760 160,760Q160,760 160,760L160,200Q160,200 160,200Q160,200 160,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760Z" />
</group>
</vector>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

-10
View File
@@ -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="@android:color/white"
android:pathData="M16,1H4C2.9,1 2,1.9 2,3v14h2V3h12V1zM19,5H8C6.9,5 6,5.9 6,7v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2V7C21,5.9 20.1,5 19,5zM19,21H8V7h11V21z"/>
</vector>
@@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M480,600L280,400L680,400L480,600Z"/>
</vector>
-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="960" android:viewportWidth="960" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M720,630Q720,734 647,807Q574,880 470,880Q366,880 293,807Q220,734 220,630L220,260Q220,185 272.5,132.5Q325,80 400,80Q475,80 527.5,132.5Q580,185 580,260L580,610Q580,656 548,688Q516,720 470,720Q424,720 392,688Q360,656 360,610L360,240L440,240L440,610Q440,623 448.5,631.5Q457,640 470,640Q483,640 491.5,631.5Q500,623 500,610L500,260Q499,218 470.5,189Q442,160 400,160Q358,160 329,189Q300,218 300,260L300,630Q299,701 349,750.5Q399,800 470,800Q540,800 589,750.5Q638,701 640,630L640,240L720,240L720,630Z"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 B

After

Width:  |  Height:  |  Size: 214 B

-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M11.07,12.85c0.77,-1.39 2.25,-2.21 3.11,-3.44 0.91,-1.29 0.4,-3.7 -2.18,-3.7 -1.69,0 -2.52,1.28 -2.87,2.34L6.54,6.96C7.25,4.83 9.18,3 11.99,3c2.35,0 3.96,1.07 4.78,2.41 0.7,1.15 1.11,3.3 0.03,4.9 -1.2,1.77 -2.35,2.31 -2.97,3.45 -0.25,0.46 -0.35,0.76 -0.35,2.24h-2.89C10.58,15.22 10.46,13.95 11.07,12.85zM14,20c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2 0.9,-2 2,-2 2,0.9 2,2z"/>
</vector>
-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>
-24
View File
@@ -1,24 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="#000000">
<group
android:pivotX="480"
android:pivotY="480"
android:scaleX="1.15"
android:scaleY="1.15"
android:translateX="-80"
android:translateY="40">
<path
android:fillColor="@android:color/white"
android:pathData="M300,600L360,600L360,440L300,440L300,490L240,490L240,550L300,550L300,600Z
M400,550L720,550L720,490L400,490L400,550Z
M600,440L660,440L660,390L720,390L660,330L660,280L600,280L600,440Z
M240,390L560,390L560,330L240,330L240,390Z" />
</group>
</vector>
-17
View File
@@ -1,17 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:tint="#000000"
android:viewportWidth="960"
android:viewportHeight="960">
<group
android:translateY="-40">
<path
android:fillColor="@android:color/white"
android:pathData="M80,880L80,800L880,800L880,880L80,880ZM280,440L280,320L680,320L680,440L280,440ZM280,680L280,560L680,560L680,680L280,680Z" />
</group>
</vector>
@@ -1,173 +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:padding="12dp">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="34dp"
android:gravity="center"
android:text="New style"
android:textColor="#FFFFFF"
android:textSize="24sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/buttonFromBuiltInStyle"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="16dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/builtInStyleIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_edit"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/builtInStyleText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="From built-In style"
android:textColor="#FFFFFF"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/buttonFromFile"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/buttonFromBuiltInStyle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/fileIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@drawable/ic_file"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/fileBtnText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="From file"
android:textColor="#FFFFFF"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/buttonCreateEmpty"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="10dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/buttonFromFile"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/emptyIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@drawable/ic_create"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/emptyBtnText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Create empty"
android:textColor="#FFFFFF"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,305 +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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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/styleType"
android:layout_width="112dp"
android:layout_height="27dp"
android:layout_marginTop="4dp"
android:gravity="center"
android:text="Style Type?"
android:textColor="#FDFDFD"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/styleName" />
<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" />
<com.google.android.material.card.MaterialCardView
android:id="@+id/editButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="16dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/builtInStyleAlert"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/hideIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_edit"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="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>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/copyButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/editButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/copyIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@drawable/ic_copy"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="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>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/exportButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/copyButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/exportIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_save"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="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_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/deleteButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/exportButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/deleteIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_delete"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="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_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/closeButton"
android:layout_width="match_parent"
android:layout_height="38dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="10dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="#1B1A1A"
app:cardCornerRadius="0dp"
app:cardElevation="0dp"
app:strokeColor="#292828"
app:strokeWidth="1dp"
app:layout_constraintTop_toBottomOf="@id/deleteButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/closeIcon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="16dp"
android:src="@android:drawable/ic_menu_more"
app:tint="#A4A3A3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="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_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</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>
+11 -17
View File
@@ -8,36 +8,33 @@
<ImageButton
android:id="@+id/routeButton"
android:layout_width="64dp"
android:layout_height="38dp"
android:layout_width="65dp"
android:layout_height="42dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="34dp"
android:layout_marginBottom="36dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
android:src="@android:drawable/ic_menu_directions"
app:layout_constraintBottom_toBottomOf="@+id/placesButton"
app:layout_constraintStart_toStartOf="parent" />
<ImageButton
android:id="@+id/placesButton"
android:layout_width="64dp"
android:layout_height="38dp"
android:layout_width="65dp"
android:layout_height="43dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="34dp"
android:layout_marginBottom="36dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
app:layout_constraintBottom_toBottomOf="@+id/layersButton"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@android:drawable/ic_menu_myplaces" />
<ImageButton
android:id="@+id/layersButton"
android:layout_width="64dp"
android:layout_height="38dp"
android:layout_width="65dp"
android:layout_height="42dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="4dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_styles_menu" />
@@ -45,11 +42,10 @@
<ImageButton
android:id="@+id/modeButton"
android:layout_width="60dp"
android:layout_height="34dp"
android:layout_height="36dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="44dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
android:src="@android:drawable/ic_menu_manage"
app:layout_constraintBottom_toBottomOf="@+id/locationButtonContainer"
app:layout_constraintEnd_toEndOf="parent"
@@ -58,12 +54,11 @@
<ImageButton
android:id="@+id/attributionButton"
android:layout_width="60dp"
android:layout_height="24dp"
android:layout_height="31dp"
android:layout_marginStart="272dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.375"
@@ -74,7 +69,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="21dp"
android:layout_marginBottom="24dp"
app:layout_constraintBottom_toBottomOf="@+id/attributionButton"
app:layout_constraintEnd_toEndOf="parent">
@@ -83,7 +78,6 @@
android:layout_width="60dp"
android:layout_height="47dp"
android:backgroundTint="#000000"
android:background="@drawable/bg_map_button"
android:contentDescription="Location"
app:srcCompat="@android:drawable/ic_menu_mylocation" />
+1
View File
@@ -2,4 +2,5 @@
<android.widget.LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
</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,38 +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="6dp">
<LinearLayout
android:id="@+id/searchContainer"
android:layout_width="320dp"
android:layout_height="35dp"
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>
@@ -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"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#000000"
android:background="#0E0000"
android:foregroundTint="#FFFFFF">
<TextView
@@ -15,7 +16,7 @@
android:layout_marginEnd="12dp"
android:text="Choice Item"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textSize="24sp"
android:ellipsize="end"
android:maxLines="1"
android:scrollHorizontally="false"
@@ -23,12 +24,12 @@
android:autoSizeMinTextSize="12sp"
android:autoSizeMaxTextSize="24sp"
android:autoSizeStepGranularity="1sp"
app:layout_constraintStart_toEndOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/poiIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/icon"
android:id="@+id/poiIcon"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_marginStart="7dp"
@@ -38,20 +39,20 @@
app:srcCompat="@drawable/maplibre_info_bg_selector" />
<TextView
android:id="@+id/details"
android:id="@+id/type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="7dp"
android:layout_marginEnd="12dp"
android:layout_marginBottom="12dp"
android:text="Type"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:textStyle="bold"
android:ellipsize="end"
android:singleLine="true"
app:layout_constraintStart_toEndOf="@id/icon"
app:layout_constraintStart_toEndOf="@id/poiIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/choiceName"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,25 +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"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/selectionName"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/currentSelection"
android:drawableEnd="@drawable/ic_dropdown"
android:drawablePadding="4dp"
android:drawableTint="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
+14 -157
View File
@@ -2,7 +2,6 @@
<androidx.constraintlayout.widget.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#000000"
@@ -20,183 +19,41 @@
<ImageButton
android:id="@+id/closeButton"
android:layout_width="30dp"
android:layout_height="27dp"
android:layout_height="30dp"
android:backgroundTint="#000000"
app:srcCompat="@android:drawable/ic_menu_close_clear_cancel"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="28dp"
<TextView
android:id="@+id/stylesLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Styles"
android:textColor="#FFFFFF"
android:textSize="18sp"
android:layout_marginTop="16dp"
android:layout_marginBottom="2dp"
app:layout_constraintTop_toBottomOf="@id/dragHandle"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/stylesTab"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Style"
android:textAllCaps="false"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="@android:color/white"
android:clickable="true"
android:focusable="true"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/ic_bubble"
android:drawablePadding="6dp"
android:drawableTint="@android:color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/optionsTab"
app:layout_constraintHorizontal_chainStyle="packed" />
<TextView
android:id="@+id/optionsTab"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Options"
android:textAllCaps="false"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#888888"
android:clickable="true"
android:focusable="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/ic_sliders"
android:drawablePadding="2dp"
android:drawableTint="#888888"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/stylesTab"
app:layout_constraintEnd_toStartOf="@id/overlaysTab" />
<TextView
android:id="@+id/overlaysTab"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="Overlays"
android:textAllCaps="false"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#888888"
android:clickable="true"
android:focusable="true"
android:layout_marginStart="8dp"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/ic_stack"
android:drawablePadding="4dp"
android:drawableTint="#888888"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/optionsTab"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<FrameLayout
android:id="@+id/tabContent"
<HorizontalScrollView
android:id="@+id/styles"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/tabs"
app:layout_constraintTop_toBottomOf="@id/stylesLabel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" >
<!-- Styles tab -->
<LinearLayout
android:id="@+id/styles"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:id="@+id/stylesScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:fillViewport="false">
app:layout_constraintBottom_toBottomOf="parent">
<LinearLayout
android:id="@+id/stylesLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</HorizontalScrollView>
</LinearLayout>
<!-- Options tab -->
<ScrollView
android:id="@+id/options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<LinearLayout
android:id="@+id/optionsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sources"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="12dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/sourcesLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</ScrollView>
<!-- Overlays tab -->
<ScrollView
android:id="@+id/overlays"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<LinearLayout
android:id="@+id/overlaysLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
</FrameLayout>
<!-- Sources selection to be implemented here -->
</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">
<!-- Base application theme. -->
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
@@ -11,5 +11,6 @@
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
+1 -7
View File
@@ -1,11 +1,5 @@
<resources>
<string name="app_name">Strike Maps</string>
<string name="settings_title">Settings</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-array name="map_renderers">
<item>MapLibre GL JS</item>
<item>MapLibre Native</item>
<item>VTM</item>
</string-array>
<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>
</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. -->
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<style name="Theme.StrikeMaps" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/black</item>
<item name="colorPrimaryVariant">@color/black</item>
@@ -11,5 +11,6 @@
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="21">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
+3
View File
@@ -10,6 +10,7 @@ constraintlayout = "2.1.4"
playServicesMaps = "19.0.0"
viewpager2 = "1.1.0"
kotlin = "2.3.10"
coreKtx = "1.18.0"
[libraries]
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" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
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" }
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }