From 50748c023e36440538b6a065a72097bd1de45537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20B=C3=BCnz?= Date: Sun, 28 Dec 2025 11:19:57 +0100 Subject: [PATCH 1/2] WIP --- .../criticalmaps/model/ChatModel.java | 80 ------------------- .../criticalmaps/model/ChatModel.kt | 65 +++++++++++++++ .../model/OtherUsersLocationModel.java | 44 ---------- .../model/OtherUsersLocationModel.kt | 34 ++++++++ .../criticalmaps/model/OwnLocationModel.java | 46 ----------- .../criticalmaps/model/OwnLocationModel.kt | 41 ++++++++++ .../criticalmaps/model/PermissionRequest.java | 62 -------------- .../criticalmaps/model/PermissionRequest.kt | 18 +++++ .../criticalmaps/model/StorageLocation.java | 38 --------- .../criticalmaps/model/StorageLocation.kt | 25 ++++++ .../criticalmaps/model/UserModel.java | 39 --------- .../criticalmaps/model/UserModel.kt | 30 +++++++ .../model/chat/ReceivedChatMessage.java | 23 ------ .../model/chat/ReceivedChatMessage.kt | 8 ++ .../criticalmaps/model/ChatModelTest.java | 78 ------------------ .../criticalmaps/model/ChatModelTest.kt | 49 ++++++++++++ .../model/OwnLocationModelTest.java | 44 ---------- .../model/OwnLocationModelTest.kt | 46 +++++++++++ 18 files changed, 316 insertions(+), 454 deletions(-) delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.kt delete mode 100644 app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.java create mode 100644 app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.kt delete mode 100644 app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.java create mode 100644 app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.kt diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java deleted file mode 100644 index f8630a6f..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java +++ /dev/null @@ -1,80 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import androidx.annotation.NonNull; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import de.stephanlindauer.criticalmaps.model.chat.ReceivedChatMessage; -import de.stephanlindauer.criticalmaps.utils.AeSimpleSHA1; -import timber.log.Timber; - - -@Singleton -public class ChatModel { - - private final UserModel userModel; - private List receivedChatMessages = new ArrayList<>(); - - public static final int MESSAGE_MAX_LENGTH = 255; - - @Inject - public ChatModel(UserModel userModel) { - this.userModel = userModel; - } - - @NonNull - public List getReceivedChatMessages() { - return this.receivedChatMessages; - } - - public void setFromJson(JSONArray jsonArray) throws JSONException, - UnsupportedEncodingException { - receivedChatMessages = new ArrayList<>(jsonArray.length()); - - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject jsonObject = jsonArray.getJSONObject(i); - - String device = URLDecoder.decode(jsonObject.getString("device"), "UTF-8"); - String identifier = URLDecoder.decode(jsonObject.getString("identifier"), "UTF-8"); - String message = URLDecoder.decode(jsonObject.getString("message"), "UTF-8"); - Date timestamp = new Date(Long.parseLong(jsonObject.getString("timestamp")) * 1000); - - receivedChatMessages.add(new ReceivedChatMessage(message, timestamp)); - } - - receivedChatMessages.sort(Comparator.comparing(ReceivedChatMessage::getTimestamp)); - } - - public JSONObject createNewOutgoingMessage(String message) { - JSONObject messageObject = new JSONObject(); - try { - messageObject.put("text", urlEncodeMessage(message)); - messageObject.put("identifier", AeSimpleSHA1.SHA1(message + Math.random())); - messageObject.put("device", userModel.getChangingDeviceToken()); - } catch (JSONException e) { - Timber.d(e); - } - return messageObject; - } - - private String urlEncodeMessage(String messageToEncode) { - try { - return URLEncoder.encode(messageToEncode, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return ""; - } - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.kt new file mode 100644 index 00000000..92a76900 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.kt @@ -0,0 +1,65 @@ +package de.stephanlindauer.criticalmaps.model + +import de.stephanlindauer.criticalmaps.model.chat.ReceivedChatMessage +import de.stephanlindauer.criticalmaps.utils.AeSimpleSHA1 +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import timber.log.Timber +import java.io.UnsupportedEncodingException +import java.net.URLDecoder +import java.net.URLEncoder +import java.util.Date +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ChatModel @Inject constructor( + private val userModel: UserModel +) { + private var receivedChatMessages = mutableListOf() + + fun getReceivedChatMessages(): List = receivedChatMessages + + @Throws(JSONException::class, UnsupportedEncodingException::class) + fun setFromJson(jsonArray: JSONArray) { + receivedChatMessages = ArrayList(jsonArray.length()) + + for (i in 0 until jsonArray.length()) { + val jsonObject = jsonArray.getJSONObject(i) + + val device = URLDecoder.decode(jsonObject.getString("device"), "UTF-8") + val identifier = URLDecoder.decode(jsonObject.getString("identifier"), "UTF-8") + val message = URLDecoder.decode(jsonObject.getString("message"), "UTF-8") + val timestamp = Date(jsonObject.getString("timestamp").toLong() * 1000) + + receivedChatMessages.add(ReceivedChatMessage(message, timestamp)) + } + + receivedChatMessages.sortBy { it.timestamp } + } + + fun createNewOutgoingMessage(message: String): JSONObject { + val messageObject = JSONObject() + try { + messageObject.put("text", urlEncodeMessage(message)) + messageObject.put("identifier", AeSimpleSHA1.SHA1(message + Math.random()) ?: "") + messageObject.put("device", userModel.changingDeviceToken) + } catch (e: JSONException) { + Timber.d(e) + } + return messageObject + } + + private fun urlEncodeMessage(messageToEncode: String): String { + return try { + URLEncoder.encode(messageToEncode, "UTF-8") + } catch (e: UnsupportedEncodingException) { + "" + } + } + + companion object { + const val MESSAGE_MAX_LENGTH = 255 + } +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java deleted file mode 100644 index f1e84546..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.maplibre.android.geometry.LatLng; - -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class OtherUsersLocationModel { - - private final Map otherUsersLocations = new HashMap<>(); - - private final UserModel userModel; - - @Inject - public OtherUsersLocationModel(UserModel userModel) { - this.userModel = userModel; - } - - - public void setFromJson(JSONArray jsonArray) throws JSONException { - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject locationObject = jsonArray.getJSONObject(i); - if (locationObject.getString("device").equals(userModel.getChangingDeviceToken())) { - continue; // Ignore own location - } - String deviceId = locationObject.getString("device"); - int latitudeE6 = Integer.parseInt(locationObject.getString("latitude")); - int longitudeE6 = Integer.parseInt(locationObject.getString("longitude")); - - otherUsersLocations.put(deviceId, new LatLng(latitudeE6 / 1E6, longitudeE6 / 1E6)); - } - } - - public Map getOtherUsersLocations() { - return otherUsersLocations; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.kt new file mode 100644 index 00000000..8e747600 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.kt @@ -0,0 +1,34 @@ +package de.stephanlindauer.criticalmaps.model + +import org.json.JSONArray +import org.json.JSONException +import org.maplibre.android.geometry.LatLng +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class OtherUsersLocationModel @Inject constructor( + private val userModel: UserModel +) { + private val otherUsersLocations = HashMap() + + @Throws(JSONException::class) + fun setFromJson(jsonArray: JSONArray) { + for (i in 0 until jsonArray.length()) { + val locationObject = jsonArray.getJSONObject(i) + val deviceId = locationObject.getString("device") + + // Ignore own location + if (deviceId == userModel.changingDeviceToken) { + continue + } + + val latitudeE6 = locationObject.getString("latitude").toInt() + val longitudeE6 = locationObject.getString("longitude").toInt() + + otherUsersLocations[deviceId] = LatLng(latitudeE6 / 1E6, longitudeE6 / 1E6) + } + } + + fun getOtherUsersLocations(): Map = otherUsersLocations +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.java deleted file mode 100644 index 053c659a..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.java +++ /dev/null @@ -1,46 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import androidx.annotation.NonNull; - -import org.json.JSONException; -import org.json.JSONObject; -import org.maplibre.android.geometry.LatLng; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import timber.log.Timber; - -@Singleton -public class OwnLocationModel { - - private static final float ACCURACY_PRECISE_THRESHOLD = 50.0f; //meters - - public LatLng ownLocation; - private boolean isLocationPrecise; - - @Inject - public OwnLocationModel() { - } - - public void setLocation(@NonNull LatLng location, float accuracy) { - ownLocation = location; - isLocationPrecise = accuracy < ACCURACY_PRECISE_THRESHOLD; - } - - public boolean hasPreciseLocation() { - return ownLocation != null && isLocationPrecise; - } - - @NonNull - public JSONObject getLocationJson() { - JSONObject locationObject = new JSONObject(); - try { - locationObject.put("longitude", Integer.toString((int) (ownLocation.getLongitude() * 1000000.0D))); - locationObject.put("latitude", Integer.toString((int) (ownLocation.getLatitude() * 1000000.0D))); - } catch (JSONException e) { - Timber.e(e); - } - return locationObject; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.kt new file mode 100644 index 00000000..92a6dbc4 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OwnLocationModel.kt @@ -0,0 +1,41 @@ +package de.stephanlindauer.criticalmaps.model + +import org.json.JSONException +import org.json.JSONObject +import org.maplibre.android.geometry.LatLng +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class OwnLocationModel @Inject constructor() { + + @JvmField + var ownLocation: LatLng? = null + private var isLocationPrecise = false + + fun setLocation(location: LatLng, accuracy: Float) { + ownLocation = location + isLocationPrecise = accuracy < ACCURACY_PRECISE_THRESHOLD + } + + fun hasPreciseLocation(): Boolean = ownLocation != null && isLocationPrecise + + fun getLocationJson(): JSONObject { + val location = ownLocation + require(location != null) { "Location must be set before calling getLocationJson()" } + + val locationObject = JSONObject() + try { + locationObject.put("longitude", (location.longitude * 1_000_000.0).toInt().toString()) + locationObject.put("latitude", (location.latitude * 1_000_000.0).toInt().toString()) + } catch (e: JSONException) { + Timber.e(e) + } + return locationObject + } + + companion object { + private const val ACCURACY_PRECISE_THRESHOLD = 50.0f // meters + } +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.java deleted file mode 100644 index bc049c4c..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.java +++ /dev/null @@ -1,62 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class PermissionRequest { - private final String[] permissions; - private final String rationale; - private final Runnable onGrantedCallback; - private final Runnable onDeniedCallback; - private final Runnable onPermanentlyDeniedCallback; - private final int requestCode; - - public PermissionRequest(@NonNull String[] permissions, - @NonNull String rationale, - @Nullable Runnable onGrantedCallback, - @Nullable Runnable onDeniedCallback, - @Nullable Runnable onPermanentlyDeniedCallback) { - this.permissions = permissions; - this.rationale = rationale; - this.onGrantedCallback = - onGrantedCallback != null ? onGrantedCallback : () -> { - }; - this.onDeniedCallback = - onDeniedCallback != null ? onDeniedCallback : () -> { - }; - this.onPermanentlyDeniedCallback = - onPermanentlyDeniedCallback != null ? onPermanentlyDeniedCallback : () -> { - }; - // Can only use lower 16 bits for requestCode --> short - this.requestCode = (short) Math.round(Math.random() * Short.MAX_VALUE); - } - - @NonNull - public String[] getPermissions() { - return permissions; - } - - public int getRequestCode() { - return requestCode; - } - - @NonNull - public Runnable getOnGrantedCallback() { - return onGrantedCallback; - } - - @NonNull - public Runnable getOnDeniedCallback() { - return onDeniedCallback; - } - - @NonNull - public Runnable getOnPermanentlyDeniedCallback() { - return onPermanentlyDeniedCallback; - } - - @NonNull - public String getRationale() { - return rationale; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.kt new file mode 100644 index 00000000..67cd788f --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/PermissionRequest.kt @@ -0,0 +1,18 @@ +package de.stephanlindauer.criticalmaps.model + +import kotlin.random.Random + +class PermissionRequest( + val permissions: Array, + val rationale: String, + onGrantedCallback: Runnable? = null, + onDeniedCallback: Runnable? = null, + onPermanentlyDeniedCallback: Runnable? = null +) { + val onGrantedCallback: Runnable = onGrantedCallback ?: Runnable { } + val onDeniedCallback: Runnable = onDeniedCallback ?: Runnable { } + val onPermanentlyDeniedCallback: Runnable = onPermanentlyDeniedCallback ?: Runnable { } + + // Can only use lower 16 bits for requestCode --> short + val requestCode: Int = Random.nextInt(Short.MAX_VALUE.toInt()) +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.java deleted file mode 100644 index 3f078dc4..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import android.os.StatFs; -import java.io.File; - - -public class StorageLocation { - private final File dbFile; - - public StorageLocation(File path) { - if (!path.exists()) { - throw new IllegalArgumentException("Path does not exist or is read only: " + path); - } - - dbFile = new File(path + File.separator + "mbgl-offline.db"); - } - - public long getFreeSpaceBytes() { - return new StatFs(dbFile.getAbsolutePath()).getAvailableBytes(); - } - - public long getTotalSizeBytes() { - return new StatFs(dbFile.getAbsolutePath()).getTotalBytes(); - } - - public long getUsedSpace() { - return getTotalSizeBytes() - getFreeSpaceBytes(); - } - - public long getCacheSize() { - long cacheSize = 0; - if (dbFile.exists()) { - cacheSize = dbFile.length(); - } - - return cacheSize; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.kt new file mode 100644 index 00000000..6170a399 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/StorageLocation.kt @@ -0,0 +1,25 @@ +package de.stephanlindauer.criticalmaps.model + +import android.os.StatFs +import java.io.File + +class StorageLocation(path: File) { + private val dbFile: File + + init { + require(path.exists()) { "Path does not exist or is read only: $path" } + dbFile = File(path, "mbgl-offline.db") + } + + val freeSpaceBytes: Long + get() = StatFs(dbFile.absolutePath).availableBytes + + val totalSizeBytes: Long + get() = StatFs(dbFile.absolutePath).totalBytes + + val usedSpace: Long + get() = totalSizeBytes - freeSpaceBytes + + val cacheSize: Long + get() = if (dbFile.exists()) dbFile.length() else 0 +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.java deleted file mode 100644 index c665a366..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import android.annotation.SuppressLint; -import android.provider.Settings; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Locale; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import de.stephanlindauer.criticalmaps.App; -import de.stephanlindauer.criticalmaps.utils.AeSimpleSHA1; - -import static android.provider.Settings.Secure.getString; - -@Singleton -public class UserModel { - - private final String changingDeviceToken; - - @Inject - public UserModel(App app) { - - @SuppressLint("HardwareIds") final String androidId = getString(app.getContentResolver(), Settings.Secure.ANDROID_ID); - - final GregorianCalendar gregorianCalendar = new GregorianCalendar(); - gregorianCalendar.add(Calendar.HOUR, 6); - final String dateString = new SimpleDateFormat("yyyy-MM-dd", Locale.US).format(gregorianCalendar.getTime()); - - changingDeviceToken = AeSimpleSHA1.SHA1(androidId + dateString); - } - - public String getChangingDeviceToken() { - return changingDeviceToken; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.kt new file mode 100644 index 00000000..58491944 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/UserModel.kt @@ -0,0 +1,30 @@ +package de.stephanlindauer.criticalmaps.model + +import android.annotation.SuppressLint +import android.provider.Settings +import de.stephanlindauer.criticalmaps.App +import de.stephanlindauer.criticalmaps.utils.AeSimpleSHA1 +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.GregorianCalendar +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class UserModel @Inject constructor(app: App) { + + @SuppressLint("HardwareIds") + val changingDeviceToken: String + + init { + val androidId = Settings.Secure.getString(app.contentResolver, Settings.Secure.ANDROID_ID) + + val gregorianCalendar = GregorianCalendar().apply { + add(Calendar.HOUR, 6) + } + val dateString = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(gregorianCalendar.time) + + changingDeviceToken = AeSimpleSHA1.SHA1(androidId + dateString) ?: "" + } +} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.java deleted file mode 100644 index 7f039934..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.stephanlindauer.criticalmaps.model.chat; - -import java.util.Date; - - -public class ReceivedChatMessage { - - private final Date timestamp; - private final String message; - - public ReceivedChatMessage(String message, Date timestamp) { - this.message = message; - this.timestamp = timestamp; - } - - public Date getTimestamp() { - return timestamp; - } - - public String getMessage() { - return message; - } -} diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.kt new file mode 100644 index 00000000..3f33b227 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/chat/ReceivedChatMessage.kt @@ -0,0 +1,8 @@ +package de.stephanlindauer.criticalmaps.model.chat + +import java.util.Date + +data class ReceivedChatMessage( + val message: String, + val timestamp: Date +) diff --git a/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.java b/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.java deleted file mode 100644 index 07d63343..00000000 --- a/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import org.json.JSONArray; -import org.json.JSONException; -import org.junit.Test; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; - -import de.stephanlindauer.criticalmaps.model.chat.ReceivedChatMessage; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class ChatModelTest { - - @Test - public void setFromJson_testThatChatmessagesAreSorted() throws IOException, URISyntaxException, - JSONException { - final String json = readToString(new File(getClass().getClassLoader() - .getResource("server_response_chatmessages.json").toURI())); - final JSONArray response = new JSONArray(json); - - UserModel userModel = mock(UserModel.class); - when(userModel.getChangingDeviceToken()).thenReturn("t0k3n"); - - final ChatModel tested = new ChatModel(userModel); - - tested.setFromJson(response); - final ReceivedChatMessage message0 = tested.getReceivedChatMessages().get(0); - final ReceivedChatMessage message1 = tested.getReceivedChatMessages().get(1); - - assertThat(message0.getTimestamp()).isLessThan(message1.getTimestamp()); - } - - - @Test - public void setFromJson_existingMessagesAreReplaced() throws URISyntaxException, IOException, - JSONException { - final String json = readToString(new File(getClass().getClassLoader() - .getResource("server_response_chatmessages.json").toURI())); - final JSONArray testResponse = new JSONArray(json); - - UserModel userModel = mock(UserModel.class); - when(userModel.getChangingDeviceToken()).thenReturn("t0k3n"); - - final ChatModel tested = new ChatModel(userModel); - - tested.setFromJson(testResponse); - final int sizeBefore = tested.getReceivedChatMessages().size(); - - tested.setFromJson(testResponse); - assertThat(tested.getReceivedChatMessages()).hasSize(sizeBefore); - } - - public String readToString(File file) throws IOException { - return readToString(Charset.defaultCharset(), file); - } - - public String readToString(Charset charset, File file) throws IOException { - try (FileInputStream stream = new FileInputStream(file)) { - return readToStringFromFileInputStream(charset, stream); - } - } - - private String readToStringFromFileInputStream(Charset charset, FileInputStream stream) throws IOException { - try (FileChannel fc = stream.getChannel()) { - final MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); - return charset.decode(bb).toString(); - } - } -} diff --git a/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.kt b/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.kt new file mode 100644 index 00000000..9f0969ed --- /dev/null +++ b/app/src/test/java/de/stephanlindauer/criticalmaps/model/ChatModelTest.kt @@ -0,0 +1,49 @@ +package de.stephanlindauer.criticalmaps.model + +import com.google.common.truth.Truth.assertThat +import org.json.JSONArray +import org.junit.Test +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import java.io.File + +class ChatModelTest { + + @Test + fun `setFromJson - test that chatmessages are sorted`() { + val json = readToString(File(javaClass.classLoader!! + .getResource("server_response_chatmessages.json")!!.toURI())) + val response = JSONArray(json) + + val userModel = mock(UserModel::class.java) + `when`(userModel.changingDeviceToken).thenReturn("t0k3n") + + val tested = ChatModel(userModel) + + tested.setFromJson(response) + val message0 = tested.getReceivedChatMessages()[0] + val message1 = tested.getReceivedChatMessages()[1] + + assertThat(message0.timestamp).isLessThan(message1.timestamp) + } + + @Test + fun `setFromJson - existing messages are replaced`() { + val json = readToString(File(javaClass.classLoader!! + .getResource("server_response_chatmessages.json")!!.toURI())) + val testResponse = JSONArray(json) + + val userModel = mock(UserModel::class.java) + `when`(userModel.changingDeviceToken).thenReturn("t0k3n") + + val tested = ChatModel(userModel) + + tested.setFromJson(testResponse) + val sizeBefore = tested.getReceivedChatMessages().size + + tested.setFromJson(testResponse) + assertThat(tested.getReceivedChatMessages()).hasSize(sizeBefore) + } + + private fun readToString(file: File) = file.readText() +} diff --git a/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.java b/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.java deleted file mode 100644 index 164a4f33..00000000 --- a/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package de.stephanlindauer.criticalmaps.model; - -import org.junit.Test; -import org.maplibre.android.geometry.LatLng; - - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; - -public class OwnLocationModelTest { - @Test - public void hasPreciseLocation_impreciseLocationIsReportedAsImprecise() { - OwnLocationModel tested = new OwnLocationModel(); - - tested.setLocation(new LatLng(0d, 0d), 50.0f); - - assertThat(tested.hasPreciseLocation()).isFalse(); - } - - @Test - public void hasPreciseLocation_preciseLocationIsReportedAsPrecise() { - OwnLocationModel tested = new OwnLocationModel(); - - tested.setLocation(new LatLng(0d, 0d), 49.9f); - - assertThat(tested.hasPreciseLocation()).isTrue(); - } - - @Test - public void getLocationJson_throwsWhenNoLocation() { - OwnLocationModel tested = new OwnLocationModel(); - - assertThrows(NullPointerException.class, tested::getLocationJson); - } - - @Test - public void getLocationJson_returnsCorrectJson() { - OwnLocationModel tested = new OwnLocationModel(); - - String expected = "{\"latitude\":\"40741895\",\"longitude\":\"-73989308\"}"; - tested.setLocation(new LatLng(40.741895d, -73.989308d), 1.1f); - assertThat(tested.getLocationJson().toString()).isEqualTo(expected); - } -} diff --git a/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.kt b/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.kt new file mode 100644 index 00000000..1e5773bf --- /dev/null +++ b/app/src/test/java/de/stephanlindauer/criticalmaps/model/OwnLocationModelTest.kt @@ -0,0 +1,46 @@ +package de.stephanlindauer.criticalmaps.model + +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows +import org.junit.Test +import org.maplibre.android.geometry.LatLng + +class OwnLocationModelTest { + + @Test + fun `hasPreciseLocation - imprecise location is reported as imprecise`() { + val tested = OwnLocationModel() + + tested.setLocation(LatLng(0.0, 0.0), 50.0f) + + assertThat(tested.hasPreciseLocation()).isFalse() + } + + @Test + fun `hasPreciseLocation - precise location is reported as precise`() { + val tested = OwnLocationModel() + + tested.setLocation(LatLng(0.0, 0.0), 49.9f) + + assertThat(tested.hasPreciseLocation()).isTrue() + } + + @Test + fun `getLocationJson - throws when no location`() { + val tested = OwnLocationModel() + + val exception = assertThrows(IllegalArgumentException::class.java) { + tested.getLocationJson() + } + assertThat(exception.message).isEqualTo("Location must be set before calling getLocationJson()") + } + + @Test + fun `getLocationJson - returns correct json`() { + val tested = OwnLocationModel() + + val expected = """{"latitude":"40741895","longitude":"-73989308"}""" + tested.setLocation(LatLng(40.741895, -73.989308), 1.1f) + assertThat(tested.getLocationJson().toString()).isEqualTo(expected) + } +} From a87833a2eff2afce47c09cbf5e77c8648e38f56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20B=C3=BCnz?= Date: Sun, 4 Jan 2026 00:26:16 +0100 Subject: [PATCH 2/2] update gpx models --- .../criticalmaps/model/gpx/GpxModel.java | 51 ------------------- .../criticalmaps/model/gpx/GpxModel.kt | 20 ++++++++ .../criticalmaps/model/gpx/GpxPoi.java | 24 --------- .../criticalmaps/model/gpx/GpxPoi.kt | 10 ++++ .../criticalmaps/model/gpx/GpxTrack.java | 26 ---------- .../criticalmaps/model/gpx/GpxTrack.kt | 10 ++++ 6 files changed, 40 insertions(+), 101 deletions(-) delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.kt delete mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.java create mode 100644 app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.kt diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.java deleted file mode 100644 index 336fa5ac..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -package de.stephanlindauer.criticalmaps.model.gpx; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class GpxModel { - - private String uri; - private List tracks = new ArrayList<>(); - private List poiList = new ArrayList<>(); - - @Inject - public GpxModel() { - } - - public String getUri() { - return uri; - } - - public void setUri(String uri) { - this.uri = uri; - } - - public List getTracks() { - return tracks; - } - - public void setTracks(List tracks) { - this.tracks = tracks; - } - - public List getPoiList() { - return poiList; - } - - public void setPoiList(List poiList) { - this.poiList = poiList; - } - - public void clear() { - tracks.clear(); - poiList.clear(); - uri = null; - } -} -*/ diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.kt new file mode 100644 index 00000000..b6ded488 --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxModel.kt @@ -0,0 +1,20 @@ +/* +package de.stephanlindauer.criticalmaps.model.gpx + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GpxModel @Inject constructor() { + + var uri: String? = null + var tracks: MutableList = mutableListOf() + var poiList: MutableList = mutableListOf() + + fun clear() { + tracks.clear() + poiList.clear() + uri = null + } +} +*/ diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.java deleted file mode 100644 index 84410d4b..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.java +++ /dev/null @@ -1,24 +0,0 @@ -/* -package de.stephanlindauer.criticalmaps.model.gpx; - -import org.osmdroid.util.GeoPoint; - -public class GpxPoi { - - private final String name; - private final GeoPoint position; - - public GpxPoi(String name, GeoPoint position) { - this.name = name; - this.position = position; - } - - public String getName() { - return name; - } - - public GeoPoint getPosition() { - return position; - } -} -*/ diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.kt new file mode 100644 index 00000000..e7b1b87a --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxPoi.kt @@ -0,0 +1,10 @@ +/* +package de.stephanlindauer.criticalmaps.model.gpx + +import org.osmdroid.util.GeoPoint + +data class GpxPoi( + val name: String, + val position: GeoPoint +) +*/ diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.java deleted file mode 100644 index 30527e5d..00000000 --- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -package de.stephanlindauer.criticalmaps.model.gpx; - -import org.osmdroid.util.GeoPoint; - -import java.util.List; - -public class GpxTrack { - - private final String name; - private final List waypoints; - - public GpxTrack(String name, List waypoints) { - this.name = name; - this.waypoints = waypoints; - } - - public String getName() { - return name; - } - - public List getWaypoints() { - return waypoints; - } -} -*/ diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.kt b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.kt new file mode 100644 index 00000000..ed3b6c3f --- /dev/null +++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/gpx/GpxTrack.kt @@ -0,0 +1,10 @@ +/* +package de.stephanlindauer.criticalmaps.model.gpx + +import org.osmdroid.util.GeoPoint + +data class GpxTrack( + val name: String, + val waypoints: List +) +*/