Change internal style filesystem to an internal registry and introduce style options concept
This commit is contained in:
@@ -3,11 +3,10 @@ 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
|
The source for these builds is available on https://github.com/maplibre/maplibre-gl-js
|
||||||
#
|
#
|
||||||
|
|
||||||
All files inside the /dist folder are under the following license
|
All files inside the /dist folder are subject to the following license
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
Copyright (c) 2023, MapLibre contributors
|
Copyright (c) 2023, MapLibre contributors
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package eu.konggdev.strikemaps;
|
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 eu.konggdev.strikemaps.app.AppController;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import android.content.SharedPreferences;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.MainActivity;
|
import eu.konggdev.strikemaps.MainActivity;
|
||||||
import eu.konggdev.strikemaps.R;
|
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
|
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
|
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
|
||||||
|
|
||||||
@@ -14,28 +14,43 @@ public class AppController {
|
|||||||
private final MainActivity appActivity;
|
private final MainActivity appActivity;
|
||||||
|
|
||||||
private MapComponent map;
|
private MapComponent map;
|
||||||
|
|
||||||
private UIComponent ui;
|
private UIComponent ui;
|
||||||
public AppController(MainActivity refActivity) {
|
|
||||||
appActivity = refActivity;
|
private RegistryStorageComponent registry;
|
||||||
}
|
|
||||||
|
|
||||||
|
public AppController(MainActivity appActivity) { this.appActivity = appActivity;}
|
||||||
|
|
||||||
public void logcat(String log) {
|
public void logcat(String log) {
|
||||||
appActivity.logcat(log);
|
appActivity.logcat(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UIComponent getUi() {
|
public UIComponent getUi() {
|
||||||
if (ui == null) init();
|
if (ui == null) init();
|
||||||
return ui;
|
return ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MapComponent getMap() {
|
public MapComponent getMap() {
|
||||||
if (map == null) init();
|
if (map == null) init();
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RegistryStorageComponent getRegistry() {
|
||||||
|
if (registry == null) init();
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
public SharedPreferences getPrefs() {
|
public SharedPreferences getPrefs() {
|
||||||
return getActivity().getSharedPreferences("user_prefs", MODE_PRIVATE);
|
return getActivity().getSharedPreferences("user_prefs", MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppCompatActivity getActivity() { return appActivity; }
|
public AppCompatActivity getActivity() { return appActivity; }
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
if (getActivity().getSupportActionBar() != null)
|
if (getActivity().getSupportActionBar() != null)
|
||||||
getActivity().getSupportActionBar().show();
|
getActivity().getSupportActionBar().show();
|
||||||
|
if(registry == null) registry = new RegistryStorageComponent(this);
|
||||||
if(map == null) map = new MapComponent(this);
|
if(map == null) map = new MapComponent(this);
|
||||||
if(ui == null) {
|
if(ui == null) {
|
||||||
ui = new UIComponent(this, map);
|
ui = new UIComponent(this, map);
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
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 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 String startupMapStyle(SharedPreferences prefs) {
|
|
||||||
return prefs.getString(KEY_STARTUP_MAP_STYLE, DEFAULT_MAP_STYLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean startupMapStyle(SharedPreferences prefs, String updated) {
|
|
||||||
return prefs.edit().putString(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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-65
@@ -1,17 +1,29 @@
|
|||||||
package eu.konggdev.strikemaps.data.helper;
|
package eu.konggdev.strikemaps.helper;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
|
|
||||||
//FIXME: Ugly
|
//FIXME: Ugly
|
||||||
public final class FileHelper {
|
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) {
|
public static String loadStringFromAssetFile(String filePath, AppController app) {
|
||||||
try (InputStream is = app.getActivity().getAssets().open(filePath)) {
|
try (InputStream is = app.getActivity().getAssets().open(filePath)) {
|
||||||
int size = is.available();
|
int size = is.available();
|
||||||
@@ -24,19 +36,6 @@ public final class FileHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String loadStringFromUserFile(String filePath) {
|
|
||||||
File file = new File(filePath);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeUserFile(String path, String fileName, String content, AppController app) throws IOException {
|
public static void writeUserFile(String path, String fileName, String content, AppController app) throws IOException {
|
||||||
try {
|
try {
|
||||||
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
|
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
|
||||||
@@ -60,16 +59,6 @@ public final class FileHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean deleteFile(String filePath) {
|
|
||||||
File file = new File(filePath);
|
|
||||||
|
|
||||||
if (!file.exists() || !file.isFile()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return file.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] getAssetFiles(String path, String fileExt, AppController app) {
|
public static String[] getAssetFiles(String path, String fileExt, AppController app) {
|
||||||
AssetManager assetManager = app.getActivity().getAssets();
|
AssetManager assetManager = app.getActivity().getAssets();
|
||||||
try {
|
try {
|
||||||
@@ -109,44 +98,4 @@ public final class FileHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean userFileExists(String path, String fileName, AppController app) {
|
|
||||||
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
|
|
||||||
|
|
||||||
if (!userDirectory.exists() || !userDirectory.isDirectory()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = new File(userDirectory, fileName);
|
|
||||||
|
|
||||||
return file.exists() && file.isFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String[] getUserFiles(String path, String fileExt, AppController app) {
|
|
||||||
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), 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,127 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,10 @@ import java.util.*;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import eu.konggdev.strikemaps.Component;
|
import eu.konggdev.strikemaps.Component;
|
||||||
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
|
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
|
||||||
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
|
||||||
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer;
|
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
|
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
||||||
|
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
|
||||||
|
import eu.konggdev.strikemaps.map.renderer.implementation.VtmRenderer;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
|||||||
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
|
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
|
||||||
|
|
||||||
public class MapComponent implements Component {
|
public class MapComponent implements Component {
|
||||||
MapRenderer mapRenderer;
|
private final MapRenderer mapRenderer;
|
||||||
AppController app;
|
private final AppController app;
|
||||||
|
|
||||||
public MapStyle style;
|
public MapStyle style;
|
||||||
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
|
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
|
||||||
@@ -50,7 +50,7 @@ public class MapComponent implements Component {
|
|||||||
|
|
||||||
public void setStyle(MapStyle style) {
|
public void setStyle(MapStyle style) {
|
||||||
this.style = style;
|
this.style = style;
|
||||||
mapRenderer.styleUpdate(style);
|
mapRenderer.styleUpdate(style.effectiveDocument());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void switchOverlay(MapOverlay overlay) {
|
public void switchOverlay(MapOverlay overlay) {
|
||||||
@@ -67,14 +67,14 @@ public class MapComponent implements Component {
|
|||||||
return overlays.containsKey(overlay);
|
return overlays.containsKey(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectPoint(Feature selection) {
|
|
||||||
//FIXME: Put back FragmentPointPreviewPopup (private code atm)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void overlayUpdate(MapOverlay in) {
|
public void overlayUpdate(MapOverlay in) {
|
||||||
mapRenderer.overlayUpdate(in);
|
mapRenderer.overlayUpdate(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void selectPoint(Feature selection) {
|
||||||
|
//FIXME: Put back FragmentPointPreviewPopup (private code atm)
|
||||||
|
}
|
||||||
|
|
||||||
public boolean onMapClick(LatLng point) {
|
public boolean onMapClick(LatLng point) {
|
||||||
List<Feature> features = mapRenderer.featuresAtPoint(point);
|
List<Feature> features = mapRenderer.featuresAtPoint(point);
|
||||||
|
|
||||||
@@ -99,4 +99,12 @@ public class MapComponent implements Component {
|
|||||||
//TODO: Likely Nonfeature(?) point selection
|
//TODO: Likely Nonfeature(?) point selection
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onMapInit() {
|
||||||
|
setStyle(
|
||||||
|
app.getRegistry().getStyle(
|
||||||
|
UserPrefsHelper.startupMapStyle(app.getPrefs())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -7,8 +7,8 @@ import java.net.URI;
|
|||||||
public final class OfflineDownloader {
|
public final class OfflineDownloader {
|
||||||
|
|
||||||
public static String[] fetchAvailableExports(MapSource source) {
|
public static String[] fetchAvailableExports(MapSource source) {
|
||||||
URI uri = URI.create(source.url);
|
//URI uri = URI.create(source.url);
|
||||||
String host = uri.getHost();
|
//String host = uri.getHost();
|
||||||
return new String[0];
|
return new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-1
@@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
|
import eu.konggdev.strikemaps.data.provider.LocationDataProvider;
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package eu.konggdev.strikemaps.map.renderer;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface MapRenderer {
|
public interface MapRenderer {
|
||||||
void styleUpdate(MapStyle style);
|
void styleUpdate(StyleDocument style);
|
||||||
|
|
||||||
void overlayUpdate(MapOverlay overlay);
|
void overlayUpdate(MapOverlay overlay);
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -3,7 +3,6 @@ package eu.konggdev.strikemaps.map.renderer.implementation;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.webkit.ValueCallback;
|
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebSettings;
|
import android.webkit.WebSettings;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
@@ -18,9 +17,9 @@ import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
|||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.android.maps.Style;
|
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -61,7 +60,7 @@ public class MapLibreGLJSRenderer implements MapRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void styleUpdate(MapStyle style) {
|
public void styleUpdate(StyleDocument style) {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
|
|
||||||
@@ -73,7 +72,8 @@ public class MapLibreGLJSRenderer implements MapRenderer {
|
|||||||
//Sources
|
//Sources
|
||||||
ObjectNode sources = mapper.createObjectNode();
|
ObjectNode sources = mapper.createObjectNode();
|
||||||
if (style.sources != null)
|
if (style.sources != null)
|
||||||
style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v)));
|
for (MapSource source : style.sources)
|
||||||
|
sources.set(source.name, source.makeJson());
|
||||||
|
|
||||||
//Layers
|
//Layers
|
||||||
ArrayNode layers = mapper.createArrayNode();
|
ArrayNode layers = mapper.createArrayNode();
|
||||||
@@ -145,5 +145,5 @@ public class MapLibreGLJSRenderer implements MapRenderer {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bridge { }
|
static class Bridge { }
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -10,10 +10,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
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.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import org.maplibre.android.MapLibre;
|
import org.maplibre.android.MapLibre;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.android.maps.MapLibreMap;
|
import org.maplibre.android.maps.MapLibreMap;
|
||||||
@@ -32,7 +32,6 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
@NonNull MapComponent controller;
|
@NonNull MapComponent controller;
|
||||||
MapLibreMap map;
|
MapLibreMap map;
|
||||||
final MapView mapView;
|
final MapView mapView;
|
||||||
|
|
||||||
private JsonNode origin;
|
private JsonNode origin;
|
||||||
|
|
||||||
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
|
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
|
||||||
@@ -45,7 +44,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void styleUpdate(MapStyle style) {
|
public void styleUpdate(StyleDocument style) {
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||||
|
|
||||||
@@ -57,7 +56,8 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
//Sources
|
//Sources
|
||||||
ObjectNode sources = mapper.createObjectNode();
|
ObjectNode sources = mapper.createObjectNode();
|
||||||
if (style.sources != null)
|
if (style.sources != null)
|
||||||
style.sources.forEach((k, v) -> sources.set(k, mapper.valueToTree(v)));
|
for (MapSource source : style.sources)
|
||||||
|
sources.set(source.name, source.makeJson());
|
||||||
|
|
||||||
//Layers
|
//Layers
|
||||||
ArrayNode layers = mapper.createArrayNode();
|
ArrayNode layers = mapper.createArrayNode();
|
||||||
@@ -128,7 +128,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
|
|||||||
public void onMapReady(@NonNull MapLibreMap maplibreMap) {
|
public void onMapReady(@NonNull MapLibreMap maplibreMap) {
|
||||||
this.map = maplibreMap;
|
this.map = maplibreMap;
|
||||||
|
|
||||||
controller.setStyle(MapStyle.fromFile(UserPrefsHelper.startupMapStyle(app.getPrefs()), app));
|
controller.onMapInit();
|
||||||
|
|
||||||
//I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :)
|
//I have my own implementation of attribution that credits MapLibre among others, it's not as bad as it looks :)
|
||||||
map.getUiSettings().setLogoEnabled(false);
|
map.getUiSettings().setLogoEnabled(false);
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@ import eu.konggdev.strikemaps.app.AppController;
|
|||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
|
||||||
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import org.maplibre.android.geometry.LatLng;
|
import org.maplibre.android.geometry.LatLng;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
@@ -38,7 +38,7 @@ public class VtmRenderer implements MapRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void styleUpdate(MapStyle style) {
|
public void styleUpdate(StyleDocument style) {
|
||||||
//TODO
|
//TODO
|
||||||
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
OkHttpClient.Builder builder = new OkHttpClient.Builder();
|
||||||
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
|
OSciMap4TileSource tileSource = OSciMap4TileSource.builder().httpFactory(new OkHttpEngine.OkHttpFactory(builder)).build();
|
||||||
|
|||||||
@@ -1,21 +1,139 @@
|
|||||||
package eu.konggdev.strikemaps.map.source;
|
package eu.konggdev.strikemaps.map.source;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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 class MapSource {
|
||||||
public String url;
|
public enum MapSourceContractType {
|
||||||
public JsonNode data;
|
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 type;
|
public String type;
|
||||||
public String schema;
|
public String schema;
|
||||||
|
|
||||||
public JsonNode tiles;
|
|
||||||
public int minzoom;
|
public int minzoom;
|
||||||
public int maxzoom;
|
public int maxzoom;
|
||||||
|
|
||||||
public String scheme;
|
public String scheme;
|
||||||
public int tileSize;
|
public int tileSize;
|
||||||
|
|
||||||
public String attribution;
|
|
||||||
|
|
||||||
public String encoding;
|
public String encoding;
|
||||||
public MapSource() { }
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
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,68 +1,64 @@
|
|||||||
package eu.konggdev.strikemaps.map.style;
|
package eu.konggdev.strikemaps.map.style;
|
||||||
|
|
||||||
import android.graphics.Bitmap;
|
import androidx.annotation.NonNull;
|
||||||
import android.graphics.BitmapFactory;
|
import androidx.annotation.Nullable;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
|
||||||
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 class MapStyle {
|
||||||
//Only local data
|
public static final class StoredRepresentation {
|
||||||
public String name;
|
public final String json;
|
||||||
public Bitmap icon;
|
public final StyleOptions options;
|
||||||
|
|
||||||
public JsonNode metadata; // everything except layers + sources
|
public final StyleManagementMetadata managementMetadata;
|
||||||
public Map<String, MapSource> sources;
|
|
||||||
public ArrayNode layerDefinitions; // "layers" array
|
|
||||||
|
|
||||||
//FIXME
|
public StoredRepresentation(String json, StyleOptions options, StyleManagementMetadata managementMetadata) {
|
||||||
public static MapStyle fromFile(String filename, AppController app) {
|
this.json = json;
|
||||||
String styleContents;
|
this.options = options;
|
||||||
if (filename.startsWith("/storage")) styleContents = FileHelper.loadStringFromUserFile(filename);
|
this.managementMetadata = managementMetadata;
|
||||||
else styleContents = FileHelper.loadStringFromAssetFile(filename, app);
|
}
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
public MapStyle restore() {
|
||||||
try {
|
MapStyle style = new MapStyle(
|
||||||
JsonNode root = mapper.readTree(styleContents);
|
json,
|
||||||
|
options,
|
||||||
MapStyle style = new MapStyle();
|
managementMetadata
|
||||||
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;
|
return style;
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getIcon(String iconLocator, AppController app) {
|
@NonNull public final StyleDocument document;
|
||||||
switch(iconLocator.split("//")[0]) {
|
@NonNull public final StyleOptions options;
|
||||||
//TODO: https
|
|
||||||
case "assets:":
|
// Null when the style is not managed
|
||||||
return BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], app));
|
@Nullable public StyleManagementMetadata managementMetadata;
|
||||||
default:
|
|
||||||
app.logcat("Unimplemented icon locator space: " + iconLocator);
|
// Original json representation of the style document, as we got it
|
||||||
return null;
|
@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 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
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() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,8 +7,10 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import eu.konggdev.strikemaps.R;
|
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.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
|
|
||||||
public class GenericItem implements UIItem {
|
public class GenericItem implements UIItem {
|
||||||
@@ -53,19 +55,20 @@ public class GenericItem implements UIItem {
|
|||||||
hasImage = true;
|
hasImage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static GenericItem fromStyle(MapStyle style, MapComponent map, Runnable onClick) {
|
public static GenericItem fromStyle(StyleDocument style, AppController app, Runnable onClick) {
|
||||||
if(style == null) return new GenericItem("Unknown");
|
if(style == null) return new GenericItem("Unknown");
|
||||||
if(style.icon != null)
|
if(style.icon != null)
|
||||||
return new GenericItem(style.name, style.icon, onClick);
|
return new GenericItem(style.name, FileHelper.getIcon(style.icon, app), onClick);
|
||||||
return new GenericItem(style.name, onClick);
|
return new GenericItem(style.name, onClick);
|
||||||
}
|
}
|
||||||
public final static GenericItem fromStyle(MapStyle style, MapComponent map, Runnable onClick, Runnable onLongClick) {
|
public static GenericItem fromStyle(StyleDocument style, AppController app, Runnable onClick, Runnable onLongClick) {
|
||||||
if(style == null) return new GenericItem("Unknown");
|
if(style == null) return new GenericItem("Unknown");
|
||||||
if(style.icon != null)
|
if(style.icon != null)
|
||||||
return new GenericItem(style.name, style.icon, onClick, onLongClick);
|
return new GenericItem(style.name, FileHelper.getIcon(style.icon, app), onClick, onLongClick);
|
||||||
return new GenericItem(style.name, onClick, onLongClick);
|
return new GenericItem(style.name, onClick, onLongClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public View makeView(UIComponent spawner) {
|
public View makeView(UIComponent spawner) {
|
||||||
View v = spawner.inflateUi(R.layout.item_generic);
|
View v = spawner.inflateUi(R.layout.item_generic);
|
||||||
//FIXME: These shouldn't be casted like that!
|
//FIXME: These shouldn't be casted like that!
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package eu.konggdev.strikemaps.ui.element.item;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
|
import eu.konggdev.strikemaps.map.source.MapSource;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
|
|
||||||
public class InlineItem implements UIItem {
|
public class InlineItem implements UIItem {
|
||||||
@@ -18,6 +19,15 @@ public class InlineItem implements UIItem {
|
|||||||
this.onClick = onClick;
|
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
|
@Override
|
||||||
public View makeView(UIComponent spawner) {
|
public View makeView(UIComponent spawner) {
|
||||||
View view = spawner.inflateUi(R.layout.item_inline);
|
View view = spawner.inflateUi(R.layout.item_inline);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public class PreviewItem implements UIItem {
|
|||||||
public static PreviewItem fromFeature(Feature feature) {
|
public static PreviewItem fromFeature(Feature feature) {
|
||||||
return new PreviewItem(feature.getStringProperty("name"), feature.getStringProperty("class"));
|
return new PreviewItem(feature.getStringProperty("name"), feature.getStringProperty("class"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public View makeView(UIComponent spawner) {
|
public View makeView(UIComponent spawner) {
|
||||||
View view = spawner.inflateUi(R.layout.item_preview);
|
View view = spawner.inflateUi(R.layout.item_preview);
|
||||||
((TextView) view.findViewById(R.id.choiceName)).setText(name);
|
((TextView) view.findViewById(R.id.choiceName)).setText(name);
|
||||||
@@ -36,6 +38,7 @@ public class PreviewItem implements UIItem {
|
|||||||
((TextView) view.findViewById(R.id.details)).setText(details);
|
((TextView) view.findViewById(R.id.details)).setText(details);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public View makeView(UIComponent spawner, View.OnClickListener onClick) {
|
public View makeView(UIComponent spawner, View.OnClickListener onClick) {
|
||||||
View view = makeView(spawner);
|
View view = makeView(spawner);
|
||||||
view.setOnClickListener(onClick);
|
view.setOnClickListener(onClick);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,64 +1,38 @@
|
|||||||
package eu.konggdev.strikemaps.ui.factory;
|
package eu.konggdev.strikemaps.ui.factory;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.EditorInfo;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.*;
|
import android.widget.*;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
import eu.konggdev.strikemaps.app.util.JsonPatcher;
|
||||||
import eu.konggdev.strikemaps.data.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
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.UIComponent;
|
||||||
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
|
||||||
import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
|
import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
||||||
import org.maplibre.geojson.Feature;
|
import org.maplibre.geojson.Feature;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static androidx.core.content.ContextCompat.getSystemService;
|
|
||||||
|
|
||||||
//FIXME: Move Item functions into specific classes for specific types - e.g. StyleItem
|
//FIXME: Cleaner architecture would be having a class for each AlertDialog type
|
||||||
public final class AlertDialogFactory {
|
public final class AlertDialogFactory {
|
||||||
public static AlertDialog copyBuiltInStyle(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
|
public static AlertDialog copyBuiltInStyle(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
|
||||||
//TODO: Use an UI element thats supposed to be vertical, instead of GenericItem
|
//TODO: Use an UI element that's supposed to be vertical, instead of GenericItem
|
||||||
List<String> styles = Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app));
|
|
||||||
|
|
||||||
LinearLayout container = new LinearLayout(app.getActivity());
|
LinearLayout container = new LinearLayout(app.getActivity());
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
for (String style : styles) {
|
|
||||||
View itemView = GenericItem
|
|
||||||
.fromStyle(
|
|
||||||
MapStyle.fromFile(style, app),
|
|
||||||
map,
|
|
||||||
() -> ui.alert(AlertDialogFactory.createStyle(app, FileHelper.loadStringFromAssetFile(style, app), mapChangePopup))
|
|
||||||
)
|
|
||||||
.makeView(ui);
|
|
||||||
|
|
||||||
container.addView(itemView);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollView scrollView = new ScrollView(app.getActivity());
|
ScrollView scrollView = new ScrollView(app.getActivity());
|
||||||
scrollView.addView(container);
|
scrollView.addView(container);
|
||||||
|
|
||||||
@@ -75,15 +49,6 @@ public final class AlertDialogFactory {
|
|||||||
final EditText nameInput = new EditText(app.getActivity());
|
final EditText nameInput = new EditText(app.getActivity());
|
||||||
nameInput.setHint("Name");
|
nameInput.setHint("Name");
|
||||||
|
|
||||||
final CheckBox inferFileName = new CheckBox(app.getActivity());
|
|
||||||
inferFileName.setText("Infer filename automatically");
|
|
||||||
inferFileName.setChecked(true);
|
|
||||||
|
|
||||||
final EditText fileInput = new EditText(app.getActivity());
|
|
||||||
fileInput.setHint("Filename");
|
|
||||||
|
|
||||||
final TextView inferedFileName = new TextView(app.getActivity());
|
|
||||||
inferedFileName.setPadding(0, 2, 0, 0);
|
|
||||||
LinearLayout container = new LinearLayout(app.getActivity());
|
LinearLayout container = new LinearLayout(app.getActivity());
|
||||||
container.setOrientation(LinearLayout.VERTICAL);
|
container.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
@@ -91,38 +56,6 @@ public final class AlertDialogFactory {
|
|||||||
container.setPadding(padding, padding, padding, 0);
|
container.setPadding(padding, padding, padding, 0);
|
||||||
|
|
||||||
container.addView(nameInput);
|
container.addView(nameInput);
|
||||||
container.addView(inferFileName);
|
|
||||||
container.addView(fileInput);
|
|
||||||
container.addView(inferedFileName);
|
|
||||||
|
|
||||||
inferFileName.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
fileInput.setVisibility(isChecked ? View.GONE : View.VISIBLE);
|
|
||||||
inferedFileName.setVisibility(isChecked ? View.VISIBLE : View.GONE);
|
|
||||||
});
|
|
||||||
|
|
||||||
fileInput.setVisibility(inferFileName.isChecked() ? View.GONE : View.VISIBLE);
|
|
||||||
inferedFileName.setVisibility(inferFileName.isChecked() ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
nameInput.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
String nameText = "";
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
||||||
if (!s.isEmpty() && s != null) {
|
|
||||||
nameText = "File name will be: " + s.toString().toLowerCase(Locale.ROOT) + ".style.json";
|
|
||||||
}
|
|
||||||
} else if (s != null) {
|
|
||||||
nameText = "File name will be: " + s.toString().toLowerCase(Locale.ROOT) + ".style.json";
|
|
||||||
}
|
|
||||||
|
|
||||||
inferedFileName.setText(nameText);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {}
|
|
||||||
});
|
|
||||||
|
|
||||||
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
|
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
|
||||||
.setTitle("Create")
|
.setTitle("Create")
|
||||||
@@ -160,21 +93,6 @@ public final class AlertDialogFactory {
|
|||||||
|
|
||||||
assert !name.isEmpty();
|
assert !name.isEmpty();
|
||||||
|
|
||||||
String fileName;
|
|
||||||
if (inferFileName.isChecked()) {
|
|
||||||
fileName = name.toLowerCase(Locale.ROOT) + ".style.json";
|
|
||||||
} else {
|
|
||||||
fileName = fileInput.getText().toString();
|
|
||||||
if (fileName.isEmpty()) {
|
|
||||||
fileInput.setError("File name required");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fileName.endsWith(".style.json")) {
|
|
||||||
fileInput.setError("File must end with .style.json");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonNode root;
|
JsonNode root;
|
||||||
@@ -194,12 +112,12 @@ public final class AlertDialogFactory {
|
|||||||
root = JsonPatcher.patch(root, node);
|
root = JsonPatcher.patch(root, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FileHelper.userFileExists("style", fileName, app)) {
|
app.getRegistry().addStyle(new MapStyle(
|
||||||
app.getUi().alert(askUserOverwriteFile(app, fileName, "style", mapper.writeValueAsString(root), dialog, mapChangePopup));
|
mapper.writeValueAsString(root),
|
||||||
} else {
|
new StyleOptions(),
|
||||||
FileHelper.writeUserFile("style", fileName, mapper.writeValueAsString(root), app);
|
null
|
||||||
}
|
));
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Toast.makeText(app.getActivity(), "Failed to create", Toast.LENGTH_SHORT).show();
|
Toast.makeText(app.getActivity(), "Failed to create", Toast.LENGTH_SHORT).show();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -212,46 +130,6 @@ public final class AlertDialogFactory {
|
|||||||
return dialog;
|
return dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AlertDialog askUserOverwriteFile(AppController app, String fileName, String path, String content, FragmentMapChangePopup mapChangePopup) {
|
|
||||||
return new AlertDialog.Builder(app.getActivity())
|
|
||||||
.setMessage("Style of filename " + fileName + " already exists, do you wish to overwrite it?")
|
|
||||||
.setPositiveButton("Yes", (dialog, which) -> {
|
|
||||||
try {
|
|
||||||
FileHelper.writeUserFile(path, fileName, content, app);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
mapChangePopup.reloadStyles();
|
|
||||||
})
|
|
||||||
.setNegativeButton("No", null)
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AlertDialog askUserOverwriteFile(AppController app, String fileName, String path, String content, AlertDialog originDialog, FragmentMapChangePopup mapChangePopup) {
|
|
||||||
return new AlertDialog.Builder(app.getActivity())
|
|
||||||
.setMessage("Style of filename: " + fileName + " already exists, do you wish to overwrite it?")
|
|
||||||
.setPositiveButton("Yes", (dialog, which) -> {
|
|
||||||
try {
|
|
||||||
FileHelper.writeUserFile(path, fileName, content, app);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originDialog != null) {
|
|
||||||
originDialog.dismiss();
|
|
||||||
}
|
|
||||||
mapChangePopup.reloadStyles();
|
|
||||||
dialog.dismiss();
|
|
||||||
})
|
|
||||||
.setNegativeButton("No", (dialog, which) -> {
|
|
||||||
if (originDialog != null) {
|
|
||||||
originDialog.dismiss();
|
|
||||||
}
|
|
||||||
dialog.dismiss();
|
|
||||||
})
|
|
||||||
.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AlertDialog pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) {
|
public static AlertDialog pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) {
|
||||||
LinearLayout layout = new LinearLayout(app.getActivity());
|
LinearLayout layout = new LinearLayout(app.getActivity());
|
||||||
layout.setOrientation(LinearLayout.VERTICAL);
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|||||||
+10
-14
@@ -18,8 +18,9 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
|||||||
import com.google.android.material.card.MaterialCardView;
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.app.util.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
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.UIComponent;
|
||||||
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
||||||
@@ -35,7 +36,6 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
FragmentMapChangePopup mapChangePopup;
|
FragmentMapChangePopup mapChangePopup;
|
||||||
|
|
||||||
|
|
||||||
private final ActivityResultLauncher<Intent> importLauncher =
|
private final ActivityResultLauncher<Intent> importLauncher =
|
||||||
registerForActivityResult(
|
registerForActivityResult(
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
@@ -79,17 +79,15 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
|
|||||||
mapChangePopup
|
mapChangePopup
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
if (!FileHelper.userFileExists("style", fileName, app)) {
|
app.getRegistry().addStyle(
|
||||||
FileHelper.writeUserFile("style", fileName, content, app);
|
new MapStyle(
|
||||||
} else {
|
content,
|
||||||
app.getUi().alert(AlertDialogFactory.askUserOverwriteFile(app, fileName, "style", content, mapChangePopup));
|
new StyleOptions(),
|
||||||
}
|
null
|
||||||
|
)
|
||||||
|
);
|
||||||
mapChangePopup.reloadStyles();
|
mapChangePopup.reloadStyles();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,9 +117,7 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
@Nullable ViewGroup container,
|
|
||||||
@Nullable Bundle savedInstanceState) {
|
|
||||||
|
|
||||||
View view = inflater.inflate(R.layout.dialog_new_style, container, false);
|
View view = inflater.inflate(R.layout.dialog_new_style, container, false);
|
||||||
|
|
||||||
|
|||||||
+41
-65
@@ -16,7 +16,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
|||||||
import com.google.android.material.card.MaterialCardView;
|
import com.google.android.material.card.MaterialCardView;
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.app.util.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
import eu.konggdev.strikemaps.ui.UIComponent;
|
import eu.konggdev.strikemaps.ui.UIComponent;
|
||||||
@@ -25,7 +24,6 @@ import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -35,82 +33,70 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
@NonNull
|
@NonNull
|
||||||
UIComponent ui;
|
UIComponent ui;
|
||||||
@NonNull
|
@NonNull
|
||||||
FragmentMapChangePopup mapChangePopup;
|
final FragmentMapChangePopup mapChangePopup;
|
||||||
|
|
||||||
private final MapStyle style;
|
private final MapStyle style;
|
||||||
private final String stylePath;
|
private final Integer id;
|
||||||
|
|
||||||
private final ActivityResultLauncher<Intent> exportLauncher =
|
private ActivityResultLauncher<Intent> exportLauncher;
|
||||||
registerForActivityResult(
|
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
|
||||||
result -> {
|
|
||||||
|
|
||||||
if (result.getResultCode() == android.app.Activity.RESULT_OK
|
|
||||||
&& result.getData() != null) {
|
|
||||||
|
|
||||||
Uri uri = result.getData().getData();
|
|
||||||
|
|
||||||
try (OutputStream out =
|
|
||||||
requireContext()
|
|
||||||
.getContentResolver()
|
|
||||||
.openOutputStream(uri)) {
|
|
||||||
|
|
||||||
out.write(getContents().getBytes());
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
//Action definitions
|
|
||||||
//*//
|
|
||||||
void deleteStyle() {
|
void deleteStyle() {
|
||||||
boolean deleted = FileHelper.deleteFile(stylePath);
|
app.getRegistry().deleteStyle(id);
|
||||||
if (!deleted) {
|
|
||||||
Toast.makeText(requireContext(), "Failed deleting style", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapChangePopup.reloadStyles();
|
mapChangePopup.reloadStyles();
|
||||||
dismiss();
|
dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showExportDialog() {
|
||||||
private void showExportDialog(String fileName) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
intent.setType("application/json");
|
intent.setType("application/json");
|
||||||
intent.putExtra(Intent.EXTRA_TITLE, fileName);
|
|
||||||
|
|
||||||
exportLauncher.launch(intent);
|
exportLauncher.launch(intent);
|
||||||
}
|
}
|
||||||
//*//
|
|
||||||
|
|
||||||
String getContents() {
|
public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, Integer id) {
|
||||||
if (stylePath.startsWith("/storage")) return FileHelper.loadStringFromUserFile(stylePath);
|
|
||||||
else return FileHelper.loadStringFromAssetFile(stylePath, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
public StyleDetailsBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup, MapStyle style, String stylePath) {
|
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.ui = ui;
|
this.ui = ui;
|
||||||
this.mapChangePopup = mapChangePopup;
|
this.mapChangePopup = mapChangePopup;
|
||||||
this.style = style;
|
this.style = style;
|
||||||
this.stylePath = stylePath;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@Nullable ViewGroup container,
|
super.onCreate(savedInstanceState);
|
||||||
@Nullable Bundle 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);
|
View view = inflater.inflate(R.layout.dialog_style_details, container, false);
|
||||||
|
|
||||||
TextView styleNameView = view.findViewById(R.id.styleName);
|
TextView styleNameView = view.findViewById(R.id.styleName);
|
||||||
TextView fileNameView = view.findViewById(R.id.fileName);
|
|
||||||
TextView styleTypeView = view.findViewById(R.id.styleType);
|
TextView styleTypeView = view.findViewById(R.id.styleType);
|
||||||
|
|
||||||
TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert);
|
TextView builtInStyleAlert = view.findViewById(R.id.builtInStyleAlert);
|
||||||
@@ -120,27 +106,17 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
|
|||||||
MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton);
|
MaterialCardView deleteButtonLayout = view.findViewById(R.id.deleteButton);
|
||||||
MaterialCardView closeButtonLayout = view.findViewById(R.id.closeButton);
|
MaterialCardView closeButtonLayout = view.findViewById(R.id.closeButton);
|
||||||
|
|
||||||
|
styleNameView.setText(style.document.name);
|
||||||
|
|
||||||
styleNameView.setText(style.name);
|
if (style.managementMetadata != null) {
|
||||||
|
|
||||||
String[] pathSplit = stylePath.split("/");
|
|
||||||
String fileName = pathSplit[pathSplit.length - 1];
|
|
||||||
|
|
||||||
fileNameView.setText(fileName);
|
|
||||||
|
|
||||||
if (Objects.equals(pathSplit[0], "bundled") && pathSplit.length > 1) {
|
|
||||||
styleTypeView.setText("Built-In Style");
|
styleTypeView.setText("Built-In Style");
|
||||||
fileNameView.setVisibility(View.GONE);
|
|
||||||
editButtonLayout.setVisibility(View.GONE);
|
|
||||||
builtInStyleAlert.setVisibility(View.VISIBLE);
|
|
||||||
deleteButtonLayout.setVisibility(View.GONE);
|
|
||||||
} else {
|
} else {
|
||||||
styleTypeView.setText("User Style");
|
styleTypeView.setText("User Style");
|
||||||
}
|
}
|
||||||
|
|
||||||
editButtonLayout.setOnClickListener(v -> Toast.makeText(requireContext(), "Editor not implemented yet\nWait for release", Toast.LENGTH_SHORT).show());
|
editButtonLayout.setOnClickListener(v -> Toast.makeText(requireContext(), "Editor not implemented yet\nWait for release", Toast.LENGTH_SHORT).show());
|
||||||
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, getContents(), mapChangePopup)));
|
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, style.json, mapChangePopup)));
|
||||||
exportButtonLayout.setOnClickListener(v -> showExportDialog(fileName));
|
exportButtonLayout.setOnClickListener(v -> showExportDialog());
|
||||||
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
|
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
|
||||||
closeButtonLayout.setOnClickListener(v -> dismiss());
|
closeButtonLayout.setOnClickListener(v -> dismiss());
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
|
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
|
||||||
import eu.konggdev.strikemaps.map.overlay.implementation.LocationOverlay;
|
import eu.konggdev.strikemaps.map.overlay.implementation.LocationOverlay;
|
||||||
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
|
||||||
|
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ public class FragmentLayoutSearch extends Fragment implements Layout {
|
|||||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setupButton(view, R.id.hamburgerButton, click(() -> {
|
setupButton(view, R.id.hamburgerButton, click(() -> {
|
||||||
|
|
||||||
View menuView = getLayoutInflater().inflate(R.layout.dropdown_main, null);
|
View menuView = getLayoutInflater().inflate(R.layout.dropdown_main, null);
|
||||||
@@ -80,43 +78,6 @@ public class FragmentLayoutSearch extends Fragment implements Layout {
|
|||||||
int popupWidth = menuView.getMeasuredWidth();
|
int popupWidth = menuView.getMeasuredWidth();
|
||||||
int containerWidth = anchor.getWidth();
|
int containerWidth = anchor.getWidth();
|
||||||
|
|
||||||
int xOffset = containerWidth - popupWidth;
|
|
||||||
popupWindow.showAsDropDown(anchor, xOffset, 1);
|
|
||||||
});
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
setupButton(view, R.id.offlineMapsButton, click(() -> {
|
|
||||||
View offlineMapsPopupView = getLayoutInflater().inflate(R.layout.dropdown_offline, null);
|
|
||||||
|
|
||||||
PopupWindow popupWindow = new PopupWindow(
|
|
||||||
offlineMapsPopupView,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
popupWindow.setOutsideTouchable(true);
|
|
||||||
popupWindow.setFocusable(true);
|
|
||||||
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
|
||||||
|
|
||||||
View anchor = view.findViewById(R.id.searchContainer);
|
|
||||||
|
|
||||||
setupButton(offlineMapsPopupView, R.id.offlineMaps, click(() -> {
|
|
||||||
popupWindow.dismiss();
|
|
||||||
app.getUi().swapScreen(DefinedScreen.OFFLINE);
|
|
||||||
}));
|
|
||||||
|
|
||||||
anchor.post(() -> {
|
|
||||||
|
|
||||||
offlineMapsPopupView.measure(
|
|
||||||
View.MeasureSpec.UNSPECIFIED,
|
|
||||||
View.MeasureSpec.UNSPECIFIED
|
|
||||||
);
|
|
||||||
|
|
||||||
int popupWidth = offlineMapsPopupView.getMeasuredWidth();
|
|
||||||
int containerWidth = anchor.getWidth();
|
|
||||||
|
|
||||||
int xOffset = containerWidth - popupWidth;
|
int xOffset = containerWidth - popupWidth;
|
||||||
popupWindow.showAsDropDown(anchor, xOffset, 1);
|
popupWindow.showAsDropDown(anchor, xOffset, 1);
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-31
@@ -2,23 +2,10 @@ package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.data.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.source.MapSource;
|
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
|
||||||
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
|
||||||
import eu.konggdev.strikemaps.ui.element.item.InlineItem;
|
|
||||||
import eu.konggdev.strikemaps.ui.element.item.PreviewItem;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class FragmentLayoutContentOfflineMaps extends Fragment implements MainContentLayout {
|
public class FragmentLayoutContentOfflineMaps extends Fragment implements MainContentLayout {
|
||||||
@NonNull AppController app;
|
@NonNull AppController app;
|
||||||
@@ -37,24 +24,9 @@ public class FragmentLayoutContentOfflineMaps extends Fragment implements MainCo
|
|||||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
List<MapSource> sources = new ArrayList<>();
|
|
||||||
|
|
||||||
List<String> stylePaths = new ArrayList<>();
|
// LinearLayout sourcesLayout = view.findViewById(R.id.llDownloadContainer);
|
||||||
stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app)));
|
// for (MapSource source : sources)
|
||||||
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
|
// sourcesLayout.addView(new InlineItem(source.name, () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
|
||||||
/* Parsing an entire MapStyle is absolutely unnecessary and resource wasteful
|
|
||||||
TODO: A method should be implemented to parse a List<MapSource> directly from the JSON */
|
|
||||||
for (String stylePath : stylePaths) {
|
|
||||||
MapStyle parsedStyle = MapStyle.fromFile(stylePath, app);
|
|
||||||
sources.addAll(parsedStyle.sources.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
LinearLayout sourcesLayout = view.findViewById(R.id.llDownloadContainer);
|
|
||||||
for (MapSource source : sources) {
|
|
||||||
if (!Objects.equals(source.type, "raster"))
|
|
||||||
sourcesLayout.addView(new InlineItem(source.url, () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
|
|
||||||
else
|
|
||||||
sourcesLayout.addView(new InlineItem(source.tiles.toString(), () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-7
@@ -1,6 +1,5 @@
|
|||||||
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
|
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
|
||||||
|
|
||||||
import android.app.ActionBar;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
@@ -8,17 +7,12 @@ import android.widget.Spinner;
|
|||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import com.google.android.material.appbar.MaterialToolbar;
|
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.data.helper.UserPrefsHelper;
|
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
|
||||||
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
public class FragmentLayoutContentSettings extends Fragment implements MainContentLayout {
|
public class FragmentLayoutContentSettings extends Fragment implements MainContentLayout {
|
||||||
@NonNull AppController app;
|
@NonNull AppController app;
|
||||||
|
|||||||
+77
-28
@@ -1,17 +1,20 @@
|
|||||||
package eu.konggdev.strikemaps.ui.fragment.popup;
|
package eu.konggdev.strikemaps.ui.fragment.popup;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import android.widget.TextView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.R;
|
import eu.konggdev.strikemaps.R;
|
||||||
import eu.konggdev.strikemaps.app.AppController;
|
import eu.konggdev.strikemaps.app.AppController;
|
||||||
import eu.konggdev.strikemaps.data.helper.FileHelper;
|
|
||||||
import eu.konggdev.strikemaps.map.MapComponent;
|
import eu.konggdev.strikemaps.map.MapComponent;
|
||||||
|
|
||||||
import eu.konggdev.strikemaps.map.style.MapStyle;
|
import eu.konggdev.strikemaps.map.style.MapStyle;
|
||||||
@@ -20,10 +23,7 @@ import eu.konggdev.strikemaps.ui.fragment.dialog.NewStyleBottomSheet;
|
|||||||
import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet;
|
import eu.konggdev.strikemaps.ui.fragment.dialog.StyleDetailsBottomSheet;
|
||||||
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
import eu.konggdev.strikemaps.ui.element.item.GenericItem;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Map;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
|
||||||
public class FragmentMapChangePopup extends Fragment implements Popup {
|
public class FragmentMapChangePopup extends Fragment implements Popup {
|
||||||
@NonNull AppController app;
|
@NonNull AppController app;
|
||||||
@@ -34,16 +34,65 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
|
|
||||||
private View view;
|
private View view;
|
||||||
|
|
||||||
// Action definitions
|
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() {
|
void newStyleFlow() {
|
||||||
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
|
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
|
||||||
}
|
}
|
||||||
|
|
||||||
void styleDetails(MapStyle style, String stylePath) {
|
void styleDetails(MapStyle entry, Integer id) {
|
||||||
new StyleDetailsBottomSheet(app, map, ui, this, style, stylePath).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
|
new StyleDetailsBottomSheet(app, map, ui, this, entry, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
|
||||||
}
|
}
|
||||||
//*//
|
|
||||||
|
|
||||||
public FragmentMapChangePopup(AppController app, Integer region) {
|
public FragmentMapChangePopup(AppController app, Integer region) {
|
||||||
super(R.layout.popup_map_change);
|
super(R.layout.popup_map_change);
|
||||||
@@ -63,30 +112,30 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadStyles() {
|
@SuppressLint("UseCompatTextViewDrawableApis")
|
||||||
List<String> stylePaths = new ArrayList<>();
|
void switchTab(int target) {
|
||||||
stylePaths.addAll(Arrays.asList(FileHelper.getAssetFiles("bundled/style", ".style.json", app)));
|
tabs.forEach((button, content) -> {
|
||||||
stylePaths.addAll(Arrays.asList(FileHelper.getUserFiles("style", ".style.json", app)));
|
view.findViewById(content).setVisibility(button == target ? View.VISIBLE : View.GONE);
|
||||||
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
|
int color = (button == target
|
||||||
stylesLayout.removeAllViews();
|
? Color.WHITE
|
||||||
for (String stylePath : stylePaths) {
|
: Color.parseColor("#888888"));
|
||||||
MapStyle parsedStyle = MapStyle.fromFile(stylePath, app);
|
((TextView) view.findViewById(button)).setTextColor(color);
|
||||||
stylesLayout.addView(GenericItem.fromStyle(parsedStyle, map,
|
((TextView) view.findViewById(button)).setCompoundDrawableTintList(ColorStateList.valueOf(color));
|
||||||
() -> map.setStyle(parsedStyle),
|
});
|
||||||
() -> this.styleDetails(parsedStyle, stylePath)).makeView(ui));
|
tabLoadActions.get(target).run();
|
||||||
}
|
|
||||||
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
|
|
||||||
stylesLayout.addView(new GenericItem("",
|
|
||||||
addNewIcon,
|
|
||||||
this::newStyleFlow).makeView(ui));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||||
//FIXME
|
//FIXME
|
||||||
|
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()));
|
setupButton(view, R.id.closeButton, click(() -> ui.getCurrentScreen().closePopup()));
|
||||||
setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup());
|
setupDragHandle(view, view, () -> ui.getCurrentScreen().closePopup());
|
||||||
this.view = view;
|
|
||||||
reloadStyles();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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,10 +1,5 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<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">
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
<path android:fillColor="@android:color/white" android:pathData="M400,680L400,280L600,480L400,680Z"/>
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24"
|
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z" />
|
|
||||||
</vector>
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<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.
|
Before Width: | Height: | Size: 5.2 KiB |
@@ -0,0 +1,10 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<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>
|
||||||
@@ -20,19 +20,6 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/fileName"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="27dp"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="\?.style.json"
|
|
||||||
android:textColor="#BDBDBD"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/styleName" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/styleType"
|
android:id="@+id/styleType"
|
||||||
android:layout_width="112dp"
|
android:layout_width="112dp"
|
||||||
@@ -44,7 +31,7 @@
|
|||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/fileName" />
|
app:layout_constraintTop_toBottomOf="@id/styleName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/builtInStyleAlert"
|
android:id="@+id/builtInStyleAlert"
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="200dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:cardCornerRadius="12dp"
|
|
||||||
app:cardElevation="12dp"
|
|
||||||
app:cardBackgroundColor="#000000">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="8dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/offlineMaps"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:text="Offline Maps"
|
|
||||||
android:textColor="#FFFFFF"
|
|
||||||
android:background="?android:attr/selectableItemBackground"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -25,17 +25,6 @@
|
|||||||
android:queryHint="Search..."
|
android:queryHint="Search..."
|
||||||
android:background="@android:color/transparent" />
|
android:background="@android:color/transparent" />
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/offlineMapsButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:contentDescription="Offline Maps"
|
|
||||||
android:src="@drawable/ic_offline"
|
|
||||||
android:padding="6dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
app:tint="#B0B0B0" />
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/hamburgerButton"
|
android:id="@+id/hamburgerButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?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>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="#000000"
|
android:background="#000000"
|
||||||
@@ -19,41 +20,183 @@
|
|||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/closeButton"
|
android:id="@+id/closeButton"
|
||||||
android:layout_width="30dp"
|
android:layout_width="30dp"
|
||||||
android:layout_height="30dp"
|
android:layout_height="27dp"
|
||||||
android:backgroundTint="#000000"
|
android:backgroundTint="#000000"
|
||||||
app:srcCompat="@android:drawable/ic_menu_close_clear_cancel"
|
app:srcCompat="@android:drawable/ic_menu_close_clear_cancel"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<TextView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/stylesLabel"
|
android:id="@+id/tabs"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="28dp"
|
||||||
android:text="Styles"
|
|
||||||
android:textColor="#FFFFFF"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_marginBottom="2dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/dragHandle"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<HorizontalScrollView
|
<TextView
|
||||||
android:id="@+id/styles"
|
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"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
app:layout_constraintTop_toBottomOf="@id/stylesLabel"
|
app:layout_constraintTop_toBottomOf="@id/tabs"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
app:layout_constraintBottom_toBottomOf="parent" >
|
||||||
|
|
||||||
|
<!-- Styles tab -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/stylesLayout"
|
android:id="@+id/styles"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal" />
|
android:orientation="vertical">
|
||||||
</HorizontalScrollView>
|
|
||||||
|
|
||||||
<!-- Sources selection to be implemented here -->
|
<HorizontalScrollView
|
||||||
|
android:id="@+id/stylesScrollView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="2dp"
|
||||||
|
android:fillViewport="false">
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user