Compare commits

..
8 Commits
Author SHA1 Message Date
konggd 004e9579c8 refactor: move getIcon away from FileTools
fix: use IconResolver in GenericItem
2026-09-15 14:07:35 +02:00
konggd f58cfdfabf refactor: define screens inside activity 2026-09-14 03:28:07 +02:00
konggd 580729dadb Fix activity location in AndroidManifest 2026-09-13 18:15:14 +02:00
konggd a3ae33812e Support android API 23 2026-09-13 16:43:29 +02:00
konggd 7a88939ea4 Migrate to modern OnBackPressedCallback inside MainActivity 2026-09-13 16:35:19 +02:00
konggd ecf3e4052a refactor: hold components inside activity 2026-09-13 16:25:39 +02:00
konggd abb6713798 Minor UI improvements 2026-09-13 16:25:39 +02:00
konggd 85734fa98d feat: add indicator to currently active style in map change ui 2026-09-13 16:25:36 +02:00
34 changed files with 540 additions and 520 deletions

No files matched your search

+3 -3
View File
@@ -9,8 +9,8 @@ android {
defaultConfig {
applicationId = "eu.konggdev.strikemaps"
minSdk = 24
targetSdk = 35
minSdk = 23
targetSdk = 36
versionCode = 1
versionName = "1.0"
@@ -53,7 +53,7 @@ dependencies {
implementation("com.github.mapsforge.vtm:vtm-android:0.27.0")
implementation("com.github.mapsforge.vtm:vtm-http:0.27.0")
implementation ("com.github.mapsforge.vtm:vtm-android:0.27.0@jar")
runtimeOnly ("com.github.mapsforge.vtm:vtm-android:0.27.0:natives-armeabi-v7a@jar")
runtimeOnly("com.github.mapsforge.vtm:vtm-android:0.27.0:natives-armeabi-v7a@jar")
runtimeOnly("com.github.mapsforge.vtm:vtm-android:0.27.0:natives-arm64-v8a@jar")
runtimeOnly("com.github.mapsforge.vtm:vtm-android:0.27.0:natives-x86@jar")
runtimeOnly("com.github.mapsforge.vtm:vtm-android:0.27.0:natives-x86_64@jar")
+2 -3
View File
@@ -9,10 +9,9 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.StrikeMaps"
tools:targetApi="31">
android:theme="@style/Theme.StrikeMaps">
<activity
android:name=".MainActivity"
android:name=".activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -3,7 +3,7 @@
"name": "Satellite",
"icon": "assets://satellite.bmp",
"sources": {
"satelite": {
"imagery": {
"type": "raster",
"schema" : "raster",
"tiles": [
@@ -17,9 +17,9 @@
},
"layers": [
{
"id": "satelite",
"id": "background",
"type": "raster",
"source": "satelite"
"source": "imagery"
}
],
"id": "satellite"
@@ -1,35 +0,0 @@
package eu.konggdev.strikemaps;
import eu.konggdev.strikemaps.app.AppController;
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
AppController app;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = new AppController(this);
setContentView(R.layout.view_main);
app.init();
}
public void logcat(String tag, String log) {
Log.i(tag, log);
}
public void logcat(String log) {
Log.i("LogcatGeneric", log);
}
@Override
public void onBackPressed() {
if (!app.getUi().back())
super.onBackPressed();
}
}
@@ -0,0 +1,90 @@
package eu.konggdev.strikemaps.activity;
import android.content.SharedPreferences;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import android.os.Bundle;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
import eu.konggdev.strikemaps.ui.element.region.content.MainContentRegion;
import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutControls;
import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutSearch;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentOfflineMaps;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentSettings;
import eu.konggdev.strikemaps.ui.screen.Screen;
import java.util.List;
import java.util.Map;
public class MainActivity extends ComponentHolderActivity {
private List<Component> components;
private UIComponent ui;
private Map<String, Screen> initScreens(UIComponent ui, MapComponent map, RegistryStorageComponent registry, SharedPreferences userPrefs) {
return Map.of(
//Main screen
"main", new Screen(
this,
//Main screen init regions definition
Map.of(
R.id.mainContentView, new MainContentRegion(map.toFragment(), R.id.mainContentView),
R.id.bottomUi, new UIRegion(new FragmentLayoutControls(this, ui, map, registry, userPrefs, R.id.bottomUi), R.id.bottomUi),
R.id.topUi, new UIRegion(new FragmentLayoutSearch(this, ui, R.id.topUi), R.id.topUi)
) //TODO: Probably stop referencing layout 3(!) times everytime
),
//Settings screen
"settings", new Screen(
this,
//Just the settings content fragment
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentSettings(this, ui, userPrefs), R.id.mainContentView)
)
),
//Offline maps screen
"offline", new Screen(
this,
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentOfflineMaps(this), R.id.mainContentView)
)
)
);
}
@NonNull
private List<Component> initComponents() {
SharedPreferences userPrefs = getSharedPreferences("user_prefs", MODE_PRIVATE);
RegistryStorageComponent registry = new RegistryStorageComponent(this, userPrefs);
MapComponent map = new MapComponent(this, registry, userPrefs);
UIComponent ui = new UIComponent(this, map, registry, userPrefs);
ui.defineScreens(initScreens(ui, map, registry, userPrefs));
this.ui = ui;
return List.of(
map,
ui,
registry
);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.components = initComponents();
ui.swapScreen("main");
setContentView(R.layout.view_main);
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (!ui.back()) {
setEnabled(false);
getOnBackPressedDispatcher().onBackPressed();
}
}
});
}
}
@@ -1,60 +0,0 @@
package eu.konggdev.strikemaps.app;
import android.content.SharedPreferences;
import androidx.appcompat.app.AppCompatActivity;
import eu.konggdev.strikemaps.MainActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
import static android.content.Context.MODE_PRIVATE;
public class AppController {
private final MainActivity appActivity;
private MapComponent map;
private UIComponent ui;
private RegistryStorageComponent registry;
public AppController(MainActivity appActivity) { this.appActivity = appActivity;}
public void logcat(String log) {
appActivity.logcat(log);
}
public UIComponent getUi() {
if (ui == null) init();
return ui;
}
public MapComponent getMap() {
if (map == null) init();
return map;
}
public RegistryStorageComponent getRegistry() {
if (registry == null) init();
return registry;
}
public SharedPreferences getPrefs() {
return getActivity().getSharedPreferences("user_prefs", MODE_PRIVATE);
}
public AppCompatActivity getActivity() { return appActivity; }
public void init() {
if (getActivity().getSupportActionBar() != null)
getActivity().getSupportActionBar().show();
if(registry == null) registry = new RegistryStorageComponent(this);
if(map == null) map = new MapComponent(this);
if(ui == null) {
ui = new UIComponent(this, map);
ui.swapScreen(DefinedScreen.MAIN); //Initial
}
}
}
@@ -0,0 +1,24 @@
package eu.konggdev.strikemaps.app;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import eu.konggdev.strikemaps.Component;
import java.util.ArrayList;
import java.util.List;
public abstract class ComponentHolderActivity extends AppCompatActivity {
private List<Component> components = new ArrayList<>();
public final <T extends Component> T getComponent(Class<T> type) {
return components.stream()
.filter(type::isInstance)
.findFirst()
.map(type::cast)
.orElse(null);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
@@ -1,101 +0,0 @@
package eu.konggdev.strikemaps.helper;
import android.content.res.AssetManager;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import eu.konggdev.strikemaps.app.AppController;
//FIXME: Ugly
public final class FileHelper {
public static Bitmap getIcon(String iconLocator, AppController app) {
switch (iconLocator.split("//")[0]) {
//TODO: https
case "assets:":
return BitmapFactory.decodeStream(FileHelper.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], app));
default:
app.logcat("Unimplemented icon locator space: " + iconLocator);
return null;
}
}
public static String loadStringFromAssetFile(String filePath, AppController app) {
try (InputStream is = app.getActivity().getAssets().open(filePath)) {
int size = is.available();
byte[] buffer = new byte[size];
is.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 {
try {
File userDirectory = new File(app.getActivity().getExternalFilesDir(null), path);
if (!userDirectory.exists() && !userDirectory.mkdirs()) {
app.logcat("Failed to create directory: " + userDirectory.getAbsolutePath());
return;
}
File file = new File(userDirectory, fileName);
try (FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
writer.write(content);
writer.flush();
}
} catch (IOException e) {
throw e;
}
}
public static String[] getAssetFiles(String path, String fileExt, AppController app) {
AssetManager assetManager = app.getActivity().getAssets();
try {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
String fullPath = (path == null || path.isEmpty()) ? "" : path;
String[] files = assetManager.list(fullPath);
if (files == null) return new String[0];
if (fileExt == null || fileExt.isEmpty())
return files;
List<String> filtered = new ArrayList<>();
for (String file : files) {
if (file.toLowerCase().endsWith(fileExt.toLowerCase())) {
filtered.add((fullPath.isEmpty() ? "" : fullPath + "/") + file);
}
}
return filtered.toArray(new String[0]);
} catch (IOException e) {
e.printStackTrace();
return new String[0];
}
}
public static InputStream openAssetStream(String path, AppController app) {
try {
return app.getActivity().getAssets().open(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -3,12 +3,13 @@ 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.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.source.tiles.SourceTiles;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import eu.konggdev.strikemaps.util.file.FileTools;
import org.apache.commons.codec.digest.DigestUtils;
import java.lang.reflect.Type;
@@ -32,11 +33,11 @@ public final class UserPrefsHelper {
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) {
public static Map<Integer, MapStyle> DEFAULT_STYLES(ComponentHolderActivity app) {
Map<Integer, MapStyle> styles = new HashMap<>();
String[] styleAssets = FileHelper.getAssetFiles("bundled/style", ".style.json", app);
String[] styleAssets = FileTools.getAssetFiles("bundled/style", ".style.json", app);
for (int i = 0; i < styleAssets.length; i++) {
String styleContents = FileHelper.loadStringFromAssetFile(styleAssets[i], app);
String styleContents = FileTools.loadStringFromAssetFile(styleAssets[i], app);
styles.put( i,
new MapStyle(
styleContents,
@@ -92,7 +93,7 @@ public final class UserPrefsHelper {
return prefs.edit().putBoolean(KEY_LAST_LOCATION_ENABLED, status).commit();
}
public static Map<Integer, MapStyle> styles(SharedPreferences prefs, AppController app) {
public static Map<Integer, MapStyle> styles(SharedPreferences prefs, ComponentHolderActivity app) {
String json = prefs.getString(KEY_STYLES, null);
if (json == null) return DEFAULT_STYLES(app);
Type type = new TypeToken<Map<Integer, MapStyle.StoredRepresentation>>() {}.getType();
@@ -2,45 +2,60 @@ package eu.konggdev.strikemaps.map;
import java.util.*;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.Toast;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreGLJSRenderer;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
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.geojson.Feature;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.renderer.implementation.MapLibreNativeRenderer;
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentMap;
public class MapComponent implements Component {
private final MapRenderer mapRenderer;
private final AppController app;
private final String TAG = "MapComponent";
private final MapRenderer mapRenderer;
private final ComponentHolderActivity activity;
private final RegistryStorageComponent registry;
private final SharedPreferences userPrefs;
//Current style
public Integer styleId;
public Map<Class<? extends MapOverlay>, MapOverlay> overlays = new HashMap<>();
public MapComponent(AppController ref) {
this.app = ref;
switch(UserPrefsHelper.mapRenderer(app.getPrefs())) {
public MapComponent(ComponentHolderActivity activity, RegistryStorageComponent registry, SharedPreferences userPrefs) {
this.activity = activity;
this.registry = registry;
this.userPrefs = userPrefs;
switch(UserPrefsHelper.mapRenderer(userPrefs)) {
case 0:
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
this.mapRenderer = new MapLibreGLJSRenderer(activity, this);
break;
case 1:
this.mapRenderer = new MapLibreNativeRenderer(app, this);
this.mapRenderer = new MapLibreNativeRenderer(activity, this);
break;
case 2:
this.mapRenderer = new VtmRenderer(app, this);
this.mapRenderer = new VtmRenderer(activity, this);
break;
default: //This shouldn't happen
Toast.makeText(app.getActivity(), "Invalid renderer value in preferences\nFalling back to MapLibre GL JS", Toast.LENGTH_SHORT).show();
this.mapRenderer = new MapLibreGLJSRenderer(app, this);
Toast.makeText(activity, "Invalid renderer value in preferences\nFalling back to MapLibre GL JS", Toast.LENGTH_SHORT).show();
Log.e(TAG, "Invalid renderer value in preferences\nFalling back to MapLibre GL JS");
this.mapRenderer = new MapLibreGLJSRenderer(activity, this);
break;
};
}
@@ -51,6 +66,14 @@ public class MapComponent implements Component {
public void setStyle(Integer id) {
styleId = id;
//Pass to renderer
StyleDocument document = registry.getStyle(styleId)
.effectiveDocument(registry);
if (document == null) {
Log.e(TAG, "Received null style from registry");
return;
}
mapRenderer.styleUpdate(document);
}
public void switchOverlay(MapOverlay overlay) {
@@ -87,10 +110,12 @@ public class MapComponent implements Component {
selectPoint(features.get(0));
break;
default:
app.getUi().alert(
AlertDialogFactory.pointSelector(app, features, selectedItem -> {
selectPoint(selectedItem);
}));
UIComponent ui = activity.getComponent(UIComponent.class);
if (ui != null) {
ui.alert(
AlertDialogFactory.pointSelector(activity, ui, features, this::selectPoint)
);
}
}
return true;
}
@@ -102,7 +127,7 @@ public class MapComponent implements Component {
public void onMapInit() {
setStyle(
UserPrefsHelper.startupMapStyle(app.getPrefs())
UserPrefsHelper.startupMapStyle(userPrefs)
);
}
}
@@ -1,10 +1,12 @@
package eu.konggdev.strikemaps.map.offline;
import okhttp3.*;
import java.io.IOException;
public final class OfflineTileResolver {
public byte[] resolve(String url) throws IOException {
//byte[] data = getOfflineTile();
public final class OfflineTileResolver {
public static byte[] resolve(String url) throws IOException {
//TODO
return null;
}
}
@@ -3,8 +3,9 @@ package eu.konggdev.strikemaps.map.overlay.implementation;
import android.location.Location;
import android.location.LocationListener;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
@@ -16,15 +17,13 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
public class LocationOverlay implements MapOverlay, LocationListener {
LocationDataProvider locationDataProvider;
AppController app;
MapComponent map;
public Location currentLocation = null;
public LocationOverlay(AppController app) {
this.app = app;
this.map = app.getMap();
this.locationDataProvider = new LocationDataProvider(app.getActivity(), this);
public LocationOverlay(MapComponent map, AppCompatActivity activity) {
this.map = map;
this.locationDataProvider = new LocationDataProvider(activity, this);
}
@Override
@@ -1,12 +1,12 @@
package eu.konggdev.strikemaps.map.overlay.implementation;
import com.fasterxml.jackson.databind.JsonNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
public class PointSelectionOverlay implements MapOverlay {
AppController app;
ComponentHolderActivity app;
MapComponent map;
@Override
public JsonNode makePatch() {
@@ -1,19 +1,20 @@
package eu.konggdev.strikemaps.map.renderer.implementation;
import android.annotation.SuppressLint;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.util.JsonPatcher;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.util.json.JsonPatcher;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
@@ -27,19 +28,20 @@ import java.util.List;
//Stub for now
public class MapLibreGLJSRenderer implements MapRenderer {
@NonNull AppController app;
private final static String TAG = "MapLibreGLJSRenderer";
private final AppCompatActivity activity;
@NonNull MapComponent controller;
private final MapComponent controller;
final WebView webView;
private JsonNode origin;
@SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
public MapLibreGLJSRenderer(AppController app, MapComponent controller) {
this.app = app;
public MapLibreGLJSRenderer(ComponentHolderActivity activity, MapComponent controller) {
this.activity = activity;
this.controller = controller;
webView = new WebView(app.getActivity());
webView = new WebView(activity);
webView.setLayoutParams(
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -86,7 +88,7 @@ public class MapLibreGLJSRenderer implements MapRenderer {
root.set("layers", layers);
this.origin = root;
} catch (Exception e) {
app.logcat("Failed to parse style: " + style.name);
Log.e(TAG, "Failed to parse style: " + style.name);
e.printStackTrace();
}
}
@@ -96,7 +98,7 @@ public class MapLibreGLJSRenderer implements MapRenderer {
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
} catch (Exception e) {
app.logcat("Failed to set style: " + style.name);
Log.e(TAG, "Failed to set style: " + style.name);
e.printStackTrace();
}
@@ -127,7 +129,7 @@ public class MapLibreGLJSRenderer implements MapRenderer {
webView.evaluateJavascript("map.setStyle(" + mapped + ", { diff: false });", null);
webView.evaluateJavascript("map.redraw()", null); //Force redraw to make the style change visible
} catch (Exception e) {
app.logcat("Failed to patch overlay: " + overlay.toString());
Log.e(TAG, "Failed to patch overlay: " + overlay.toString());
e.printStackTrace();
}
}
@@ -1,16 +1,17 @@
package eu.konggdev.strikemaps.map.renderer.implementation;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import eu.konggdev.strikemaps.app.util.JsonPatcher;
import eu.konggdev.strikemaps.map.offline.OfflineTileResolver;
import eu.konggdev.strikemaps.util.json.JsonPatcher;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
@@ -21,27 +22,26 @@ import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.maps.MapView;
import org.maplibre.android.maps.OnMapReadyCallback;
import org.maplibre.android.maps.Style;
import org.maplibre.android.module.http.HttpRequestUtil;
import org.maplibre.android.offline.OfflineManager;
import org.maplibre.geojson.Feature;
import java.util.List;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
@NonNull AppController app;
@NonNull MapComponent controller;
private final static String TAG = "MapLibreNativeRenderer";
private final AppCompatActivity activity;
private final MapComponent controller;
MapLibreMap map;
final MapView mapView;
private JsonNode origin;
public MapLibreNativeRenderer(AppController app, MapComponent controller) {
this.app = app;
public MapLibreNativeRenderer(ComponentHolderActivity activity, MapComponent controller) {
this.activity = activity;
this.controller = controller;
MapLibre.getInstance(app.getActivity());
this.mapView = new MapView(app.getActivity());
MapLibre.getInstance(activity);
this.mapView = new MapView(activity);
//TODO: Investigate if we might want to restore any savedInstanceState for MapLibre
mapView.onCreate(null);
//We need this to get the onMapReady callback
@@ -75,7 +75,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
root.set("layers", layers);
this.origin = root;
} catch (Exception e) {
app.logcat("Failed to parse style: " + style.name);
Log.e(TAG, "Failed to parse style: " + style.name);
e.printStackTrace();
}
}
@@ -83,7 +83,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
try {
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(origin)));
} catch (Exception e) {
app.logcat("Failed to set style: " + style.name);
Log.e(TAG, "Failed to set style: " + style.name);
e.printStackTrace();
}
@@ -115,7 +115,7 @@ public class MapLibreNativeRenderer implements MapRenderer, OnMapReadyCallback {
map.setStyle(new Style.Builder().fromJson(mapper.writeValueAsString(merged)));
} catch (Exception e) {
app.logcat("Failed to patch overlay: " + overlay.toString());
Log.e(TAG, "Failed to patch overlay: " + overlay.toString());
e.printStackTrace();
}
}
@@ -1,7 +1,7 @@
package eu.konggdev.strikemaps.map.renderer.implementation;
import android.view.View;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.MapOverlay;
import eu.konggdev.strikemaps.map.renderer.MapRenderer;
@@ -19,16 +19,16 @@ import java.util.List;
public class VtmRenderer implements MapRenderer {
AppController app;
ComponentHolderActivity activity;
MapComponent controller;
Map map;
final MapView mapView;
public VtmRenderer(AppController app, MapComponent controller) {
this.app = app;
public VtmRenderer(ComponentHolderActivity activity, MapComponent controller) {
this.activity = activity;
this.controller = controller;
this.mapView = new MapView(app.getActivity());
this.mapView = new MapView(activity);
this.map = mapView.map();
}
@@ -2,7 +2,6 @@ package eu.konggdev.strikemaps.map.style;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
@@ -4,7 +4,6 @@ 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.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.options.StyleOptions;
import eu.konggdev.strikemaps.map.style.source.StyleSource;
@@ -71,24 +70,6 @@ public class StyleDocument {
if (effectiveSource != null) source.current = effectiveSource;
}
for (JsonNode layer : result.layerDefinitions) {
JsonNode option = layer.get("option");
if (option == null)
continue;
if ("enable".equals(option.path("type").asText())) {
String id = layer.path("id").asText();
boolean enabled = options.getBoolean(
id,
option.path("default").asBoolean(true)
);
}
}
return result;
}
}
@@ -1,31 +1,32 @@
package eu.konggdev.strikemaps.storage;
import android.content.SharedPreferences;
import androidx.annotation.Nullable;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.source.MapSource;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import java.util.Map;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
import org.apache.commons.codec.digest.DigestUtils;
public class RegistryStorageComponent implements Component {
private AppController app;
private final ComponentHolderActivity activity;
private final SharedPreferences userPrefs;
private Map<Integer, MapStyle> styles;
private Map<Integer, MapSource> sources;
public RegistryStorageComponent(AppController app) {
this.app = app;
styles();
public RegistryStorageComponent(ComponentHolderActivity activity, SharedPreferences userPrefs) {
this.activity = activity;
this.userPrefs = userPrefs;
initAll();
}
private Map<Integer, MapSource> sources() {
if (sources == null) sources = UserPrefsHelper.sources(app.getPrefs());
if (sources == null) sources = UserPrefsHelper.sources(userPrefs);
return sources;
}
@@ -38,7 +39,7 @@ public class RegistryStorageComponent implements Component {
}
private Map<Integer, MapStyle> styles() {
if (styles == null) styles = UserPrefsHelper.styles(app.getPrefs(), app);
if (styles == null) styles = UserPrefsHelper.styles(userPrefs, activity);
return styles;
}
@@ -73,10 +74,14 @@ public class RegistryStorageComponent implements Component {
}
private void save() {
UserPrefsHelper.styles(app.getPrefs(), styles);
UserPrefsHelper.styles(userPrefs, styles);
}
public void checkForUpdates() {
//
}
private void initAll() {
styles(); sources();
}
}
@@ -1,13 +1,14 @@
package eu.konggdev.strikemaps.ui;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import eu.konggdev.strikemaps.Component;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.element.region.content.MainContentRegion;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutControls;
@@ -15,57 +16,44 @@ import eu.konggdev.strikemaps.ui.fragment.layout.FragmentLayoutSearch;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentOfflineMaps;
import eu.konggdev.strikemaps.ui.fragment.layout.content.main.FragmentLayoutContentSettings;
import eu.konggdev.strikemaps.ui.screen.Screen;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.function.Consumer;
public class UIComponent implements Component {
@NonNull AppController app;
MapComponent map;
private final String TAG = "UIComponent";
private final ComponentHolderActivity activity;
private final MapComponent map;
private final RegistryStorageComponent registry;
private final SharedPreferences userPrefs;
private final ArrayDeque<Screen> screenStack = new ArrayDeque<>();
public UIComponent(AppController app, MapComponent map) {
this.app = app;
private Map<String, Screen> screens;
public UIComponent(ComponentHolderActivity activity, MapComponent map, RegistryStorageComponent registry, SharedPreferences userPrefs) {
this.activity = activity;
this.map = map;
this.registry = registry;
this.userPrefs = userPrefs;
}
public Map<DefinedScreen, Screen> getScreens(MapComponent map) {
return Map.of(
//Main screen
DefinedScreen.MAIN, new Screen(
//App reference
app,
//Main screen init regions definition
Map.of(
R.id.mainContentView, new MainContentRegion(map.toFragment(), R.id.mainContentView),
R.id.bottomUi, new UIRegion(new FragmentLayoutControls(app, R.id.bottomUi), R.id.bottomUi),
R.id.topUi, new UIRegion(new FragmentLayoutSearch(app, R.id.topUi), R.id.topUi)
) //TODO: Probably stop referencing layout 3(!) times everytime
),
//Settings screen
DefinedScreen.SETTINGS, new Screen(
app,
//Just the settings content fragment
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentSettings(app), R.id.mainContentView)
)
),
//Offline maps screen
DefinedScreen.OFFLINE, new Screen(
app,
Map.of(
R.id.mainContentView, new MainContentRegion(new FragmentLayoutContentOfflineMaps(app), R.id.mainContentView)
)
)
);
public void defineScreens(Map<String, Screen> screens) {
this.screens = screens;
}
public void swapScreen(DefinedScreen screenKey) {
public void swapScreen(String screenKey) {
if (screens == null) return;
Screen newScreen = screens.get(screenKey);
if (newScreen == null) {
Log.e(TAG, "Invalid screen " + screenKey + " invoked");
return;
}
if (!screenStack.isEmpty()) getCurrentScreen().detachAll();
screenStack.add(getScreens(map).get(screenKey));
screenStack.add(newScreen);
getCurrentScreen().attachAll();
}
@@ -86,11 +74,7 @@ public class UIComponent implements Component {
dialog.show();
}
public <T> void alert(AlertDialog dialog, Consumer<T> callback) {
dialog.show();
}
public View inflateUi(int layout) {
return app.getActivity().getLayoutInflater().inflate(layout, null);
return activity.getLayoutInflater().inflate(layout, null);
}
}
@@ -5,75 +5,70 @@ import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.helper.FileHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.document.StyleDocument;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.util.icon.IconResolver;
public class GenericItem implements UIItem {
@NonNull public String name;
public String name;
public Bitmap image;
public Runnable onClick;
public Runnable onLongClick;
boolean hasImage;
public GenericItem(String refName) {
this.name = refName;
public GenericItem(String name) {
this.name = name;
hasImage = false;
}
public GenericItem(String refName, Runnable onClick) {
this.name = refName;
public GenericItem(String name, Runnable onClick) {
this.name = name;
this.onClick = onClick;
hasImage = false;
}
public GenericItem(String refName, Runnable onClick, Runnable onLongClick) {
this.name = refName;
public GenericItem(String name, Runnable onClick, Runnable onLongClick) {
this.name = name;
this.onClick = onClick;
this.onLongClick = onLongClick;
hasImage = false;
}
public GenericItem(String refName, Bitmap refImage) {
this.name = refName;
this.image = refImage;
public GenericItem(String name, Bitmap Image) {
this.name = name;
this.image = Image;
hasImage = true;
}
public GenericItem(String refName, Bitmap refImage, Runnable onClick) {
this.name = refName;
this.image = refImage;
public GenericItem(String name, Bitmap Image, Runnable onClick) {
this.name = name;
this.image = Image;
this.onClick = onClick;
hasImage = true;
}
public GenericItem(String refName, Bitmap refImage, Runnable onClick, Runnable onLongClick) {
this.name = refName;
this.image = refImage;
public GenericItem(String name, Bitmap Image, Runnable onClick, Runnable onLongClick) {
this.name = name;
this.image = Image;
this.onClick = onClick;
this.onLongClick = onLongClick;
hasImage = true;
}
public GenericItem(StyleDocument style, AppController app, Runnable onClick) {
if (style == null) {
this.name = "Unknown";
return;
}
public GenericItem(StyleDocument style, Runnable onClick, AppCompatActivity activity) {
if (style == null) return;
this.name = style.name;
this.image = style.icon != null ? FileHelper.getIcon(style.icon, app) : null;
this.image = style.icon != null ? IconResolver.getIcon(style.icon, activity) : null;
this.onClick = onClick;
}
public GenericItem(StyleDocument style, AppController app, Runnable onClick, Runnable onLongClick) {
if (style == null) {
this.name = "Unknown";
return;
}
public GenericItem(StyleDocument style, Runnable onClick, Runnable onLongClick, AppCompatActivity activity) {
if (style == null) return;
this.name = style.name;
this.image = style.icon != null ? FileHelper.getIcon(style.icon, app) : null;
this.image = style.icon != null ? IconResolver.getIcon(style.icon, activity) : null;
this.onClick = onClick;
this.onLongClick = onLongClick;
}
@@ -8,11 +8,13 @@ import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AppCompatActivity;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.util.JsonPatcher;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.util.json.JsonPatcher;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.map.style.management.StyleManagementMetadata;
@@ -27,10 +29,7 @@ import java.util.function.Consumer;
//FIXME: Cleaner architecture would be having a class for each AlertDialog type
public final class AlertDialogFactory {
public static AlertDialog styleManagementOptions(
AppController app,
StyleManagementMetadata metadata
) {
public static AlertDialog styleManagementOptions(AppCompatActivity activity, StyleManagementMetadata metadata) {
String[] options = {
"Update style",
"Update automatically"
@@ -41,7 +40,7 @@ public final class AlertDialogFactory {
metadata.autoUpdate
};
return new AlertDialog.Builder(app.getActivity())
return new AlertDialog.Builder(activity)
.setTitle("Built-in Style")
.setMultiChoiceItems(options, checked, (dialog, which, isChecked) -> {
if (which == 0) {
@@ -55,16 +54,16 @@ public final class AlertDialogFactory {
.create();
}
public static AlertDialog copyBuiltInStyle(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
public static AlertDialog copyBuiltInStyle(AppCompatActivity activity, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
//TODO: Use an UI element that's supposed to be vertical, instead of GenericItem
LinearLayout container = new LinearLayout(app.getActivity());
LinearLayout container = new LinearLayout(activity);
container.setOrientation(LinearLayout.VERTICAL);
ScrollView scrollView = new ScrollView(app.getActivity());
ScrollView scrollView = new ScrollView(activity);
scrollView.addView(container);
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
AlertDialog dialog = new AlertDialog.Builder(activity)
.setTitle("Copy from")
.setView(scrollView)
.setNegativeButton("Cancel", null)
@@ -72,19 +71,20 @@ public final class AlertDialogFactory {
return dialog;
}
public static AlertDialog createStyle(AppController app, String baseStyleContents, FragmentMapChangePopup mapChangePopup) {
final EditText nameInput = new EditText(app.getActivity());
public static AlertDialog createStyle(AppCompatActivity activity, RegistryStorageComponent registry, String baseStyleContents, FragmentMapChangePopup mapChangePopup) {
final EditText nameInput = new EditText(activity);
nameInput.setHint("Name");
LinearLayout container = new LinearLayout(app.getActivity());
LinearLayout container = new LinearLayout(activity);
container.setOrientation(LinearLayout.VERTICAL);
int padding = (int) (20 * app.getActivity().getResources().getDisplayMetrics().density);
int padding = (int) (20 * activity.getResources().getDisplayMetrics().density);
container.setPadding(padding, padding, padding, 0);
container.addView(nameInput);
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
AlertDialog dialog = new AlertDialog.Builder(activity)
.setTitle("Create")
.setView(container)
.setPositiveButton("Create", null)
@@ -95,12 +95,12 @@ public final class AlertDialogFactory {
Button createButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
createButton.setOnClickListener(v -> {
ObjectMapper mapper = new ObjectMapper();
ObjectMapper mactivityer = new ObjectMapper();
boolean nameEntryRequired = true;
String styleContentName = "";
if (baseStyleContents != null) {
try {
JsonNode root = mapper.readTree(baseStyleContents);
JsonNode root = mactivityer.readTree(baseStyleContents);
if (!root.path("name").asText().isEmpty()) {
nameEntryRequired = false; //We can take the name from the style
styleContentName = root.path("name").asText();
@@ -125,28 +125,28 @@ public final class AlertDialogFactory {
JsonNode root;
if(baseStyleContents != null) {
if(!baseStyleContents.isEmpty()) {
root = mapper.readTree(baseStyleContents);
root = mactivityer.readTree(baseStyleContents);
} else {
root = mapper.createObjectNode();
root = mactivityer.createObjectNode();
}
} else {
root = mapper.createObjectNode();
root = mactivityer.createObjectNode();
}
if (!root.path("name").asText().isEmpty()) {
ObjectNode node = mapper.createObjectNode();
ObjectNode node = mactivityer.createObjectNode();
node.put("name", name);
root = JsonPatcher.patch(root, node);
}
app.getRegistry().addStyle(new MapStyle(
mapper.writeValueAsString(root),
registry.addStyle(new MapStyle(
mactivityer.writeValueAsString(root),
new StyleOptions(),
null
));
dialog.dismiss();
} catch (Exception e) {
Toast.makeText(app.getActivity(), "Failed to create", Toast.LENGTH_SHORT).show();
Toast.makeText(activity, "Failed to create", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
@@ -157,20 +157,20 @@ public final class AlertDialogFactory {
return dialog;
}
public static AlertDialog pointSelector(AppController app, List<Feature> features, Consumer<Feature> callback) {
LinearLayout layout = new LinearLayout(app.getActivity());
public static AlertDialog pointSelector(ComponentHolderActivity activity, UIComponent ui, List<Feature> features, Consumer<Feature> callback) {
LinearLayout layout = new LinearLayout(activity);
layout.setOrientation(LinearLayout.VERTICAL);
ScrollView scrollView = new ScrollView(app.getActivity());
ScrollView scrollView = new ScrollView(activity);
scrollView.addView(layout);
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
AlertDialog dialog = new AlertDialog.Builder(activity)
.setView(scrollView)
.create();
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.parseColor("#000000")));
for (Feature feature : features) {
View itemView = PreviewItem.fromFeature(feature).makeView(app.getUi(), v -> {
View itemView = PreviewItem.fromFeature(feature).makeView(ui, v -> {
dialog.dismiss();
new Handler(Looper.getMainLooper())
.post(() -> callback.accept(feature));
@@ -181,30 +181,30 @@ public final class AlertDialogFactory {
return dialog;
}
public static AlertDialog searchSettings(AppController app) {
return new AlertDialog.Builder(app.getActivity())
public static AlertDialog searchSettings(AppCompatActivity activity) {
return new AlertDialog.Builder(activity)
.setTitle("Configure Search")
.setPositiveButton("OK", null)
.create();
}
public static AlertDialog restartDialog(AppController app) {
return new AlertDialog.Builder(app.getActivity())
public static AlertDialog restartDialog(AppCompatActivity activity) {
return new AlertDialog.Builder(activity)
.setTitle("Restart required")
.setMessage("Restart the app to apply changes.")
.setMessage("Restart the activity to activityly changes.")
.setCancelable(false)
.setNegativeButton("Cancel", (d, w) -> {
Toast.makeText(app.getActivity(),
"Changes will be applied on next restart",
Toast.makeText(activity,
"Changes will be activitylied on next restart",
Toast.LENGTH_SHORT).show();
d.dismiss();
})
.setPositiveButton("Restart", (d, w) -> {
Intent i = app.getActivity().getPackageManager()
.getLaunchIntentForPackage(app.getActivity().getPackageName());
Intent i = activity.getPackageManager()
.getLaunchIntentForPackage(activity.getPackageName());
if (i != null) {
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
app.getActivity().startActivity(i);
activity.startActivity(i);
}
Runtime.getRuntime().exit(0);
})
@@ -14,13 +14,15 @@ import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.card.MaterialCardView;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
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.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
@@ -30,11 +32,15 @@ import java.io.*;
public class NewStyleBottomSheet extends BottomSheetDialogFragment {
private final String styleBase = "{\"name\":\"None\"}";
@NonNull AppController app;
@NonNull MapComponent map;
@NonNull UIComponent ui;
@NonNull
FragmentMapChangePopup mapChangePopup;
private AppCompatActivity activity;
private MapComponent map;
private UIComponent ui;
private RegistryStorageComponent registry;
private FragmentMapChangePopup mapChangePopup;
private final ActivityResultLauncher<Intent> importLauncher =
registerForActivityResult(
@@ -72,15 +78,16 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
String content = contentBuilder.toString();
if (fileName != null && !fileName.endsWith(".style.json")) {
app.getUi().alert(
ui.alert(
AlertDialogFactory.createStyle(
app,
activity,
registry,
content,
mapChangePopup
)
);
} else {
app.getRegistry().addStyle(
registry.addStyle(
new MapStyle(
content,
new StyleOptions(),
@@ -108,10 +115,11 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
importLauncher.launch(intent);
}
public NewStyleBottomSheet(AppController app, MapComponent map, UIComponent ui, FragmentMapChangePopup mapChangePopup) {
this.app = app;
public NewStyleBottomSheet(AppCompatActivity activity, MapComponent map, UIComponent ui, RegistryStorageComponent registry, FragmentMapChangePopup mapChangePopup) {
this.activity = activity;
this.map = map;
this.ui = ui;
this.registry = registry;
this.mapChangePopup = mapChangePopup;
}
@@ -126,7 +134,7 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
MaterialCardView emptyBtn = view.findViewById(R.id.buttonCreateEmpty);
builtInBtn.setOnClickListener(v -> {
app.getUi().alert(AlertDialogFactory.copyBuiltInStyle(app, map, ui, mapChangePopup));
ui.alert(AlertDialogFactory.copyBuiltInStyle(activity, map, ui, mapChangePopup));
});
fileBtn.setOnClickListener(v -> {
@@ -134,7 +142,7 @@ public class NewStyleBottomSheet extends BottomSheetDialogFragment {
});
emptyBtn.setOnClickListener(v -> {
app.getUi().alert(AlertDialogFactory.createStyle(app, styleBase, mapChangePopup));
ui.alert(AlertDialogFactory.createStyle(activity, registry, styleBase, mapChangePopup));
});
return view;
@@ -14,10 +14,11 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.card.MaterialCardView;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
@@ -29,16 +30,15 @@ import java.io.IOException;
import java.io.OutputStream;
public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
@NonNull
AppController app;
@NonNull
MapComponent map;
@NonNull
UIComponent ui;
@NonNull
RegistryStorageComponent registry;
@NonNull
final FragmentMapChangePopup mapChangePopup;
private final AppCompatActivity activity;
private final MapComponent map;
private final UIComponent ui;
private final RegistryStorageComponent registry;
private final FragmentMapChangePopup mapChangePopup;
private final Integer styleId;
@@ -58,11 +58,11 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
exportLauncher.launch(intent);
}
public StyleDetailsBottomSheet(AppController app, FragmentMapChangePopup mapChangePopup, Integer styleId) {
this.app = app;
this.map = app.getMap();
this.ui = app.getUi();
this.registry = app.getRegistry();
public StyleDetailsBottomSheet(AppCompatActivity activity, MapComponent map, UIComponent ui, RegistryStorageComponent registry, FragmentMapChangePopup mapChangePopup, Integer styleId) {
this.activity = activity;
this.map = map;
this.ui = ui;
this.registry = registry;
this.mapChangePopup = mapChangePopup;
this.styleId = styleId;
}
@@ -118,7 +118,7 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
managedStyleIndicator.setVisibility(View.VISIBLE);
styleManagementInfo.setOnClickListener(v ->
ui.alert(
AlertDialogFactory.styleManagementOptions(app, style.managementMetadata)
AlertDialogFactory.styleManagementOptions(activity, style.managementMetadata)
)
);
@@ -127,7 +127,7 @@ public class StyleDetailsBottomSheet extends BottomSheetDialogFragment {
}
editButtonLayout.setOnClickListener(v -> Toast.makeText(requireContext(), "Editor not implemented yet\nWait for release", Toast.LENGTH_SHORT).show());
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(app, style.json, mapChangePopup)));
copyButtonLayout.setOnClickListener(v -> ui.alert(AlertDialogFactory.createStyle(activity, registry, style.json, mapChangePopup)));
exportButtonLayout.setOnClickListener(v -> showExportDialog());
deleteButtonLayout.setOnClickListener(v -> deleteStyle());
@@ -2,31 +2,42 @@ package eu.konggdev.strikemaps.ui.fragment.layout;
import android.Manifest;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import android.widget.TextView;
import android.widget.Toast;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.overlay.implementation.LocationOverlay;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.fragment.popup.FragmentMapChangePopup;
public class FragmentLayoutControls extends Fragment implements Layout {
AppController app;
private final AppCompatActivity activity;
private final UIComponent ui;
private final MapComponent map;
private final RegistryStorageComponent registry;
private final SharedPreferences userPrefs;
View rootView;
private final Integer region;
// Action definitions
//*//
public void notImplemented() { //Should never be called in release
Toast.makeText(requireContext(), "Not implemented yet\nWait for release", Toast.LENGTH_SHORT).show();
}
@@ -35,29 +46,32 @@ public class FragmentLayoutControls extends Fragment implements Layout {
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
app.getMap().switchOverlay(new LocationOverlay(app));
map.switchOverlay(new LocationOverlay(map, activity));
setupView();
}
}
public void zoomToLocation() {
if(!app.getMap().hasOverlay(LocationOverlay.class)) {
if(map.hasOverlay(LocationOverlay.class)) {
Toast.makeText(requireContext(), "Hold to enable location", Toast.LENGTH_SHORT).show();
return;
}
}
public void attributtionDialog() {
AlertDialog dialog = new AlertDialog.Builder(app.getActivity())
.setTitle(app.getActivity().getString(R.string.attribution_title))
.setMessage(app.getActivity().getString(R.string.shipped_attribution))
AlertDialog dialog = new AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.attribution_title))
.setMessage(activity.getString(R.string.shipped_attribution))
.setPositiveButton("OK", null).show();
}
//*//
}
public FragmentLayoutControls(AppController app, Integer region) {
public FragmentLayoutControls(AppCompatActivity activity, UIComponent ui, MapComponent map, RegistryStorageComponent registry, SharedPreferences userPrefs, Integer region) {
super(R.layout.fragment_controls);
this.app = app;
this.activity = activity;
this.ui = ui;
this.map = map;
this.registry = registry;
this.userPrefs = userPrefs;
this.region = region;
}
@@ -78,7 +92,7 @@ public class FragmentLayoutControls extends Fragment implements Layout {
/* Restores location enabled status from user prefs,
TODO: Should be moved out of UI code in the future */
if(UserPrefsHelper.persistLocationEnabled(app.getPrefs()) && UserPrefsHelper.lastLocationEnabled(app.getPrefs()) && !app.getMap().hasOverlay(LocationOverlay.class))
if(UserPrefsHelper.persistLocationEnabled(userPrefs) && UserPrefsHelper.lastLocationEnabled(userPrefs) && !map.hasOverlay(LocationOverlay.class))
toggleLocationService();
this.setupView();
@@ -86,7 +100,7 @@ public class FragmentLayoutControls extends Fragment implements Layout {
public void setupView() {
if (rootView == null) return;
setupButton(rootView, R.id.layersButton, click(() -> app.getUi().getCurrentScreen().open(new FragmentMapChangePopup(app, R.id.bottomUi))));
setupButton(rootView, R.id.layersButton, click(() -> ui.getCurrentScreen().open(new FragmentMapChangePopup(activity, ui, map, registry, R.id.bottomUi))));
setupButton(rootView, R.id.attributionButton, click(this::attributtionDialog));
setupButton(rootView, R.id.locationButton, click(this::zoomToLocation), longClick(this::toggleLocationService));
@@ -97,14 +111,14 @@ public class FragmentLayoutControls extends Fragment implements Layout {
setupButton(rootView, R.id.modeButton, click(this::notImplemented));
TextView locationServiceStatusIndicator = rootView.findViewById(R.id.locationServiceStatusIndicator);
if (app.getMap().hasOverlay(LocationOverlay.class)) {
if (map.hasOverlay(LocationOverlay.class)) {
locationServiceStatusIndicator.setBackgroundColor(Color.parseColor("#00FF00")); //green
} else {
locationServiceStatusIndicator.setBackgroundColor(Color.parseColor("#FB0303")); //red
}
if(UserPrefsHelper.persistLocationEnabled(app.getPrefs()))
UserPrefsHelper.lastLocationEnabled(app.getPrefs(), app.getMap().hasOverlay(LocationOverlay.class));
if(UserPrefsHelper.persistLocationEnabled(userPrefs))
UserPrefsHelper.lastLocationEnabled(userPrefs, map.hasOverlay(LocationOverlay.class));
}
@Override
@@ -8,20 +8,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
import eu.konggdev.strikemaps.ui.screen.definition.DefinedScreen;
public class FragmentLayoutSearch extends Fragment implements Layout {
AppController app;
private final AppCompatActivity activity;
private final UIComponent ui;
private final Integer region;
public FragmentLayoutSearch(AppController app, Integer region) {
public FragmentLayoutSearch(AppCompatActivity activity, UIComponent ui, Integer region) {
super(R.layout.fragment_search);
this.app = app;
this.activity = activity;
this.ui = ui;
this.region = region;
}
@@ -55,12 +56,12 @@ public class FragmentLayoutSearch extends Fragment implements Layout {
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setupButton(menuView, R.id.menuSearchSettings, click(() -> {
app.getUi().alert(AlertDialogFactory.searchSettings(app));
ui.alert(AlertDialogFactory.searchSettings(activity));
}));
setupButton(menuView, R.id.menuSettings, click(() -> {
popupWindow.dismiss();
app.getUi().swapScreen(DefinedScreen.SETTINGS);
ui.swapScreen("settings");
}));
menuView.findViewById(R.id.menuSearchSettings).setOnClickListener(v -> popupWindow.dismiss());
@@ -5,14 +5,14 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
public class FragmentLayoutContentOfflineMaps extends Fragment implements MainContentLayout {
@NonNull AppController app;
private final ComponentHolderActivity activity;
public FragmentLayoutContentOfflineMaps(AppController app) {
public FragmentLayoutContentOfflineMaps(ComponentHolderActivity activity) {
super(R.layout.fragment_offline_maps);
this.app = app;
this.activity = activity;
}
@Override
@@ -27,6 +27,6 @@ public class FragmentLayoutContentOfflineMaps extends Fragment implements MainCo
// LinearLayout sourcesLayout = view.findViewById(R.id.llDownloadContainer);
// for (MapSource source : sources)
// sourcesLayout.addView(new InlineItem(source.name, () -> Toast.makeText(app.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
// sourcesLayout.addView(new InlineItem(source.name, () -> Toast.makeText(activity.getActivity(), "Work in progress", Toast.LENGTH_SHORT).show()).makeView(app.getUi()));
}
}
@@ -1,5 +1,6 @@
package eu.konggdev.strikemaps.ui.fragment.layout.content.main;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
@@ -7,21 +8,26 @@ import android.widget.Spinner;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.helper.UserPrefsHelper;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.factory.AlertDialogFactory;
public class FragmentLayoutContentSettings extends Fragment implements MainContentLayout {
@NonNull AppController app;
@NonNull UIComponent ui;
public FragmentLayoutContentSettings(AppController app) {
private final AppCompatActivity activity;
private final UIComponent ui;
private final SharedPreferences userPrefs;
public FragmentLayoutContentSettings(AppCompatActivity activity, UIComponent ui, SharedPreferences userPrefs) {
super(R.layout.fragment_settings);
this.app = app;
this.ui = app.getUi();
this.activity = activity;
this.ui = ui;
this.userPrefs = userPrefs;
}
@Override
@@ -39,7 +45,7 @@ public class FragmentLayoutContentSettings extends Fragment implements MainConte
);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mapRendererSelector.setAdapter(adapter);
mapRendererSelector.setSelection(UserPrefsHelper.mapRenderer(app.getPrefs()));
mapRendererSelector.setSelection(UserPrefsHelper.mapRenderer(userPrefs));
final boolean[] ignoreFirst = {true};
mapRendererSelector.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@@ -50,8 +56,8 @@ public class FragmentLayoutContentSettings extends Fragment implements MainConte
return;
}
UserPrefsHelper.mapRenderer(app.getPrefs(), position);
ui.alert(AlertDialogFactory.restartDialog(app));
UserPrefsHelper.mapRenderer(userPrefs, position);
ui.alert(AlertDialogFactory.restartDialog(activity));
}
@Override
@@ -10,14 +10,14 @@ import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import android.widget.LinearLayout;
import eu.konggdev.strikemaps.R;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.map.MapComponent;
import eu.konggdev.strikemaps.map.style.MapStyle;
import eu.konggdev.strikemaps.storage.RegistryStorageComponent;
import eu.konggdev.strikemaps.ui.UIComponent;
import eu.konggdev.strikemaps.ui.fragment.dialog.NewStyleBottomSheet;
@@ -28,10 +28,10 @@ import java.util.Map;
import java.util.Objects;
public class FragmentMapChangePopup extends Fragment implements Popup {
@NonNull AppController app;
@NonNull MapComponent map;
@NonNull UIComponent ui;
@NonNull RegistryStorageComponent registry;
private final AppCompatActivity activity;
private final MapComponent map;
private final UIComponent ui;
private final RegistryStorageComponent registry;
private final Integer region;
@@ -52,17 +52,16 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
public void reloadStyles() {
LinearLayout stylesLayout = view.findViewById(R.id.stylesLayout);
stylesLayout.removeAllViews();
app.getRegistry().getStyles().forEach((id, style) -> {
registry.getStyles().forEach((id, style) -> {
View item = new GenericItem(
style.document,
app,
() -> this.setStyle(id),
() -> this.styleDetails(id)).makeView(ui);
() -> this.styleDetails(id), activity).makeView(ui);
if (Objects.equals(map.styleId, id)) item.findViewById(R.id.indicator)
.setVisibility(View.VISIBLE);
stylesLayout.addView(item);
});
Bitmap addNewIcon = BitmapFactory.decodeResource(app.getActivity().getResources(), android.R.drawable.ic_menu_add);
Bitmap addNewIcon = BitmapFactory.decodeResource(activity.getResources(), android.R.drawable.ic_menu_add);
stylesLayout.addView(new GenericItem("",
addNewIcon,
this::newStyleFlow).makeView(ui));
@@ -95,11 +94,11 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
}
void newStyleFlow() {
new NewStyleBottomSheet(app, map, ui, this).show(app.getActivity().getSupportFragmentManager(), "NewStyleBottomSheet");
new NewStyleBottomSheet(activity, map, ui, registry, this).show(activity.getSupportFragmentManager(), "NewStyleBottomSheet");
}
void styleDetails(Integer id) {
new StyleDetailsBottomSheet(app, this, id).show(app.getActivity().getSupportFragmentManager(), "StyleDetailsBottomSheet");
new StyleDetailsBottomSheet(activity, map, ui, registry, this, id).show(activity.getSupportFragmentManager(), "StyleDetailsBottomSheet");
}
void setStyle(Integer id) {
@@ -107,12 +106,12 @@ public class FragmentMapChangePopup extends Fragment implements Popup {
reloadStyles();
}
public FragmentMapChangePopup(AppController app, Integer region) {
public FragmentMapChangePopup(AppCompatActivity activity, UIComponent ui, MapComponent map, RegistryStorageComponent registry, Integer region) {
super(R.layout.popup_map_change);
this.app = app;
this.map = app.getMap();
this.ui = app.getUi();
this.registry = app.getRegistry();
this.activity = activity;
this.ui = ui;
this.map = map;
this.registry = registry;
this.region = region;
}
@@ -1,23 +1,22 @@
package eu.konggdev.strikemaps.ui.screen;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.Toolbar;
import java.util.Map;
import eu.konggdev.strikemaps.app.AppController;
import eu.konggdev.strikemaps.app.ComponentHolderActivity;
import eu.konggdev.strikemaps.ui.fragment.ContainerFragment;
import eu.konggdev.strikemaps.ui.fragment.FragmentEmptyPlaceholder;
import eu.konggdev.strikemaps.ui.fragment.popup.Popup;
import eu.konggdev.strikemaps.ui.element.region.UIRegion;
public class Screen {
@NonNull AppController app;
Toolbar toolbar;
public Screen(AppController app, Map<Integer, UIRegion> regions) {
this.app = app;
private final ComponentHolderActivity activity;
public Screen(ComponentHolderActivity activity, Map<Integer, UIRegion> regions) {
this.activity = activity;
this.uiRegions = regions;
}
Map<Integer, UIRegion> uiRegions;
@@ -55,7 +54,7 @@ public class Screen {
}
public void fragmentTransaction(int layoutId, Fragment fragment) {
app.getActivity().getSupportFragmentManager()
activity.getSupportFragmentManager()
.beginTransaction()
.replace(layoutId, fragment)
.commit();
@@ -1,6 +0,0 @@
package eu.konggdev.strikemaps.ui.screen.definition;
public enum DefinedScreen {
MAIN,
SETTINGS,
OFFLINE
}
@@ -0,0 +1,67 @@
package eu.konggdev.strikemaps.util.file;
import android.content.res.AssetManager;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
//FIXME: Ugly
public final class FileTools {
private static final String TAG = "FileTools";
public static String loadStringFromAssetFile(String filePath, AppCompatActivity activity) {
try (InputStream is = activity.getAssets().open(filePath)) {
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
return new String(buffer, StandardCharsets.UTF_8);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
public static String[] getAssetFiles(String path, String fileExt, AppCompatActivity activity) {
AssetManager assetManager = activity.getAssets();
try {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
String fullPath = (path == null || path.isEmpty()) ? "" : path;
String[] files = assetManager.list(fullPath);
if (files == null) return new String[0];
if (fileExt == null || fileExt.isEmpty())
return files;
List<String> filtered = new ArrayList<>();
for (String file : files) {
if (file.toLowerCase().endsWith(fileExt.toLowerCase())) {
filtered.add((fullPath.isEmpty() ? "" : fullPath + "/") + file);
}
}
return filtered.toArray(new String[0]);
} catch (IOException e) {
e.printStackTrace();
return new String[0];
}
}
public static InputStream openAssetStream(String path, AppCompatActivity activity) {
try {
return activity.getAssets().open(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,22 @@
package eu.konggdev.strikemaps.util.icon;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import eu.konggdev.strikemaps.util.file.FileTools;
public final class IconResolver {
private static final String TAG = "IconResolver";
public static Bitmap getIcon(String iconLocator, AppCompatActivity activity) {
switch (iconLocator.split("//")[0]) {
//TODO: https
case "assets:":
return BitmapFactory.decodeStream(FileTools.openAssetStream("bundled/icon/" + iconLocator.split("//")[1], activity));
default:
Log.e(TAG, "Unimplemented icon locator space: " + iconLocator);
return null;
}
}
}
@@ -1,4 +1,4 @@
package eu.konggdev.strikemaps.app.util;
package eu.konggdev.strikemaps.util.json;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;